#!/usr/bin/env php
<?php
Phar::mapPhar('codecept.phar');

require_once 'phar://codecept.phar/vendor/codeception/codeception/app.php';

__HALT_COMPILER(); ?>
:
          codecept.phar    /   vendor/nikic/php-parser/lib/PhpParser/Error.phpE	      E	  cŊ      0   vendor/nikic/php-parser/lib/PhpParser/Parser.php          ן      7   vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter.php        :,i      8   vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.phpZ      Z  c      ?   vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php        ڤ      @   vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php;      ;  zXb      3   vendor/nikic/php-parser/lib/PhpParser/Modifiers.php              7   vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php        Q$3O      4   vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php        P}      A   vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php        )/|      ?   vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php          ͇      4   vendor/nikic/php-parser/lib/PhpParser/PhpVersion.php5	      5	  {Iӈ      5   vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.phpg       g   [ĠZ      5   vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php$
      $
  cԤ      1   vendor/nikic/php-parser/lib/PhpParser/Comment.php
      
  r/      5   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.php'      '  {      /   vendor/nikic/php-parser/lib/PhpParser/Lexer.php        DA1      6   vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php          G      <   vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php;      ;  3      7   vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php        /v      8   vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php        +      =   vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.phpc      c  ^i      B   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php        "VY?      I   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php        max      D   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php5      5  􉉤      N   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.phpw      w  =RA      K   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php-      -   &      M   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php@      @  p      D   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php        ?r٤      4   vendor/nikic/php-parser/lib/PhpParser/NodeFinder.phpa      a  >>L      5   vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php      Od      5   vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.phpH"     H" T}      ;   vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.phpq      q  nHG      >   vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.phpd      d   %S      L   vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php        ~M\      9   vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php	      	  3      @   vendor/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php        %k      9   vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php              S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php        r&%*      S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php        +܀(      S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php        &      K   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php
      
  ꍤ      O   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php        gn      O   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.phpY      Y  $JV      [   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php        R)\      M   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.phpg      g  ć      P   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.phpv      v  c%#      M   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php)      )  J      5   vendor/nikic/php-parser/lib/PhpParser/NameContext.php5      5  W      /   vendor/nikic/php-parser/lib/PhpParser/Token.php        P      F   vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.phpp       p   Ҁ      ;   vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php        Ah      7   vendor/nikic/php-parser/lib/PhpParser/Node/MatchArm.php        b\>      3   vendor/nikic/php-parser/lib/PhpParser/Node/Name.phpI      I  h٤      =   vendor/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php        Y	y      :   vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php          )       4   vendor/nikic/php-parser/lib/PhpParser/Node/Param.phpN      N        ;   vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.phpJ      J        ?   vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php        
>      8   vendor/nikic/php-parser/lib/PhpParser/Node/StaticVar.phpF      F        7   vendor/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.php        g4,      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php        O7       7   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php          |      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php        Τ      8   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.phpz      z  QR      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php        l/      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php        ᅔ_      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php.      .  Zu:      B   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.phpI       I         9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php        s      ;   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.phpA      A  뭤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php        s      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.phpM      M  _>-      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php        qmyz      @   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php        6      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php	      	  V      L   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php        j      Q   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpz      z  y6      =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.phpG       G   &      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php        $4      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.phpE       E   G)      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php        %      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php        |      D   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.phpJ       J   Z{zǤ      =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php        tf1      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php)      )  E      8   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.phpx      x  D      :   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^      ^  k      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php        ?G      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php        P      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php        rz{      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php        k      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.phpH      H  $֗8      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php        &K      9   vendor/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_.php        9V      9   vendor/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        ä      F   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php                =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php        +:n      7   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php        Md      8   vendor/nikic/php-parser/lib/PhpParser/Node/Attribute.php              B   vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.phpj      j  Bs      2   vendor/nikic/php-parser/lib/PhpParser/Node/Arg.phpy      y  IƤ      5   vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php        <5C3      E   vendor/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php        0      3   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php          Ym"      ;   vendor/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php        #      8   vendor/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php         z(      3   vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php          }序      9   vendor/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php$      $        6   vendor/nikic/php-parser/lib/PhpParser/Node/UseItem.phpV      V  )p      @   vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php          Z      B   vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php        -G$g      <   vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php        N      =   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.phpA       A   G      H   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.phpT       T   ŝ'      @   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.phpT      T  1"Ȥ      =   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.phpM       M   qO      <   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php              H   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php/      /  "      :   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php        t-ä      G   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php        u      G   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php        Y^,      D   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php        ^      K   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php'      '  &      G   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php        X      E   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php        kK      J   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php$      $  MG0      E   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php        @      9   vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.phpJ      J  3
ݤ      5   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.phpb       b   kQK      8   vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.phpy      y  ^      :   vendor/nikic/php-parser/lib/PhpParser/Node/DeclareItem.phpZ      Z  !      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.phpP      P  -g      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php        0w      C   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.phpQ      Q  N      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php        p݇D      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php        	      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php        t      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php        ڤ      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php        ,       G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.phpg      g   ~      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php        K      I   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php:      :  UФ      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php              A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php        g{      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php        i;UB      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php              D   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php        4      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php        N      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php
      
  ל      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php        s      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php	      	  w¤      C   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php        |      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php        ?      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php        	t@|      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php        3ϟ      E   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php        6L̤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php        FY      E   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php        f      I   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php        #2H      B   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php        _      K   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php          U٤      D   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php        [=w      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php        
P      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php	      	  /S      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php        4^      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php	      	  RI      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php        mw      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php        ߤ      B   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php              K   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php          iKqܤ      G   vendor/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_.php        X&Ȥ      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php        g{Dn      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php        0Ť      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php        kx      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php        X{n      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.phpH      H  H
      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php        SPx      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php        X^      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php        rM      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php        눱      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php        @w      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.phpO      O  7L      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php        EPդ      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php        L謤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php          o      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php          )`      C   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php          f      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php          hL      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php          s쑺      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php          Ǥ      E   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php                G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php          ff      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php          R      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php          b<      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php          \s      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php          We      B   vendor/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.php        694      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php          iY-      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php          8      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php          L      >   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_.php          Z      8   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.phpc      c  f5=      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php        `/      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php        y       =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.phpG       G   f      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.phpH       H   6      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php        `Ϥ      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php          <Q!      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php        鹤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php        ;e      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php*      *        >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php        J1¤      <   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.php        V7      8   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.phpS      S  &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_.php        I֤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php        xz      6   vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.php        h      =   vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php        ֩      :   vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php        E3      8   vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php0      0  ,>      <   vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php9      9  i      7   vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php>      >  Re      8   vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php              >   vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.phpg      g  Z      :   vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php        q      <   vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.phpV      V  f^      6   vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.phph      h  4ä      <   vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php^      ^  +%&O      7   vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php(      (        8   vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php        ؋SФ      ;   vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.phpr      r        :   vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.phpf      f  |a      D   vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.phpZ
      Z
  Ӫ      @   vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php        :r1      1   vendor/nikic/php-parser/lib/PhpParser/Builder.phpr       r   _g      >   vendor/nikic/php-parser/lib/PhpParser/compatibility_tokens.php        +      8   vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.phpg      g  h      .   vendor/nikic/php-parser/lib/PhpParser/Node.php        vG]      6   vendor/phar-io/version/src/VersionConstraintParser.php?
      ?
  MQ      ,   vendor/phar-io/version/src/VersionNumber.php/      /  f,w[      &   vendor/phar-io/version/src/Version.php        /C_      5   vendor/phar-io/version/src/VersionConstraintValue.php        
      ?   vendor/phar-io/version/src/constraints/AnyVersionConstraint.php          ^      <   vendor/phar-io/version/src/constraints/VersionConstraint.php          w_      C   vendor/phar-io/version/src/constraints/OrVersionConstraintGroup.php        $      P   vendor/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php              D   vendor/phar-io/version/src/constraints/AndVersionConstraintGroup.php              I   vendor/phar-io/version/src/constraints/SpecificMajorVersionConstraint.php        Xx      D   vendor/phar-io/version/src/constraints/AbstractVersionConstraint.phpK      K  qB      Q   vendor/phar-io/version/src/constraints/SpecificMajorAndMinorVersionConstraint.php        &q      A   vendor/phar-io/version/src/constraints/ExactVersionConstraint.phpj      j  <1n      /   vendor/phar-io/version/src/PreReleaseSuffix.phpY      Y  i      ,   vendor/phar-io/version/src/BuildMetaData.php[      [  )>      E   vendor/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php          XbpԤ      O   vendor/phar-io/version/src/exceptions/UnsupportedVersionConstraintException.php          /O      B   vendor/phar-io/version/src/exceptions/NoBuildMetaDataException.php                3   vendor/phar-io/version/src/exceptions/Exception.php~       ~   x|      J   vendor/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php                A   vendor/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.php        Y      4   vendor/phar-io/manifest/src/xml/CopyrightElement.php|      |  ~      2   vendor/phar-io/manifest/src/xml/LicenseElement.php        i      2   vendor/phar-io/manifest/src/xml/BundlesElement.php        ފ      4   vendor/phar-io/manifest/src/xml/ManifestDocument.php	      	  zߤ      3   vendor/phar-io/manifest/src/xml/ContainsElement.php        	u      5   vendor/phar-io/manifest/src/xml/ElementCollection.php8      8  ]a      1   vendor/phar-io/manifest/src/xml/AuthorElement.phpb      b  nU      4   vendor/phar-io/manifest/src/xml/ExtensionElement.php"      "  ;      8   vendor/phar-io/manifest/src/xml/ExtElementCollection.php          4D       >   vendor/phar-io/manifest/src/xml/ComponentElementCollection.php          
5      3   vendor/phar-io/manifest/src/xml/RequiresElement.php          X      .   vendor/phar-io/manifest/src/xml/ExtElement.php                 4   vendor/phar-io/manifest/src/xml/ComponentElement.php        @"      3   vendor/phar-io/manifest/src/xml/ManifestElement.php        UuZ      7   vendor/phar-io/manifest/src/values/AuthorCollection.php        
      ,   vendor/phar-io/manifest/src/values/Email.php        ;L?      A   vendor/phar-io/manifest/src/values/BundledComponentCollection.php        I
r      .   vendor/phar-io/manifest/src/values/License.phpJ      J  |N      *   vendor/phar-io/manifest/src/values/Url.php              <   vendor/phar-io/manifest/src/values/RequirementCollection.phpK      K  J      .   vendor/phar-io/manifest/src/values/Library.php          d      6   vendor/phar-io/manifest/src/values/ApplicationName.php        ^      ;   vendor/phar-io/manifest/src/values/CopyrightInformation.php        ^ݤ      0   vendor/phar-io/manifest/src/values/Extension.php        bͤ      D   vendor/phar-io/manifest/src/values/RequirementCollectionIterator.php        X :      I   vendor/phar-io/manifest/src/values/BundledComponentCollectionIterator.php        Yڤ      /   vendor/phar-io/manifest/src/values/Manifest.php        *0      7   vendor/phar-io/manifest/src/values/BundledComponent.php        J      >   vendor/phar-io/manifest/src/values/PhpExtensionRequirement.php(      (  s)      -   vendor/phar-io/manifest/src/values/Author.php        AI      <   vendor/phar-io/manifest/src/values/PhpVersionRequirement.php        n      2   vendor/phar-io/manifest/src/values/Requirement.php`       `   ',      2   vendor/phar-io/manifest/src/values/Application.php          X      ?   vendor/phar-io/manifest/src/values/AuthorCollectionIterator.phpz      z  $+H      +   vendor/phar-io/manifest/src/values/Type.phpk      k  J.      2   vendor/phar-io/manifest/src/ManifestSerializer.php        it      6   vendor/phar-io/manifest/src/ManifestDocumentMapper.php.      .  1      J   vendor/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php          !-      K   vendor/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php>      >  d      B   vendor/phar-io/manifest/src/exceptions/NoEmailAddressException.php          z輤      C   vendor/phar-io/manifest/src/exceptions/ManifestElementException.php          &П      @   vendor/phar-io/manifest/src/exceptions/InvalidEmailException.php          2      >   vendor/phar-io/manifest/src/exceptions/InvalidUrlException.php          Q,      J   vendor/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php          ~A      4   vendor/phar-io/manifest/src/exceptions/Exception.php          NQ      D   vendor/phar-io/manifest/src/exceptions/ManifestDocumentException.php          U      E   vendor/phar-io/manifest/src/exceptions/ElementCollectionException.php          pjN      B   vendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php          (      I   vendor/phar-io/manifest/tools/php-cs-fixer.d/PhpdocSingleLineVarFixer.php        v      1   vendor/behat/gherkin/src/Behat/Gherkin/Parser.php3      3  $      =   vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php]      ]  iȤ      @   vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php        S
o      E   vendor/behat/gherkin/src/Behat/Gherkin/Loader/FileLoaderInterface.php          =      A   vendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php          EZb      A   vendor/behat/gherkin/src/Behat/Gherkin/Loader/DirectoryLoader.php        ܯG      C   vendor/behat/gherkin/src/Behat/Gherkin/Loader/GherkinFileLoader.php        ۤ      D   vendor/behat/gherkin/src/Behat/Gherkin/Loader/AbstractFileLoader.php        R      I   vendor/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.phpW      W        0   vendor/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.phpG      G  ¤      2   vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php        yB      ;   vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.phpe      e  ?      A   vendor/behat/gherkin/src/Behat/Gherkin/Node/ArgumentInterface.phpi       i   \      ;   vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php        Ἰu      =   vendor/behat/gherkin/src/Behat/Gherkin/Node/NodeInterface.php          q!z      @   vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php        3      8   vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php        Ii      <   vendor/behat/gherkin/src/Behat/Gherkin/Node/PyStringNode.php?      ?  O      <   vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php        B      A   vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioInterface.php          A9p      9   vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php9      9  $A      E   vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioLikeInterface.php          x      F   vendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php          '~      ;   vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php        c|      D   vendor/behat/gherkin/src/Behat/Gherkin/Node/KeywordNodeInterface.php          n      >   vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php        1      C   vendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php          %,      <   vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.phpY      Y  :{      ;   vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php	      	  (      >   vendor/behat/gherkin/src/Behat/Gherkin/Filter/SimpleFilter.php        <      H   vendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php                <   vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php        S-      A   vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php9	      9	  ä      A   vendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php              ?   vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilter.phpe      e        H   vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php$      $  Q=Ϥ      A   vendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php          sH      =   vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php        {6ݤ      <   vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php        s      B   vendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php        r-M      E   vendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsInterface.phpZ      Z  [D      D   vendor/behat/gherkin/src/Behat/Gherkin/Keywords/CucumberKeywords.php              A   vendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php        e      G   vendor/behat/gherkin/src/Behat/Gherkin/Keywords/CachedArrayKeywords.php          KP      C   vendor/behat/gherkin/src/Behat/Gherkin/Exception/CacheException.php          J񼩤      B   vendor/behat/gherkin/src/Behat/Gherkin/Exception/NodeException.php          t      >   vendor/behat/gherkin/src/Behat/Gherkin/Exception/Exception.phpK       K         D   vendor/behat/gherkin/src/Behat/Gherkin/Exception/ParserException.php          dK      C   vendor/behat/gherkin/src/Behat/Gherkin/Exception/LexerException.php          U          vendor/behat/gherkin/libpath.php          trnǤ         vendor/behat/gherkin/i18n.php        9/      C   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php        P      R   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/PredefinedArray.php,      ,  15      X   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php                T   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.php          [A      G   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php        ǒ      R   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php                D   vendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php@      @  53      J   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php        PFF      P   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php	      	  e      G   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php        _      P   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php&	      &	  78      N   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php*      *  14N      P   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.phpz      z  f~      P   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php        %l      T   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php
      
  =      S   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.phpG"      G"  @?      N   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php	      	  wl      S   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php         Q      X   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.phpa      a  	qä      Y   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php        4~      \   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php        +j      N   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php\      \  _}      P   vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.phps      s  dޤ      J   vendor/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php        o+}      ;   vendor/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php        f4      I   vendor/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php          e      A   vendor/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php        &z^      =   vendor/justinrainbow/json-schema/src/JsonSchema/Validator.phps      s  #      F   vendor/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php        {N      H   vendor/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php          J      K   vendor/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php        M      T   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidConfigException.phpl       l   A!Lפ      P   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.phpI       I   %|      W   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.phpo       o   p      S   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php        \
      T   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaException.phpl       l   2      ]   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.phpu       u   =h      ^   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php          u-#1      N   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php          %      R   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.phpj       j   >      V   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php          G      Q   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/ValidationException.phpf       f         W   vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.phpw       w   N-[      6   vendor/theseer/tokenizer/src/NamespaceUriException.phpq       q   )0U      0   vendor/theseer/tokenizer/src/TokenCollection.php        O      .   vendor/theseer/tokenizer/src/XMLSerializer.php        4      -   vendor/theseer/tokenizer/src/NamespaceUri.php        X      *   vendor/theseer/tokenizer/src/Tokenizer.phpY      Y  TLӤ      *   vendor/theseer/tokenizer/src/Exception.phpf       f         9   vendor/theseer/tokenizer/src/TokenCollectionException.phpt       t   u      &   vendor/theseer/tokenizer/src/Token.php        X      8   vendor/psr/http-client/src/RequestExceptionInterface.php          ɾ      7   vendor/psr/http-client/src/ClientExceptionInterface.php`       `   f      .   vendor/psr/http-client/src/ClientInterface.php          o穤      8   vendor/psr/http-client/src/NetworkExceptionInterface.php          Dd      ;   vendor/psr/event-dispatcher/src/StoppableEventInterface.php          .      <   vendor/psr/event-dispatcher/src/EventDispatcherInterface.php          ISؤ      =   vendor/psr/event-dispatcher/src/ListenerProviderInterface.php          7
      3   vendor/psr/http-factory/src/UriFactoryInterface.php          Z      7   vendor/psr/http-factory/src/RequestFactoryInterface.php          bߤ      6   vendor/psr/http-factory/src/StreamFactoryInterface.phpT      T  0(      8   vendor/psr/http-factory/src/ResponseFactoryInterface.php          C      <   vendor/psr/http-factory/src/UploadedFileFactoryInterface.php'      '  .6      =   vendor/psr/http-factory/src/ServerRequestFactoryInterface.php          y)=      '   vendor/psr/log/src/LoggerAwareTrait.php          x      !   vendor/psr/log/src/NullLogger.php          eW      %   vendor/psr/log/src/AbstractLogger.phpp       p   %WФ      /   vendor/psr/log/src/InvalidArgumentException.php`       `    X1      +   vendor/psr/log/src/LoggerAwareInterface.php          pդ         vendor/psr/log/src/LogLevel.php          j8      "   vendor/psr/log/src/LoggerTrait.php        5      &   vendor/psr/log/src/LoggerInterface.php|      |  *b      7   vendor/psr/container/src/NotFoundExceptionInterface.phpq       q   R      8   vendor/psr/container/src/ContainerExceptionInterface.phpp       p   ;Z
      /   vendor/psr/container/src/ContainerInterface.php          [d      0   vendor/psr/http-message/src/MessageInterface.phpV      V  jj      0   vendor/psr/http-message/src/RequestInterface.php.      .  y      1   vendor/psr/http-message/src/ResponseInterface.php=      =  #      /   vendor/psr/http-message/src/StreamInterface.php        ě      5   vendor/psr/http-message/src/UploadedFileInterface.php        ZOP      ,   vendor/psr/http-message/src/UriInterface.php\      \  S8      6   vendor/psr/http-message/src/ServerRequestInterface.php7      7  2_ڤ      "   vendor/composer/platform_check.php        j)\      !   vendor/composer/autoload_real.php"      "  `ܤ      %   vendor/composer/InstalledVersions.php        xG      !   vendor/composer/autoload_psr4.php        n@         vendor/composer/installed.php(h      (h  7L      #   vendor/composer/autoload_static.php      [      %   vendor/composer/autoload_classmap.php'     ' v         vendor/composer/ClassLoader.php/      /  ⒤      "   vendor/composer/autoload_files.php        7      '   vendor/composer/autoload_namespaces.php          ~t      ,   vendor/guzzlehttp/guzzle/src/ClientTrait.php              6   vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php        [I      %   vendor/guzzlehttp/guzzle/src/Pool.php        q      -   vendor/guzzlehttp/guzzle/src/HandlerStack.phpx      x  x      *   vendor/guzzlehttp/guzzle/src/functions.phpv      v  :      2   vendor/guzzlehttp/guzzle/src/functions_include.phpa       a   ޤ      1   vendor/guzzlehttp/guzzle/src/MessageFormatter.php,      ,  {      /   vendor/guzzlehttp/guzzle/src/RequestOptions.php        4%      /   vendor/guzzlehttp/guzzle/src/BodySummarizer.php        K      &   vendor/guzzlehttp/guzzle/src/Utils.php        ^      0   vendor/guzzlehttp/guzzle/src/RetryMiddleware.php        %;)      0   vendor/guzzlehttp/guzzle/src/ClientInterface.php        -      .   vendor/guzzlehttp/guzzle/src/TransferStats.php4      4  k      +   vendor/guzzlehttp/guzzle/src/Middleware.php        ɟ      3   vendor/guzzlehttp/guzzle/src/RedirectMiddleware.phpy      y  $t      '   vendor/guzzlehttp/guzzle/src/Client.php-%      -%  5摤      1   vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.phpZ      Z  L      8   vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php        t      5   vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php7      7  s\G      :   vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php              1   vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php        s2Ϥ      D   vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.phpe       e   $       <   vendor/guzzlehttp/guzzle/src/Exception/TransferException.phpy       y   /      ;   vendor/guzzlehttp/guzzle/src/Exception/RequestException.phpr
      r
  jܤ      ;   vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php        "l}      :   vendor/guzzlehttp/guzzle/src/Exception/ClientException.phpb       b   yʤ      ?   vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php8      8  "Vp~      :   vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php          5O\$      C   vendor/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.php          co      :   vendor/guzzlehttp/guzzle/src/Exception/ServerException.phpb       b   mx      8   vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php        AN      3   vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php        g,      4   vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php        zؘ      6   vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php5      5  p      9   vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.phpG      G  e      4   vendor/guzzlehttp/guzzle/src/Handler/MockHandler.phpA      A  ׉      4   vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.phpn;      n;  ӝ      .   vendor/guzzlehttp/guzzle/src/Handler/Proxy.php%      %  o      =   vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php          Yۤ      8   vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php          xD[      :   vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php              8   vendor/guzzlehttp/promises/src/CancellationException.php~       ~         5   vendor/guzzlehttp/promises/src/TaskQueueInterface.php                *   vendor/guzzlehttp/promises/src/Promise.php        g      3   vendor/guzzlehttp/promises/src/FulfilledPromise.php5      5  mY      4   vendor/guzzlehttp/promises/src/PromisorInterface.php          1R      5   vendor/guzzlehttp/promises/src/RejectionException.php        W,	      3   vendor/guzzlehttp/promises/src/PromiseInterface.phpu      u  QDvC      (   vendor/guzzlehttp/promises/src/Utils.php        ZW	      .   vendor/guzzlehttp/promises/src/EachPromise.phpu      u  l"פ      ,   vendor/guzzlehttp/promises/src/Coroutine.php<      <  ˭      2   vendor/guzzlehttp/promises/src/RejectedPromise.php_      _  i      '   vendor/guzzlehttp/promises/src/Each.php        f6      ,   vendor/guzzlehttp/promises/src/TaskQueue.php        #r      )   vendor/guzzlehttp/promises/src/Create.phpT      T  c      5   vendor/guzzlehttp/promises/src/AggregateException.php        RY9y      %   vendor/guzzlehttp/promises/src/Is.phpd      d  {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.phpz      z  Z*      +   vendor/guzzlehttp/psr7/src/MessageTrait.php        HF      )   vendor/guzzlehttp/psr7/src/PumpStream.php+	      +	  @      %   vendor/guzzlehttp/psr7/src/Stream.php        BA      *   vendor/guzzlehttp/psr7/src/HttpFactory.php	      	  "*a      *   vendor/guzzlehttp/psr7/src/UriResolver.php        F2I      %   vendor/guzzlehttp/psr7/src/Header.phpx      x  J0      &   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.phpk      k  C ?      $   vendor/guzzlehttp/psr7/src/Query.php        (6      ,   vendor/guzzlehttp/psr7/src/UriComparator.php        yפ      .   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.php        Qq      3   vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php        N+      -   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.php        Y)~      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPStreamConnection.php;
      ;
  c8      N   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionFactory.php

      

        M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionConfig.php%      %  w}O|      Q   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySocketConnection.php        &      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.php        ä      `   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/AbstractSignalHeartbeatSender.php              W   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php1      1  C      U   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/SIGHeartbeatSender.php7      7  Cퟤ      N   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySSLConnection.php        ^hM      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSSLConnection.php        }S$      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AbstractConnection.phpI      I  Ǩ      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSocketConnection.phpv      v  v\[      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php        &      @   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/DebugHelper.php3      3  r¤      I   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol091.php5      5  \a\      I   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol080.phpM      M  =^      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait091.php        UG      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap080.php        P6Ф      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait080.php
      
  lw      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap091.php        vqF      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/MiscHelper.php4      4  y      ;   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Assert.php7      7  q      D   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/SocketConstants.php        VĄߤ      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/BigInteger.php\      \        <   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants.php        Ly      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants091.php	      	  F=¤      >   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.phpq      q  ~Z      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPIOReader.php        d	w      C   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPBufferReader.phpn      n  *L      <   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPTable.php        JR      I   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPAbstractCollection.phpV!      V!  f      =   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPWriter.php        
z      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPByteStream.phpj      j  d      >   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.phpl      l  Uۤ      =   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPReader.php        D      G   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exchange/AMQPExchangeType.php          o      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php#      #  fз      ;   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Frame.php        U>      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.php[      [  ^Z      <   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Method.php%      %  Cu      U   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.phpp       p   h      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOWaitException.phpc       c   M      N   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPExceptionInterface.phpM       M   "Wܤ      T   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidArgumentException.php          ,DΤ      G   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOException.phpw       w   f5      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPSocketException.phpc       c   bҤ      S   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNotImplementedException.phpk       k   d4а      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolException.php        q      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.php          J_      P   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfBoundsException.php                O   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfRangeException.php          |#Ǥ      L   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPRuntimeException.php          A      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPDataReadException.phpe       e   w068      R   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelClosedException.phpj       j   읦      V   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php=      =  _{      Q   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidFrameException.phpi       i   M      T   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolChannelException.phpm       m   N!      L   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.php        Mf      U   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPEmptyDeliveryTagException.phpm       m   ֔      T   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPHeartbeatMissedException.phpu       u   渖      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNoDataException.phpf       f   ? ,ͤ      P   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php_      _  <      5   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Package.phpu       u   Nۤ      G   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.phpj      j  5;      P   vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php          #      W   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.phpy       y   m7      ]   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.phpv      v  X~B      ]   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php        u      R   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php        MD      L   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpn       n         J   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php        !\      ?   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.phpf       f   Z.)Ȥ      B   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php        q      F   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php          g      L   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php[      [  %MQ      3   vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php          	n      E   vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php              E   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php        jQ      A   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.phpj      j  c$C      E   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php        k      O   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php          )      9   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.phpm       m   aˤ      2   vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php`      `  Ī      A   vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php        46      >   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)      )  Wt      7   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php|       |   a]ˤ      ;   vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php          6      W   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php        h      R   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpC      C  A      M   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php          s()      B   vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php          Lt      E   vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.phpx       x   4      @   vendor/predis/predis/src/Protocol/RequestSerializerInterface.php          $[      7   vendor/predis/predis/src/Protocol/ProtocolException.php          Z{      @   vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php^      ^  (      E   vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php        lm;      <   vendor/predis/predis/src/Protocol/Text/RequestSerializer.phpP      P  ]      9   vendor/predis/predis/src/Protocol/Text/ResponseReader.php        \      <   vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php%      %  ^t      ?   vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.phpQ      Q   +C      N   vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php        c3      @   vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php:      :  *%      D   vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php        U      A   vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php?      ?  㘭      K   vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php          j=3      B   vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php        +\      =   vendor/predis/predis/src/Protocol/ResponseReaderInterface.php          y      4   vendor/predis/predis/src/Connection/RelayMethods.phpg      g  -      ?   vendor/predis/predis/src/Connection/NodeConnectionInterface.phpl      l  Pp#      D   vendor/predis/predis/src/Connection/AggregateConnectionInterface.php        |      /   vendor/predis/predis/src/Connection/Factory.php        g;j      ;   vendor/predis/predis/src/Connection/ParametersInterface.php        Y@      8   vendor/predis/predis/src/Connection/FactoryInterface.php          (Ў      2   vendor/predis/predis/src/Connection/Parameters.phpn	      n	  >S      ;   vendor/predis/predis/src/Connection/ConnectionException.php                ;   vendor/predis/predis/src/Connection/ConnectionInterface.php        /X)      8   vendor/predis/predis/src/Connection/StreamConnection.php        ɽҤ      7   vendor/predis/predis/src/Connection/RelayConnection.php        
{      A   vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php        !3      A   vendor/predis/predis/src/Connection/CompositeStreamConnection.php        %      A   vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php        /      G   vendor/predis/predis/src/Connection/Replication/SentinelReplication.php*      *  =(6      H   vendor/predis/predis/src/Connection/Replication/ReplicationInterface.php              J   vendor/predis/predis/src/Connection/Replication/MasterSlaveReplication.phpO       O   Rx      D   vendor/predis/predis/src/Connection/CompositeConnectionInterface.php        wΤ      8   vendor/predis/predis/src/Connection/WebdisConnection.php        w      <   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.php          pL_      :   vendor/predis/predis/src/Connection/AbstractConnection.php	      	  w      0   vendor/predis/predis/src/ClientConfiguration.phpa      a  n      2   vendor/predis/predis/src/NotSupportedException.php`       `   L?      '   vendor/predis/predis/src/Autoloader.php        t%ۤ      ,   vendor/predis/predis/src/PredisException.phpl       l   G	      9   vendor/predis/predis/src/Collection/Iterator/Keyspace.php        ^      8   vendor/predis/predis/src/Collection/Iterator/ListKey.php@      @  w!      =   vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php        ]      8   vendor/predis/predis/src/Collection/Iterator/HashKey.php        na      7   vendor/predis/predis/src/Collection/Iterator/SetKey.php        6y      D   vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.phpT      T  L      +   vendor/predis/predis/src/Response/Error.php        ރ      7   vendor/predis/predis/src/Response/ResponseInterface.phpP       P   LR/N      ,   vendor/predis/predis/src/Response/Status.phpQ      Q  \      4   vendor/predis/predis/src/Response/ErrorInterface.php          hG5      5   vendor/predis/predis/src/Response/ServerException.php]      ]  
p      8   vendor/predis/predis/src/Response/Iterator/MultiBulk.php        3&      =   vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php        K'      @   vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php        ϻ      ,   vendor/predis/predis/src/Session/Handler.php;      ;  	      7   vendor/predis/predis/src/Transaction/MultiExecState.php        `f      2   vendor/predis/predis/src/Transaction/MultiExec.phpw      w  D_      B   vendor/predis/predis/src/Transaction/AbortedMultiExecException.php        6q      3   vendor/predis/predis/src/CommunicationException.phpK      K  
<.      ,   vendor/predis/predis/src/ClientException.phpY       Y   ]ov      ,   vendor/predis/predis/src/ClientInterface.php3
      3
  :m      ,   vendor/predis/predis/src/PubSub/Consumer.php        I      2   vendor/predis/predis/src/PubSub/DispatcherLoop.phpl      l  hG      4   vendor/predis/predis/src/PubSub/AbstractConsumer.php	      	  d      1   vendor/predis/predis/src/PubSub/RelayConsumer.phpG      G        -   vendor/predis/predis/src/Monitor/Consumer.phpI      I  _0      =   vendor/predis/predis/src/Command/Processor/ProcessorChain.php        *T      A   vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php%,      %,  BXE      A   vendor/predis/predis/src/Command/Processor/ProcessorInterface.php          {       5   vendor/predis/predis/src/Command/Redis/PSUBSCRIBE.phpM      M  .m      9   vendor/predis/predis/src/Command/Redis/ZREVRANGEBYLEX.php          aG      3   vendor/predis/predis/src/Command/Redis/BZPOPMIN.php          1      /   vendor/predis/predis/src/Command/Redis/MSET.php        @J?      1   vendor/predis/predis/src/Command/Redis/DECRBY.php          *sD      2   vendor/predis/predis/src/Command/Redis/CLUSTER.php          PF1      /   vendor/predis/predis/src/Command/Redis/EXEC.php          n      5   vendor/predis/predis/src/Command/Redis/BRPOPLPUSH.php          5̴N      1   vendor/predis/predis/src/Command/Redis/BZMPOP.php              /   vendor/predis/predis/src/Command/Redis/XDEL.php@      @  )/Y4      6   vendor/predis/predis/src/Command/Redis/PEXPIRETIME.php          {      3   vendor/predis/predis/src/Command/Redis/ZREVRANK.php          b琤      2   vendor/predis/predis/src/Command/Redis/MIGRATE.php.      .  ^      4   vendor/predis/predis/src/Command/Redis/RPOPLPUSH.php          yO      4   vendor/predis/predis/src/Command/Redis/SUBSCRIBE.phpK      K  ?      A   vendor/predis/predis/src/Command/Redis/BloomFilter/BFSCANDUMP.php          5      =   vendor/predis/predis/src/Command/Redis/BloomFilter/BFMADD.php          fBڤ      @   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.phpb      b  '      <   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.php        Z      B   vendor/predis/predis/src/Command/Redis/BloomFilter/BFLOADCHUNK.php          (4      1   vendor/predis/predis/src/Command/Redis/DBSIZE.php          *      /   vendor/predis/predis/src/Command/Redis/HGET.php          r      2   vendor/predis/predis/src/Command/Redis/COMMAND.php          r      0   vendor/predis/predis/src/Command/Redis/SCARD.php          }      /   vendor/predis/predis/src/Command/Redis/SPOP.php          QvQ4      0   vendor/predis/predis/src/Command/Redis/ZCARD.php          Z
      .   vendor/predis/predis/src/Command/Redis/DEL.php?      ?  o      3   vendor/predis/predis/src/Command/Redis/SENTINEL.php        A      <   vendor/predis/predis/src/Command/Redis/GEORADIUSBYMEMBER.php          %      1   vendor/predis/predis/src/Command/Redis/LPUSHX.php          sɩ      6   vendor/predis/predis/src/Command/Redis/SRANDMEMBER.php          _[      1   vendor/predis/predis/src/Command/Redis/SCRIPT.php          h0      1   vendor/predis/predis/src/Command/Redis/BGSAVE.php"      "  |X~      1   vendor/predis/predis/src/Command/Redis/GETSET.php                /   vendor/predis/predis/src/Command/Redis/DECR.php          Ԁnt      /   vendor/predis/predis/src/Command/Redis/RPOP.php          {      2   vendor/predis/predis/src/Command/Redis/RESTORE.php          y$      4   vendor/predis/predis/src/Command/Redis/ZREVRANGE.php          a_Y      4   vendor/predis/predis/src/Command/Redis/RANDOMKEY.php        Ц      ;   vendor/predis/predis/src/Command/Redis/ZREVRANGEBYSCORE.php          n尤      2   vendor/predis/predis/src/Command/Redis/PEXPIRE.php                /   vendor/predis/predis/src/Command/Redis/AUTH.php          å      3   vendor/predis/predis/src/Command/Redis/SHUTDOWN.php1      1  )k      :   vendor/predis/predis/src/Command/Redis/Search/FTCREATE.php        h      <   vendor/predis/predis/src/Command/Redis/Search/FTALIASADD.php          %&      8   vendor/predis/predis/src/Command/Redis/Search/FTINFO.php          	      ;   vendor/predis/predis/src/Command/Redis/Search/FTTAGVALS.php          l9      9   vendor/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.php        P      :   vendor/predis/predis/src/Command/Redis/Search/FTSUGADD.php              :   vendor/predis/predis/src/Command/Redis/Search/FTSUGLEN.php          6%      ;   vendor/predis/predis/src/Command/Redis/Search/FTDICTADD.php          7      :   vendor/predis/predis/src/Command/Redis/Search/FTCURSOR.php        ;Uޤ      ;   vendor/predis/predis/src/Command/Redis/Search/FTPROFILE.phpc      c  0L       =   vendor/predis/predis/src/Command/Redis/Search/FTDROPINDEX.php        y      ;   vendor/predis/predis/src/Command/Redis/Search/FTSYNDUMP.php          Zo&`      =   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.php          D      :   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.php          c͑      <   vendor/predis/predis/src/Command/Redis/Search/FTDICTDUMP.php          R      :   vendor/predis/predis/src/Command/Redis/Search/FTSUGDEL.php          Q      0   vendor/predis/predis/src/Command/Redis/LTRIM.php          2L      4   vendor/predis/predis/src/Command/Redis/PEXPIREAT.php                /   vendor/predis/predis/src/Command/Redis/ZREM.php@      @  d0      1   vendor/predis/predis/src/Command/Redis/PSETEX.php                /   vendor/predis/predis/src/Command/Redis/KEYS.php          !      6   vendor/predis/predis/src/Command/Redis/ZUNIONSTORE.php:      :  $      1   vendor/predis/predis/src/Command/Redis/HSETNX.php          D      1   vendor/predis/predis/src/Command/Redis/LRANGE.php          %׳      1   vendor/predis/predis/src/Command/Redis/BLMOVE.php~       ~   q|H&      /   vendor/predis/predis/src/Command/Redis/XLEN.php          3      1   vendor/predis/predis/src/Command/Redis/BLMPOP.php        dal      =   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTCDF.php              ?   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTMERGE.php        r      @   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTCREATE.php        {Ƥ      C   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTBYREVRANK.php          ѕԤ      >   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTINFO.php        ~      F   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTTRIMMED_MEAN.php        pjX!      =   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTMIN.php        ya      @   vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTBYRANK.php        h      B   vendor/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.php              A   vendor/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.php          tae      4   vendor/predis/predis/src/Command/Redis/XREVRANGE.php          CQ      2   vendor/predis/predis/src/Command/Redis/PFCOUNT.phpG      G  ^e      5   vendor/predis/predis/src/Command/Redis/EXPIRETIME.php          FUФ      0   vendor/predis/predis/src/Command/Redis/ZRANK.php          i      /   vendor/predis/predis/src/Command/Redis/PING.php          gG٤      /   vendor/predis/predis/src/Command/Redis/SAVE.php                2   vendor/predis/predis/src/Command/Redis/GEODIST.php          ;      .   vendor/predis/predis/src/Command/Redis/GET.php          T      2   vendor/predis/predis/src/Command/Redis/PFMERGE.phpG      G  N%      1   vendor/predis/predis/src/Command/Redis/GEOADD.php        ԋ      0   vendor/predis/predis/src/Command/Redis/SDIFF.phpC      C  g       1   vendor/predis/predis/src/Command/Redis/SELECT.php          um=      1   vendor/predis/predis/src/Command/Redis/EXISTS.php          e
j      1   vendor/predis/predis/src/Command/Redis/XRANGE.phpt      t  i      4   vendor/predis/predis/src/Command/Redis/ZLEXCOUNT.php          -      3   vendor/predis/predis/src/Command/Redis/SMEMBERS.php                .   vendor/predis/predis/src/Command/Redis/TTL.php          )j      5   vendor/predis/predis/src/Command/Redis/ZDIFFSTORE.phpA      A  YΤ      1   vendor/predis/predis/src/Command/Redis/RPUSHX.php          uW      3   vendor/predis/predis/src/Command/Redis/LASTSAVE.php          ٽ\*      0   vendor/predis/predis/src/Command/Redis/SETNX.php          ]<Ƥ      0   vendor/predis/predis/src/Command/Redis/BLPOP.php        31ؤ      3   vendor/predis/predis/src/Command/Redis/BITCOUNT.php          Dɤ      0   vendor/predis/predis/src/Command/Redis/XTRIM.php&      &  ϲ)!      5   vendor/predis/predis/src/Command/Redis/SDIFFSTORE.php        p;      /   vendor/predis/predis/src/Command/Redis/MOVE.php                ;   vendor/predis/predis/src/Command/Redis/ZREMRANGEBYSCORE.php                2   vendor/predis/predis/src/Command/Redis/EVAL_RO.php        ;      1   vendor/predis/predis/src/Command/Redis/ZRANGE.php        f      0   vendor/predis/predis/src/Command/Redis/ZDIFF.php4      4  $oX      1   vendor/predis/predis/src/Command/Redis/SUBSTR.php          m;r      0   vendor/predis/predis/src/Command/Redis/PFADD.phpB      B  @c      1   vendor/predis/predis/src/Command/Redis/PUBSUB.php5      5  ~eפ      2   vendor/predis/predis/src/Command/Redis/LINSERT.php          i      0   vendor/predis/predis/src/Command/Redis/HVALS.php          ڌ]      0   vendor/predis/predis/src/Command/Redis/FCALL.php         !      0   vendor/predis/predis/src/Command/Redis/HMSET.php        I      2   vendor/predis/predis/src/Command/Redis/PUBLISH.php          6Ѥ      1   vendor/predis/predis/src/Command/Redis/RENAME.php          !G      /   vendor/predis/predis/src/Command/Redis/XADD.php        J+      /   vendor/predis/predis/src/Command/Redis/HSET.php          Rޤ      /   vendor/predis/predis/src/Command/Redis/DUMP.php          pSA      6   vendor/predis/predis/src/Command/Redis/ZRANGEBYLEX.phpS      S  ǳQ      7   vendor/predis/predis/src/Command/Redis/PUNSUBSCRIBE.phpQ      Q  D      /   vendor/predis/predis/src/Command/Redis/INCR.php          ]Rd      0   vendor/predis/predis/src/Command/Redis/BITOP.php              3   vendor/predis/predis/src/Command/Redis/BITFIELD.php          tpg}      .   vendor/predis/predis/src/Command/Redis/SET.php          yM^      1   vendor/predis/predis/src/Command/Redis/GETBIT.php          {N_      2   vendor/predis/predis/src/Command/Redis/GEOHASH.php        o      4   vendor/predis/predis/src/Command/Redis/GEORADIUS.php+      +  s      1   vendor/predis/predis/src/Command/Redis/ZCOUNT.php          )      3   vendor/predis/predis/src/Command/Redis/EXPIREAT.php        }      2   vendor/predis/predis/src/Command/Redis/PERSIST.php                6   vendor/predis/predis/src/Command/Redis/ZINTERSTORE.php                5   vendor/predis/predis/src/Command/Redis/SMISMEMBER.php          Jb+      0   vendor/predis/predis/src/Command/Redis/HKEYS.php                /   vendor/predis/predis/src/Command/Redis/ZADD.php        ،ˤ      /   vendor/predis/predis/src/Command/Redis/PTTL.php          c      0   vendor/predis/predis/src/Command/Redis/HMGET.phpB      B  EA      8   vendor/predis/predis/src/Command/Redis/ZRANGEBYSCORE.phpB      B  .Z      /   vendor/predis/predis/src/Command/Redis/INFO.php                3   vendor/predis/predis/src/Command/Redis/GETRANGE.php          ݽ1      2   vendor/predis/predis/src/Command/Redis/OBJECT_.php          n?      2   vendor/predis/predis/src/Command/Redis/HSTRLEN.php          {      1   vendor/predis/predis/src/Command/Redis/SUNION.phpE      E  j      9   vendor/predis/predis/src/Command/Redis/GEOSEARCHSTORE.php        u"      0   vendor/predis/predis/src/Command/Redis/MULTI.php          S1      2   vendor/predis/predis/src/Command/Redis/EVALSHA.php          k謩      1   vendor/predis/predis/src/Command/Redis/MSETNX.php          s>      0   vendor/predis/predis/src/Command/Redis/ZSCAN.php        Hˤ      2   vendor/predis/predis/src/Command/Redis/SLAVEOF.phpo      o  '      4   vendor/predis/predis/src/Command/Redis/SISMEMBER.php          !      3   vendor/predis/predis/src/Command/Redis/FCALL_RO.php        DĤ      5   vendor/predis/predis/src/Command/Redis/EVALSHA_RO.php          qX      .   vendor/predis/predis/src/Command/Redis/LCS.php        6Ӣ      1   vendor/predis/predis/src/Command/Redis/SETBIT.php          
PfB      A   vendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINFO.php        Ł      B   vendor/predis/predis/src/Command/Redis/CountMinSketch/CMSMERGE.php        (y      G   vendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYPROB.php          'a|      F   vendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYDIM.php          m      C   vendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINCRBY.php          f      B   vendor/predis/predis/src/Command/Redis/CountMinSketch/CMSQUERY.php          Q,      D   vendor/predis/predis/src/Command/Redis/AbstractCommand/BZPOPBase.phpG      G  0      6   vendor/predis/predis/src/Command/Redis/UNSUBSCRIBE.phpO      O  ;)      7   vendor/predis/predis/src/Command/Redis/HINCRBYFLOAT.php          p      2   vendor/predis/predis/src/Command/Redis/ZINCRBY.php          g      /   vendor/predis/predis/src/Command/Redis/SREM.php@      @  aYۤ      0   vendor/predis/predis/src/Command/Redis/RPUSH.phpB      B  r_P      1   vendor/predis/predis/src/Command/Redis/GETDEL.php          |p      /   vendor/predis/predis/src/Command/Redis/HDEL.php@      @  Li      6   vendor/predis/predis/src/Command/Redis/ZRANGESTORE.php        5      3   vendor/predis/predis/src/Command/Redis/FLUSHALL.php          ,~      0   vendor/predis/predis/src/Command/Redis/SMOVE.php          23      1   vendor/predis/predis/src/Command/Redis/APPEND.php                0   vendor/predis/predis/src/Command/Redis/TOUCH.phpC      C  .Ѥ      0   vendor/predis/predis/src/Command/Redis/EVAL_.php        =      2   vendor/predis/predis/src/Command/Redis/DISCARD.php          n)      /   vendor/predis/predis/src/Command/Redis/LLEN.php          UH      /   vendor/predis/predis/src/Command/Redis/LREM.php          ,      7   vendor/predis/predis/src/Command/Redis/BGREWRITEAOF.php2      2  +e:      5   vendor/predis/predis/src/Command/Redis/SINTERCARD.phpU      U  rځ      2   vendor/predis/predis/src/Command/Redis/HEXISTS.php          ۍ!      /   vendor/predis/predis/src/Command/Redis/TYPE.php        }(      0   vendor/predis/predis/src/Command/Redis/LMOVE.php          $      6   vendor/predis/predis/src/Command/Redis/SINTERSTORE.php        4bo      4   vendor/predis/predis/src/Command/Redis/FUNCTIONS.php        GT7      0   vendor/predis/predis/src/Command/Redis/ECHO_.php                0   vendor/predis/predis/src/Command/Redis/HSCAN.php        ^ܤ      1   vendor/predis/predis/src/Command/Redis/SINTER.phpE      E  o򋉤      0   vendor/predis/predis/src/Command/Redis/SETEX.php          Ũ      /   vendor/predis/predis/src/Command/Redis/SORT.php        D>      0   vendor/predis/predis/src/Command/Redis/GETEX.php$      $  _      1   vendor/predis/predis/src/Command/Redis/BITPOS.php          1p      4   vendor/predis/predis/src/Command/Redis/GEOSEARCH.php        &      0   vendor/predis/predis/src/Command/Redis/BRPOP.php        D      1   vendor/predis/predis/src/Command/Redis/ZSCORE.php          8      .   vendor/predis/predis/src/Command/Redis/ACL.php        }K      0   vendor/predis/predis/src/Command/Redis/LPUSH.phpB      B  ҟu      3   vendor/predis/predis/src/Command/Redis/SETRANGE.php                /   vendor/predis/predis/src/Command/Redis/MGET.phpA      A  14      9   vendor/predis/predis/src/Command/Redis/ZREMRANGEBYLEX.php          kF      6   vendor/predis/predis/src/Command/Redis/ZRANDMEMBER.php        3WN      1   vendor/predis/predis/src/Command/Redis/ZUNION.phpf      f  9      2   vendor/predis/predis/src/Command/Redis/ZPOPMIN.php        ƻ      2   vendor/predis/predis/src/Command/Redis/WAITAOF.php          T$      1   vendor/predis/predis/src/Command/Redis/INCRBY.php                6   vendor/predis/predis/src/Command/Redis/SUNIONSTORE.php        lD"      /   vendor/predis/predis/src/Command/Redis/QUIT.php          QV      0   vendor/predis/predis/src/Command/Redis/SSCAN.php        (I(      2   vendor/predis/predis/src/Command/Redis/UNWATCH.php          Τ      1   vendor/predis/predis/src/Command/Redis/CLIENT.php        0      3   vendor/predis/predis/src/Command/Redis/RENAMENX.php          Tdh      1   vendor/predis/predis/src/Command/Redis/LINDEX.php          ]      0   vendor/predis/predis/src/Command/Redis/LMPOP.php        e      /   vendor/predis/predis/src/Command/Redis/SCAN.php
      
        <   vendor/predis/predis/src/Command/Redis/Container/CLUSTER.php          ]n+      D   vendor/predis/predis/src/Command/Redis/Container/Search/FTCURSOR.phpB      B        D   vendor/predis/predis/src/Command/Redis/Container/Search/FTCONFIG.php        $(      G   vendor/predis/predis/src/Command/Redis/Container/ContainerInterface.php          M҅      E   vendor/predis/predis/src/Command/Redis/Container/ContainerFactory.php              8   vendor/predis/predis/src/Command/Redis/Container/ACL.php          .ͤ      F   vendor/predis/predis/src/Command/Redis/Container/AbstractContainer.php0      0  9pL      C   vendor/predis/predis/src/Command/Redis/Container/Json/JSONDEBUG.php          X      F   vendor/predis/predis/src/Command/Redis/Container/FunctionContainer.php          ,WU      2   vendor/predis/predis/src/Command/Redis/SORT_RO.php        ڠ8      2   vendor/predis/predis/src/Command/Redis/ZPOPMAX.php        6      1   vendor/predis/predis/src/Command/Redis/CONFIG.php        %1      0   vendor/predis/predis/src/Command/Redis/ZMPOP.php        kdu      =   vendor/predis/predis/src/Command/Redis/Json/JSONARRAPPEND.php          5۱      7   vendor/predis/predis/src/Command/Redis/Json/JSONGET.phpZ      Z  8@      7   vendor/predis/predis/src/Command/Redis/Json/JSONSET.php        qP      8   vendor/predis/predis/src/Command/Redis/Json/JSONTYPE.php          m      :   vendor/predis/predis/src/Command/Redis/Json/JSONFORGET.php          :'\      =   vendor/predis/predis/src/Command/Redis/Json/JSONARRINSERT.php          c-g      =   vendor/predis/predis/src/Command/Redis/Json/JSONSTRAPPEND.php          哩6      :   vendor/predis/predis/src/Command/Redis/Json/JSONARRLEN.php          c      7   vendor/predis/predis/src/Command/Redis/Json/JSONDEL.php          S      9   vendor/predis/predis/src/Command/Redis/Json/JSONMERGE.php          3       :   vendor/predis/predis/src/Command/Redis/Json/JSONTOGGLE.php          dC      :   vendor/predis/predis/src/Command/Redis/Json/JSONOBJLEN.php          X      :   vendor/predis/predis/src/Command/Redis/Json/JSONSTRLEN.php          ,GD      8   vendor/predis/predis/src/Command/Redis/Json/JSONRESP.php          :+      ;   vendor/predis/predis/src/Command/Redis/Json/JSONARRTRIM.php          m-ݤ      8   vendor/predis/predis/src/Command/Redis/Json/JSONMSET.php          =1/      9   vendor/predis/predis/src/Command/Redis/Json/JSONCLEAR.php          zO3      <   vendor/predis/predis/src/Command/Redis/Json/JSONARRINDEX.php          ǌ      9   vendor/predis/predis/src/Command/Redis/Json/JSONDEBUG.php          [0	      8   vendor/predis/predis/src/Command/Redis/Json/JSONMGET.php        0I      :   vendor/predis/predis/src/Command/Redis/Json/JSONARRPOP.php          X      ;   vendor/predis/predis/src/Command/Redis/Json/JSONOBJKEYS.php          <      =   vendor/predis/predis/src/Command/Redis/Json/JSONNUMINCRBY.php                2   vendor/predis/predis/src/Command/Redis/HGETALL.php              2   vendor/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.php        U      ?   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFCOUNT.php                =   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFADD.php          U7      ?   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFADDNX.php          H\      @   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFEXISTS.php          wg      A   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFRESERVE.php        w      B   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFINSERTNX.php          J      C   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFLOADCHUNK.php          %v      A   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFMEXISTS.php          Y8      B   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFSCANDUMP.php          8      =   vendor/predis/predis/src/Command/Redis/CuckooFilter/CFDEL.php          n_^      2   vendor/predis/predis/src/Command/Redis/MONITOR.php          4      5   vendor/predis/predis/src/Command/Redis/ZINTERCARD.php[      [  ̤      1   vendor/predis/predis/src/Command/Redis/EXPIRE.php          S      0   vendor/predis/predis/src/Command/Redis/WATCH.phpe      e  &~      :   vendor/predis/predis/src/Command/Redis/ZREMRANGEBYRANK.php          o7P      2   vendor/predis/predis/src/Command/Redis/FLUSHDB.php          Hj      /   vendor/predis/predis/src/Command/Redis/COPY.php        [=A      /   vendor/predis/predis/src/Command/Redis/LSET.php          %:      9   vendor/predis/predis/src/Command/Redis/TopK/TOPKQUERY.php          |P+      7   vendor/predis/predis/src/Command/Redis/TopK/TOPKADD.php          Or      8   vendor/predis/predis/src/Command/Redis/TopK/TOPKLIST.phpu      u  ϰˤ      8   vendor/predis/predis/src/Command/Redis/TopK/TOPKINFO.php        Q      ;   vendor/predis/predis/src/Command/Redis/TopK/TOPKRESERVE.php        ww      :   vendor/predis/predis/src/Command/Redis/TopK/TOPKINCRBY.php          rOܤ      1   vendor/predis/predis/src/Command/Redis/ZINTER.phpf      f  X      /   vendor/predis/predis/src/Command/Redis/SADD.php@      @  UU      /   vendor/predis/predis/src/Command/Redis/LPOP.php          U4      /   vendor/predis/predis/src/Command/Redis/TIME.php          z瀤      5   vendor/predis/predis/src/Command/Redis/HRANDFIELD.php        ¶q      2   vendor/predis/predis/src/Command/Redis/ZMSCORE.php          ~      2   vendor/predis/predis/src/Command/Redis/SLOWLOG.php        <U      1   vendor/predis/predis/src/Command/Redis/STRLEN.php                 1   vendor/predis/predis/src/Command/Redis/GEOPOS.php        %      3   vendor/predis/predis/src/Command/Redis/BZPOPMAX.php                6   vendor/predis/predis/src/Command/Redis/INCRBYFLOAT.php          -L      3   vendor/predis/predis/src/Command/Redis/FAILOVER.php        ~      /   vendor/predis/predis/src/Command/Redis/HLEN.php          uӬ      >   vendor/predis/predis/src/Command/Redis/TimeSeries/TSCREATE.php        rP      A   vendor/predis/predis/src/Command/Redis/TimeSeries/TSMREVRANGE.php          \F"      B   vendor/predis/predis/src/Command/Redis/TimeSeries/TSQUERYINDEX.php          X      B   vendor/predis/predis/src/Command/Redis/TimeSeries/TSDELETERULE.php          Dj      =   vendor/predis/predis/src/Command/Redis/TimeSeries/TSALTER.php        $      >   vendor/predis/predis/src/Command/Redis/TimeSeries/TSINCRBY.php        C      <   vendor/predis/predis/src/Command/Redis/TimeSeries/TSMGET.php        C       ;   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.php        4      ;   vendor/predis/predis/src/Command/Redis/TimeSeries/TSADD.php        ~      =   vendor/predis/predis/src/Command/Redis/TimeSeries/TSRANGE.php        t      B   vendor/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.php          j      1   vendor/predis/predis/src/Command/RedisFactory.phpK      K  l"      ,   vendor/predis/predis/src/Command/Factory.phpo      o  I0פ      V   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/LoadStrategy.phpY      Y  ;c      Y   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/RestoreStrategy.php        ?      V   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/KillStrategy.php              W   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/StatsStrategy.php        8`      W   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/FlushStrategy.php        R      V   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/ListStrategy.php9      9  Ċh      V   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php        Y;      X   vendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DeleteStrategy.php        N      G   vendor/predis/predis/src/Command/Strategy/StrategyResolverInterface.php          J      H   vendor/predis/predis/src/Command/Strategy/SubcommandStrategyResolver.php        >      I   vendor/predis/predis/src/Command/Strategy/SubcommandStrategyInterface.php          )ܤ      ?   vendor/predis/predis/src/Command/Argument/ArrayableArgument.php          b      E   vendor/predis/predis/src/Command/Argument/Server/LimitOffsetCount.php        ؤ      C   vendor/predis/predis/src/Command/Argument/Server/LimitInterface.php          Wͧ      7   vendor/predis/predis/src/Command/Argument/Server/To.php_      _  .J      E   vendor/predis/predis/src/Command/Argument/Search/ExplainArguments.phpo       o   <      H   vendor/predis/predis/src/Command/Argument/Search/SpellcheckArguments.php)      )  Svx      D   vendor/predis/predis/src/Command/Argument/Search/SugGetArguments.php+      +  Ocݤ      D   vendor/predis/predis/src/Command/Argument/Search/SearchArguments.php        1      G   vendor/predis/predis/src/Command/Argument/Search/SynUpdateArguments.phpq       q   
      C   vendor/predis/predis/src/Command/Argument/Search/AlterArguments.phpm       m         D   vendor/predis/predis/src/Command/Argument/Search/CommonArguments.php        wG      D   vendor/predis/predis/src/Command/Argument/Search/SugAddArguments.php          a{D      O   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/AbstractField.php        4n%      N   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/NumericField.phpK      K  "H      K   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/TextField.php        R|o      J   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/GeoField.phpC      C  uc      J   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/TagField.php)      )  .      P   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/FieldInterface.php          fn1      M   vendor/predis/predis/src/Command/Argument/Search/SchemaFields/VectorField.phpO      O  ?      D   vendor/predis/predis/src/Command/Argument/Search/CreateArguments.phpQ      Q  S      B   vendor/predis/predis/src/Command/Argument/Search/DropArguments.phpP      P  Ⴘ̤      G   vendor/predis/predis/src/Command/Argument/Search/AggregateArguments.php         e      E   vendor/predis/predis/src/Command/Argument/Search/ProfileArguments.php        Q,      D   vendor/predis/predis/src/Command/Argument/Search/CursorArguments.phpz      z  93      C   vendor/predis/predis/src/Command/Argument/Geospatial/AbstractBy.php              C   vendor/predis/predis/src/Command/Argument/Geospatial/FromLonLat.php        |Ǥ      >   vendor/predis/predis/src/Command/Argument/Geospatial/ByBox.php        wV      A   vendor/predis/predis/src/Command/Argument/Geospatial/ByRadius.phph      h  ym<Ҥ      F   vendor/predis/predis/src/Command/Argument/Geospatial/FromInterface.php                D   vendor/predis/predis/src/Command/Argument/Geospatial/ByInterface.php          %      C   vendor/predis/predis/src/Command/Argument/Geospatial/FromMember.phpC      C  쫤      H   vendor/predis/predis/src/Command/Argument/TimeSeries/DecrByArguments.phpr       r   =b      G   vendor/predis/predis/src/Command/Argument/TimeSeries/RangeArguments.php        ﻋ      G   vendor/predis/predis/src/Command/Argument/TimeSeries/AlterArguments.phpq       q   ,      H   vendor/predis/predis/src/Command/Argument/TimeSeries/IncrByArguments.phpQ      Q  k      H   vendor/predis/predis/src/Command/Argument/TimeSeries/CommonArguments.phpQ      Q  7      F   vendor/predis/predis/src/Command/Argument/TimeSeries/InfoArguments.phpX      X  'פ      F   vendor/predis/predis/src/Command/Argument/TimeSeries/MGetArguments.phpp       p   U      H   vendor/predis/predis/src/Command/Argument/TimeSeries/CreateArguments.phpr       r   Č`Ǥ      E   vendor/predis/predis/src/Command/Argument/TimeSeries/AddArguments.php        y-S      E   vendor/predis/predis/src/Command/Argument/TimeSeries/GetArguments.phpo       o   K      H   vendor/predis/predis/src/Command/Argument/TimeSeries/MRangeArguments.php        Ť      5   vendor/predis/predis/src/Command/FactoryInterface.php          ?      /   vendor/predis/predis/src/Command/RawFactory.php<      <  NGv      5   vendor/predis/predis/src/Command/CommandInterface.php        :      2   vendor/predis/predis/src/Command/ScriptCommand.php        O      ,   vendor/predis/predis/src/Command/Command.php        us      /   vendor/predis/predis/src/Command/RawCommand.php:      :  t      3   vendor/predis/predis/src/Command/Traits/BitByte.php        RX      >   vendor/predis/predis/src/Command/Traits/BloomFilters/Error.php#      #  ;8      C   vendor/predis/predis/src/Command/Traits/BloomFilters/BucketSize.php`      `  *      B   vendor/predis/predis/src/Command/Traits/BloomFilters/Expansion.php4      4  ?      >   vendor/predis/predis/src/Command/Traits/BloomFilters/Items.php        4      A   vendor/predis/predis/src/Command/Traits/BloomFilters/Capacity.phpG      G  P,E      F   vendor/predis/predis/src/Command/Traits/BloomFilters/MaxIterations.php              A   vendor/predis/predis/src/Command/Traits/BloomFilters/NoCreate.phpg      g  \      .   vendor/predis/predis/src/Command/Traits/DB.php        q      3   vendor/predis/predis/src/Command/Traits/Weights.php              5   vendor/predis/predis/src/Command/Traits/LeftRight.php3      3        3   vendor/predis/predis/src/Command/Traits/Sorting.php1      1  U;a      7   vendor/predis/predis/src/Command/Traits/To/ServerTo.php        g      ;   vendor/predis/predis/src/Command/Traits/By/ByLexByScore.php        V~      9   vendor/predis/predis/src/Command/Traits/By/ByArgument.php              4   vendor/predis/predis/src/Command/Traits/By/GeoBy.phpl      l  }l\      ;   vendor/predis/predis/src/Command/Traits/With/WithScores.php              :   vendor/predis/predis/src/Command/Traits/With/WithCoord.phpg      g  IX1H      9   vendor/predis/predis/src/Command/Traits/With/WithHash.php@      @  /Q      ;   vendor/predis/predis/src/Command/Traits/With/WithValues.phpr      r  V      9   vendor/predis/predis/src/Command/Traits/With/WithDist.php@      @  |\      /   vendor/predis/predis/src/Command/Traits/Rev.php        ^      =   vendor/predis/predis/src/Command/Traits/Limit/LimitObject.php9      9  C1Τ      7   vendor/predis/predis/src/Command/Traits/Limit/Limit.php              5   vendor/predis/predis/src/Command/Traits/Storedist.phpb      b  *M      8   vendor/predis/predis/src/Command/Traits/From/GeoFrom.php~      ~        :   vendor/predis/predis/src/Command/Traits/MinMaxModifier.phpd      d  ~F7      0   vendor/predis/predis/src/Command/Traits/Keys.php        }      3   vendor/predis/predis/src/Command/Traits/Get/Get.phpp      p  ^Ȥ      @   vendor/predis/predis/src/Command/Traits/Expire/ExpireOptions.php        咄      6   vendor/predis/predis/src/Command/Traits/Json/Space.php        (      =   vendor/predis/predis/src/Command/Traits/Json/NxXxArgument.php        2':      8   vendor/predis/predis/src/Command/Traits/Json/Newline.php        [6`      7   vendor/predis/predis/src/Command/Traits/Json/Indent.php        dO      5   vendor/predis/predis/src/Command/Traits/Aggregate.php        a)      1   vendor/predis/predis/src/Command/Traits/Count.php@      @  'ڥ      3   vendor/predis/predis/src/Command/Traits/Replace.phpX      X  <vQ      3   vendor/predis/predis/src/Command/Traits/Timeout.php              ?   vendor/predis/predis/src/Command/PrefixableCommandInterface.php          ^      ?   vendor/predis/predis/src/Replication/MissingMasterException.php          GLM      6   vendor/predis/predis/src/Replication/RoleException.php          4Ig7      <   vendor/predis/predis/src/Replication/ReplicationStrategy.php        
Q      3   vendor/predis/predis/src/ClientContextInterface.php        vs      2   vendor/predis/predis/src/Configuration/Options.php        O3      :   vendor/predis/predis/src/Configuration/OptionInterface.php          P"      <   vendor/predis/predis/src/Configuration/Option/Exceptions.php        '      7   vendor/predis/predis/src/Configuration/Option/CRC16.php        g[e      9   vendor/predis/predis/src/Configuration/Option/Cluster.php5      5  zP      :   vendor/predis/predis/src/Configuration/Option/Commands.php        RD      =   vendor/predis/predis/src/Configuration/Option/Connections.php;	      ;	  L?      =   vendor/predis/predis/src/Configuration/Option/Replication.php        DA      8   vendor/predis/predis/src/Configuration/Option/Prefix.phpX      X  VH      ;   vendor/predis/predis/src/Configuration/Option/Aggregate.php        }S      ;   vendor/predis/predis/src/Configuration/OptionsInterface.php4      4  + W      #   vendor/predis/predis/src/Client.php%#      %#  7c      3   vendor/predis/predis/src/Pipeline/RelayPipeline.php        *|6      3   vendor/predis/predis/src/Pipeline/FireAndForget.phpm      m  Ln      :   vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php	      	  6^      .   vendor/predis/predis/src/Pipeline/Pipeline.php        $c      1   vendor/predis/predis/src/Pipeline/RelayAtomic.php        #-      ,   vendor/predis/predis/src/Pipeline/Atomic.phpU	      U	        E   vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php        u      ;   vendor/predis/predis/src/Cluster/Distributor/KetamaRing.phpz      z  Ä#      9   vendor/predis/predis/src/Cluster/Distributor/HashRing.php        ᗤ      C   vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.phpz       z   ~      ,   vendor/predis/predis/src/Cluster/SlotMap.phpE      E  RNw      2   vendor/predis/predis/src/Cluster/RedisStrategy.php        澳      /   vendor/predis/predis/src/Cluster/Hash/CRC16.php	      	  ,\      @   vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php          cӤ      8   vendor/predis/predis/src/Cluster/Hash/PhpiredisCRC16.php         T      3   vendor/predis/predis/src/Cluster/PredisStrategy.php        oQ      4   vendor/predis/predis/src/Cluster/ClusterStrategy.php&$      &$  A/      6   vendor/predis/predis/src/Cluster/StrategyInterface.php>      >  n      !   vendor/predis/predis/autoload.phpU       U   LA=N      -   vendor/sebastian/type/src/type/ObjectType.php        P      .   vendor/sebastian/type/src/type/UnknownType.php              ,   vendor/sebastian/type/src/type/MixedType.php        $      4   vendor/sebastian/type/src/type/GenericObjectType.phpT      T        -   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.php        D@      3   vendor/sebastian/type/src/type/IntersectionType.php              +   vendor/sebastian/type/src/type/NullType.php        A      /   vendor/sebastian/type/src/type/IterableType.php8      8  QZ      ,   vendor/sebastian/type/src/type/NeverType.phpc      c  |      +   vendor/sebastian/type/src/type/TrueType.php        XQ      /   vendor/sebastian/type/src/type/CallableType.php        -      ,   vendor/sebastian/type/src/type/UnionType.php        Ij;      '   vendor/sebastian/type/src/type/Type.php
      
  .l      .   vendor/sebastian/type/src/ReflectionMapper.php        k      &   vendor/sebastian/type/src/TypeName.php/      /  ?7      1   vendor/sebastian/type/src/exception/Exception.php          9      8   vendor/sebastian/type/src/exception/RuntimeException.php          h      '   vendor/sebastian/type/src/Parameter.php_      _  6-A      .   vendor/sebastian/global-state/src/Restorer.php}      }  &      2   vendor/sebastian/global-state/src/CodeExporter.php        Kf      1   vendor/sebastian/global-state/src/ExcludeList.phpJ      J  Z      .   vendor/sebastian/global-state/src/Snapshot.php        #ce      :   vendor/sebastian/global-state/src/exceptions/Exception.php          *      A   vendor/sebastian/global-state/src/exceptions/RuntimeException.php          XB      ,   vendor/sebastian/code-unit/src/TraitUnit.php          ͕b      ,   vendor/sebastian/code-unit/src/ClassUnit.php          դ      =   vendor/sebastian/code-unit/src/CodeUnitCollectionIterator.php        ;      5   vendor/sebastian/code-unit/src/CodeUnitCollection.php1      1  
t      /   vendor/sebastian/code-unit/src/FunctionUnit.php          a      0   vendor/sebastian/code-unit/src/InterfaceUnit.php          CT      2   vendor/sebastian/code-unit/src/TraitMethodUnit.php          }1L      +   vendor/sebastian/code-unit/src/CodeUnit.php        W¤      6   vendor/sebastian/code-unit/src/InterfaceMethodUnit.php          ä      2   vendor/sebastian/code-unit/src/ClassMethodUnit.php          :]8      )   vendor/sebastian/code-unit/src/Mapper.php        +>      A   vendor/sebastian/code-unit/src/exceptions/ReflectionException.php          N:      7   vendor/sebastian/code-unit/src/exceptions/Exception.php          T      >   vendor/sebastian/code-unit/src/exceptions/NoTraitException.php                F   vendor/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.php          zۤ      2   vendor/sebastian/recursion-context/src/Context.php        \Ĥ      4   vendor/sebastian/recursion-context/src/Exception.php          +D      C   vendor/sebastian/recursion-context/src/InvalidArgumentException.php          k0      8   vendor/sebastian/code-unit-reverse-lookup/src/Wizard.php	      	  /      5   vendor/sebastian/object-enumerator/src/Enumerator.php        O+?$      4   vendor/sebastian/object-enumerator/src/Exception.php          1      C   vendor/sebastian/object-enumerator/src/InvalidArgumentException.php          @q      9   vendor/sebastian/object-reflector/src/ObjectReflector.php        +Ob      3   vendor/sebastian/object-reflector/src/Exception.php          3      B   vendor/sebastian/object-reflector/src/InvalidArgumentException.php          W      (   vendor/sebastian/version/src/Version.php        0      C   vendor/sebastian/complexity/src/Complexity/ComplexityCollection.php        OϤ      K   vendor/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.phpu      u  b      9   vendor/sebastian/complexity/src/Complexity/Complexity.php        kդ      .   vendor/sebastian/complexity/src/Calculator.phpV      V  &n      H   vendor/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.phph      h  UA      R   vendor/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php        1頤      7   vendor/sebastian/complexity/src/Exception/Exception.php          ҹL      >   vendor/sebastian/complexity/src/Exception/RuntimeException.php          bm      *   vendor/sebastian/exporter/src/Exporter.php        ˎ_      ,   vendor/sebastian/environment/src/Runtime.php        J:      4   vendor/sebastian/environment/src/OperatingSystem.phpP      P  D      ,   vendor/sebastian/environment/src/Console.phpr      r  k      $   vendor/sebastian/diff/src/Parser.php        13[      #   vendor/sebastian/diff/src/Chunk.php        Ejt      "   vendor/sebastian/diff/src/Diff.php+      +  tg/      M   vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.phpR      R  H      "   vendor/sebastian/diff/src/Line.php        E_M      $   vendor/sebastian/diff/src/Differ.php?      ?  Z      O   vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php        jv      @   vendor/sebastian/diff/src/LongestCommonSubsequenceCalculator.php          /      1   vendor/sebastian/diff/src/Exception/Exception.php          Vj      >   vendor/sebastian/diff/src/Exception/ConfigurationException.phpJ      J  ?      @   vendor/sebastian/diff/src/Exception/InvalidArgumentException.php          =|      ?   vendor/sebastian/diff/src/Output/DiffOutputBuilderInterface.php          2G\+      :   vendor/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php        &      C   vendor/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php_      _  1      =   vendor/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php        b\,      ?   vendor/sebastian/diff/src/Output/AbstractChunkOutputBuilder.phpD      D  tH*      ?   vendor/sebastian/resource-operations/src/ResourceOperations.php        }      7   vendor/sebastian/resource-operations/build/generate.php        d      >   vendor/sebastian/comparator/src/SplObjectStorageComparator.php        .Y      5   vendor/sebastian/comparator/src/DOMNodeComparator.php        g|S      6   vendor/sebastian/comparator/src/DateTimeComparator.php              +   vendor/sebastian/comparator/src/Factory.php2      2  ~z      3   vendor/sebastian/comparator/src/ArrayComparator.php	      	  Z      2   vendor/sebastian/comparator/src/TypeComparator.phpR      R  $k      4   vendor/sebastian/comparator/src/ObjectComparator.php        Fƻ      6   vendor/sebastian/comparator/src/ResourceComparator.php        }K-      5   vendor/sebastian/comparator/src/ComparisonFailure.php4      4  =̤      5   vendor/sebastian/comparator/src/NumericComparator.php        +      7   vendor/sebastian/comparator/src/ExceptionComparator.php        ט\      .   vendor/sebastian/comparator/src/Comparator.php        cЇ      4   vendor/sebastian/comparator/src/ScalarComparator.php}      }  )f      4   vendor/sebastian/comparator/src/DoubleComparator.phpx      x  B      8   vendor/sebastian/comparator/src/exceptions/Exception.php          Ф      ?   vendor/sebastian/comparator/src/exceptions/RuntimeException.php          0      8   vendor/sebastian/comparator/src/MockObjectComparator.php        ětܤ      *   vendor/sebastian/cli-parser/src/Parser.php{      {  f      G   vendor/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.phpJ      J  kwA      E   vendor/sebastian/cli-parser/src/exceptions/UnknownOptionException.phpC      C  4x      8   vendor/sebastian/cli-parser/src/exceptions/Exception.php          }      R   vendor/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.phpc      c  =      U   vendor/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.phpl      l  $u      :   vendor/sebastian/lines-of-code/src/LineCountingVisitor.php;      ;  hd      .   vendor/sebastian/lines-of-code/src/Counter.php        uZV      2   vendor/sebastian/lines-of-code/src/LinesOfCode.php        (t      G   vendor/sebastian/lines-of-code/src/Exception/NegativeValueException.php          A+      I   vendor/sebastian/lines-of-code/src/Exception/IllogicalValuesException.php          kCo      :   vendor/sebastian/lines-of-code/src/Exception/Exception.php          ߟʤ      A   vendor/sebastian/lines-of-code/src/Exception/RuntimeException.php          Xmh      <   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\      \  
      B   vendor/codeception/module-db/src/Codeception/Lib/Interfaces/Db.php        ݪ\      A   vendor/codeception/module-db/src/Codeception/Lib/Driver/MySql.php        o      >   vendor/codeception/module-db/src/Codeception/Lib/Driver/Db.php%      %  g¤      B   vendor/codeception/module-db/src/Codeception/Lib/Driver/Sqlite.php
      
  `0      ?   vendor/codeception/module-db/src/Codeception/Lib/Driver/Oci.phpe      e  .:      B   vendor/codeception/module-db/src/Codeception/Lib/Driver/SqlSrv.php
      
  ޅʌ      F   vendor/codeception/module-db/src/Codeception/Lib/Driver/PostgreSql.php-      -  QR      @   vendor/codeception/module-db/src/Codeception/Lib/DbPopulator.php%      %  (դ      A   vendor/codeception/lib-web/src/Lib/Interfaces/ScreenshotSaver.phpX      X  ]ퟤ      @   vendor/codeception/lib-web/src/Lib/Interfaces/ElementLocator.php        6ݤ      A   vendor/codeception/lib-web/src/Lib/Interfaces/PageSourceSaver.phpu      u  [|      5   vendor/codeception/lib-web/src/Lib/Interfaces/Web.phph      h  v,      A   vendor/codeception/lib-web/src/Lib/Interfaces/SessionSnapshot.php        ͞7      8   vendor/codeception/lib-web/src/Lib/Interfaces/Remote.php        g      >   vendor/codeception/lib-web/src/Lib/Interfaces/MultiSession.php7      7  i      2   vendor/codeception/lib-web/src/Constraint/Page.php        m      +   vendor/codeception/lib-web/src/Util/Uri.php        [      /   vendor/codeception/lib-web/src/Util/Locator.php&      &  6%Ѥ      F   vendor/codeception/lib-web/src/Exception/MalformedLocatorException.phpG      G  Ik      <   vendor/codeception/lib-web/src/Exception/ElementNotFound.php        (񞖤      <   vendor/codeception/module-ftp/src/Codeception/Module/FTP.phpbW      bW   $ڢ      F   vendor/codeception/module-sequence/src/Codeception/Module/Sequence.php}      }  G
      >   vendor/codeception/module-sequence/src/Codeception/Util/sq.php        YEä      >   vendor/codeception/module-rest/src/Codeception/Step/AsJson.php        9m1e      >   vendor/codeception/module-rest/src/Codeception/Module/REST.phpe      e   ~      F   vendor/codeception/module-rest/src/Codeception/Constraint/JsonType.php              J   vendor/codeception/module-rest/src/Codeception/Constraint/JsonContains.php              @   vendor/codeception/module-rest/src/Codeception/Util/JsonType.php"      "  6z      O   vendor/codeception/module-rest/src/Codeception/Util/ArrayContainsComparator.phpr      r  n      A   vendor/codeception/module-rest/src/Codeception/Util/JsonArray.php        l      @   vendor/codeception/module-queue/src/Codeception/Module/Queue.php,      ,   u      H   vendor/codeception/module-queue/src/Codeception/Lib/Interfaces/Queue.php}      }  Q      H   vendor/codeception/module-queue/src/Codeception/Lib/Driver/Beanstalk.php        /5դ      J   vendor/codeception/module-queue/src/Codeception/Lib/Driver/Pheanstalk4.php	      	  ٬Bz      C   vendor/codeception/module-queue/src/Codeception/Lib/Driver/Iron.php	      	  )      H   vendor/codeception/module-queue/src/Codeception/Lib/Driver/AmazonSQS.phpV      V  u      J   vendor/codeception/module-phpbrowser/src/Codeception/Module/PhpBrowser.php        i(      M   vendor/codeception/module-phpbrowser/src/Codeception/Lib/Connector/Guzzle.php/      /  E24*      F   vendor/codeception/module-memcache/src/Codeception/Module/Memcache.phpH      H  Mۤ      E   vendor/codeception/lib-innerbrowser/src/Codeception/Lib/Framework.php              H   vendor/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.php      EĤ      J   vendor/codeception/lib-innerbrowser/src/Codeception/Constraint/Crawler.phpa	      a	  φ      M   vendor/codeception/lib-innerbrowser/src/Codeception/Constraint/CrawlerNot.phpe      e  (yZ      E   vendor/codeception/lib-innerbrowser/src/Codeception/Util/HttpCode.php!      !  o=      V   vendor/codeception/lib-innerbrowser/src/Codeception/Exception/ExternalUrlException.php          u]      ?   vendor/codeception/codeception/src/Codeception/Template/Api.php{      {  6      E   vendor/codeception/codeception/src/Codeception/Template/Bootstrap.php.      .  US      @   vendor/codeception/codeception/src/Codeception/Template/Unit.php        \      F   vendor/codeception/codeception/src/Codeception/Template/Acceptance.php        cI[      H   vendor/codeception/codeception/src/Codeception/Template/Dependencies.php>      >  j⮤      :   vendor/codeception/codeception/src/Codeception/Example.php	      	  ̤      9   vendor/codeception/codeception/src/Codeception/Module.php          ?      9   vendor/codeception/codeception/src/Codeception/Events.phpP      P  G      <   vendor/codeception/codeception/src/Codeception/Test/Cept.php5
      5
  Š      <   vendor/codeception/codeception/src/Codeception/Test/Cest.php"      "  (      N   vendor/codeception/codeception/src/Codeception/Test/Interfaces/Descriptive.php          ۤ      Q   vendor/codeception/codeception/src/Codeception/Test/Interfaces/StrictCoverage.php          6K      L   vendor/codeception/codeception/src/Codeception/Test/Interfaces/Dependent.phpw       w   9Ӝ      H   vendor/codeception/codeception/src/Codeception/Test/Interfaces/Plain.phpo       o   4;\      Q   vendor/codeception/codeception/src/Codeception/Test/Interfaces/ScenarioDriven.php[      [        K   vendor/codeception/codeception/src/Codeception/Test/Interfaces/Reported.php          p[w      B   vendor/codeception/codeception/src/Codeception/Test/Descriptor.php        Sˤ      C   vendor/codeception/codeception/src/Codeception/Test/Loader/Cept.php        doD      C   vendor/codeception/codeception/src/Codeception/Test/Loader/Cest.php        J      C   vendor/codeception/codeception/src/Codeception/Test/Loader/Unit.php              F   vendor/codeception/codeception/src/Codeception/Test/Loader/Gherkin.php"      "  f`      N   vendor/codeception/codeception/src/Codeception/Test/Loader/LoaderInterface.php          4Ӥ      <   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        mv      P   vendor/codeception/codeception/src/Codeception/Test/Feature/AssertionCounter.php        j.      L   vendor/codeception/codeception/src/Codeception/Test/Feature/CodeCoverage.php              N   vendor/codeception/codeception/src/Codeception/Test/Feature/ScenarioLoader.php        
      Q   vendor/codeception/codeception/src/Codeception/Test/Feature/MetadataCollector.php8      8  ׈      W   vendor/codeception/codeception/src/Codeception/Test/Feature/IgnoreIfMetadataBlocked.php        V=)      ?   vendor/codeception/codeception/src/Codeception/Test/Gherkin.phpw       w   ?      G   vendor/codeception/codeception/src/Codeception/Test/TestCaseWrapper.php        +6Ť      D   vendor/codeception/codeception/src/Codeception/Test/DataProvider.php        U"      >   vendor/codeception/codeception/src/Codeception/Test/Loader.phpN      N  p8      <   vendor/codeception/codeception/src/Codeception/Test/Test.php'      '  4      =   vendor/codeception/codeception/src/Codeception/Step/TryTo.php        <T      A   vendor/codeception/codeception/src/Codeception/Step/Assertion.php          6      E   vendor/codeception/codeception/src/Codeception/Step/GeneratedStep.php          [       Q   vendor/codeception/codeception/src/Codeception/Step/Argument/PasswordArgument.php        :^      P   vendor/codeception/codeception/src/Codeception/Step/Argument/FormattedOutput.php        `Wߤ      =   vendor/codeception/codeception/src/Codeception/Step/Retry.phps	      s	  e      ?   vendor/codeception/codeception/src/Codeception/Step/Comment.phpl      l  &      >   vendor/codeception/codeception/src/Codeception/Step/Action.php          2P      B   vendor/codeception/codeception/src/Codeception/Step/Incomplete.php        D      @   vendor/codeception/codeception/src/Codeception/Step/Executor.php+      +  F8      L   vendor/codeception/codeception/src/Codeception/Step/ConditionalAssertion.php4      4  u~      <   vendor/codeception/codeception/src/Codeception/Step/Skip.php        5ͤ      <   vendor/codeception/codeception/src/Codeception/Step/Meta.php        X      A   vendor/codeception/codeception/src/Codeception/Step/Condition.php          A      C   vendor/codeception/codeception/src/Codeception/ResultAggregator.php:      :  ="      8   vendor/codeception/codeception/src/Codeception/Suite.php'      '  (      D   vendor/codeception/codeception/src/Codeception/Subscriber/Module.php$      $  ޤ      G   vendor/codeception/codeception/src/Codeception/Subscriber/Bootstrap.php        +      F   vendor/codeception/codeception/src/Codeception/Subscriber/FailFast.php{      {  jN      Q   vendor/codeception/codeception/src/Codeception/Subscriber/GracefulTermination.php{      {  2      M   vendor/codeception/codeception/src/Codeception/Subscriber/BeforeAfterTest.php        [8      I   vendor/codeception/codeception/src/Codeception/Subscriber/Deprecation.php        W      J   vendor/codeception/codeception/src/Codeception/Subscriber/ErrorHandler.php        i      I   vendor/codeception/codeception/src/Codeception/Subscriber/PrepareTest.php        \c      M   vendor/codeception/codeception/src/Codeception/Subscriber/ExtensionLoader.phpD      D  z      V   vendor/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEventsTrait.php          M       I   vendor/codeception/codeception/src/Codeception/Subscriber/AutoRebuild.php	      	  |      E   vendor/codeception/codeception/src/Codeception/Subscriber/Console.phpi      i  +      J   vendor/codeception/codeception/src/Codeception/Subscriber/Dependencies.php        Sֱ      N   vendor/codeception/codeception/src/Codeception/Coverage/Subscriber/Printer.php        ~      L   vendor/codeception/codeception/src/Codeception/Coverage/Subscriber/Local.php        {      S   vendor/codeception/codeception/src/Codeception/Coverage/Subscriber/RemoteServer.php        [GB      R   vendor/codeception/codeception/src/Codeception/Coverage/Subscriber/LocalServer.php,      ,  g      B   vendor/codeception/codeception/src/Codeception/Coverage/Filter.php        ٤      K   vendor/codeception/codeception/src/Codeception/Coverage/SuiteSubscriber.php~      ~        R   vendor/codeception/codeception/src/Codeception/Coverage/PhpCodeCoverageFactory.php&      &  ߝ      <   vendor/codeception/codeception/src/Codeception/Extension.php        /      C   vendor/codeception/codeception/src/Codeception/Lib/Notification.php              =   vendor/codeception/codeception/src/Codeception/Lib/Parser.php        #      F   vendor/codeception/codeception/src/Codeception/Lib/ModuleContainer.php>      >  K5^V      A   vendor/codeception/codeception/src/Codeception/Lib/PauseShell.php        i~C      J   vendor/codeception/codeception/src/Codeception/Lib/Console/ReplHistory.php        V&[      J   vendor/codeception/codeception/src/Codeception/Lib/Console/DiffFactory.php        (#G      M   vendor/codeception/codeception/src/Codeception/Lib/Console/MessageFactory.php[      [  )*      E   vendor/codeception/codeception/src/Codeception/Lib/Console/Output.php        IT      F   vendor/codeception/codeception/src/Codeception/Lib/Console/Message.php        a=      H   vendor/codeception/codeception/src/Codeception/Lib/Console/Colorizer.php<      <  )9      R   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/DoctrineProvider.php          54w      N   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/PartedModule.php        s<      Q   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.php          ~      N   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/ActiveRecord.php              E   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php@       @   j{      L   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/DataMapper.php        VP      U   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/ConflictsWithModule.php          StK      P   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/ConsolePrinter.php)      )  yYL      Q   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/DependsOnModule.phpJ      J  nN-      E   vendor/codeception/codeception/src/Codeception/Lib/Interfaces/API.phpc       c   lɤ      C   vendor/codeception/codeception/src/Codeception/Lib/ParamsLoader.php4      4  ݆ܤ      G   vendor/codeception/codeception/src/Codeception/Lib/Generator/Helper.php        A%      E   vendor/codeception/codeception/src/Codeception/Lib/Generator/Cest.php        ԛ      F   vendor/codeception/codeception/src/Codeception/Lib/Generator/Group.phpK      K  ]x      P   vendor/codeception/codeception/src/Codeception/Lib/Generator/GherkinSnippets.php        Ҹ'      K   vendor/codeception/codeception/src/Codeception/Lib/Generator/PageObject.php        -      H   vendor/codeception/codeception/src/Codeception/Lib/Generator/Actions.php)      )   ѩ      H   vendor/codeception/codeception/src/Codeception/Lib/Generator/Feature.php        u-      F   vendor/codeception/codeception/src/Codeception/Lib/Generator/Actor.php        Wu      K   vendor/codeception/codeception/src/Codeception/Lib/Generator/StepObject.php        Ѥ      Q   vendor/codeception/codeception/src/Codeception/Lib/Generator/Shared/Classname.php        r=      I   vendor/codeception/codeception/src/Codeception/Lib/Generator/Snapshot.php        ts      E   vendor/codeception/codeception/src/Codeception/Lib/Generator/Test.php^      ^  7X:      C   vendor/codeception/codeception/src/Codeception/Lib/GroupManager.php5      5  ׈ۤ      I   vendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Retry.php        *      K   vendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Comment.phpP      P  "      J   vendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Friend.php        hb>      I   vendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Pause.php        Y芤      =   vendor/codeception/codeception/src/Codeception/Lib/Friend.php        M      9   vendor/codeception/codeception/src/Codeception/Lib/Di.php/      /  !{5      `   vendor/codeception/codeception/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php        E      I   vendor/codeception/codeception/src/Codeception/Event/PrintResultEvent.phpe      e  U"      B   vendor/codeception/codeception/src/Codeception/Event/TestEvent.php        ص      B   vendor/codeception/codeception/src/Codeception/Event/FailEvent.phpl      l  ^       C   vendor/codeception/codeception/src/Codeception/Event/SuiteEvent.php        [?+      B   vendor/codeception/codeception/src/Codeception/Event/StepEvent.php        vQ      ;   vendor/codeception/codeception/src/Codeception/Codecept.php'      '  :Tb      S   vendor/codeception/codeception/src/Codeception/Reporter/template/substeps.html.dist#      #  ͠w      S   vendor/codeception/codeception/src/Codeception/Reporter/template/scenario.html.dist        S&      O   vendor/codeception/codeception/src/Codeception/Reporter/template/fail.html.dist;       ;   ˟      T   vendor/codeception/codeception/src/Codeception/Reporter/template/scenarios.html.dist        /Z      O   vendor/codeception/codeception/src/Codeception/Reporter/template/step.html.distb       b   d      P   vendor/codeception/codeception/src/Codeception/Reporter/template/suite.html.dist3       3   |      Z   vendor/codeception/codeception/src/Codeception/Reporter/template/scenario_header.html.dist2       2   "      I   vendor/codeception/codeception/src/Codeception/Reporter/ReportPrinter.php          ܤ      K   vendor/codeception/codeception/src/Codeception/Reporter/PhpUnitReporter.php
      
  Lx      I   vendor/codeception/codeception/src/Codeception/Reporter/JUnitReporter.php-      -  E      H   vendor/codeception/codeception/src/Codeception/Reporter/HtmlReporter.php\0      \0  \      @   vendor/codeception/codeception/src/Codeception/TestInterface.php	      	  l      8   vendor/codeception/codeception/src/Codeception/Actor.php              ?   vendor/codeception/codeception/src/Codeception/SuiteManager.php)      )  ~      ;   vendor/codeception/codeception/src/Codeception/Scenario.php        wg79      >   vendor/codeception/codeception/src/Codeception/GroupObject.php        2P      >   vendor/codeception/codeception/src/Codeception/Application.php9      9  _>DȤ      @   vendor/codeception/codeception/src/Codeception/Configuration.phpd      d        K   vendor/codeception/codeception/src/Codeception/Command/GenerateSnapshot.php        ̳ʤ      N   vendor/codeception/codeception/src/Codeception/Command/GenerateEnvironment.php        R      A   vendor/codeception/codeception/src/Codeception/Command/DryRun.php        :c      D   vendor/codeception/codeception/src/Codeception/Command/Bootstrap.php        jA      J   vendor/codeception/codeception/src/Codeception/Command/GherkinSnippets.php        q(Ƥ      M   vendor/codeception/codeception/src/Codeception/Command/GeneratePageObject.php	      	  Z0      @   vendor/codeception/codeception/src/Codeception/Command/Clean.phpd      d  ^      >   vendor/codeception/codeception/src/Codeception/Command/Run.phpy      y  s      H   vendor/codeception/codeception/src/Codeception/Command/GenerateGroup.php        ä      G   vendor/codeception/codeception/src/Codeception/Command/GenerateCest.php        t&      G   vendor/codeception/codeception/src/Codeception/Command/GherkinSteps.php              L   vendor/codeception/codeception/src/Codeception/Command/GenerateScenarios.php        'Ф      M   vendor/codeception/codeception/src/Codeception/Command/GenerateStepObject.php        r      J   vendor/codeception/codeception/src/Codeception/Command/GenerateFeature.php9      9  NS      E   vendor/codeception/codeception/src/Codeception/Command/SelfUpdate.php
      
        L   vendor/codeception/codeception/src/Codeception/Command/Shared/StyleTrait.php        g      Q   vendor/codeception/codeception/src/Codeception/Command/Shared/FileSystemTrait.php$      $  2M
      L   vendor/codeception/codeception/src/Codeception/Command/Shared/ActorTrait.phpC      C  E      M   vendor/codeception/codeception/src/Codeception/Command/Shared/ConfigTrait.php;
      ;
  ^A      I   vendor/codeception/codeception/src/Codeception/Command/GenerateHelper.php
      
  +C      H   vendor/codeception/codeception/src/Codeception/Command/GenerateSuite.php        k@      G   vendor/codeception/codeception/src/Codeception/Command/GenerateTest.php4      4  X      B   vendor/codeception/codeception/src/Codeception/Command/Console.php        >      E   vendor/codeception/codeception/src/Codeception/Command/Completion.php        |      M   vendor/codeception/codeception/src/Codeception/Command/CompletionFallback.php              ?   vendor/codeception/codeception/src/Codeception/Command/Init.phpN      N  :s      I   vendor/codeception/codeception/src/Codeception/Command/ConfigValidate.php        PA      @   vendor/codeception/codeception/src/Codeception/Command/Build.php        i      @   vendor/codeception/codeception/src/Codeception/Util/Fixtures.php        J      H   vendor/codeception/codeception/src/Codeception/Util/ReflectionHelper.php         c      =   vendor/codeception/codeception/src/Codeception/Util/Debug.php              D   vendor/codeception/codeception/src/Codeception/Util/PathResolver.php%      %  Jx      B   vendor/codeception/codeception/src/Codeception/Util/FileSystem.phpD	      D	  ?ͤ      @   vendor/codeception/codeception/src/Codeception/Util/Autoload.php        /=      B   vendor/codeception/codeception/src/Codeception/Util/Annotation.phpG      G  K\\      H   vendor/codeception/codeception/src/Codeception/Util/StackTraceFilter.php        /      =   vendor/codeception/codeception/src/Codeception/Util/Maybe.php        p^g      I   vendor/codeception/codeception/src/Codeception/Util/Shared/Namespaces.php        @yj      @   vendor/codeception/codeception/src/Codeception/Util/Template.php        F-
      F   vendor/codeception/codeception/src/Codeception/Util/ActionSequence.php        ߔˤ      ?   vendor/codeception/codeception/src/Codeception/InitTemplate.phpu(      u(  eĤ      I   vendor/codeception/codeception/src/Codeception/CustomCommandInterface.php                B   vendor/codeception/codeception/src/Codeception/Attribute/Group.php        %uf      D   vendor/codeception/codeception/src/Codeception/Attribute/Prepare.php          \r      A   vendor/codeception/codeception/src/Codeception/Attribute/Then.php          U$      D   vendor/codeception/codeception/src/Codeception/Attribute/Depends.php	      	  פ      H   vendor/codeception/codeception/src/Codeception/Attribute/BeforeClass.php          (|Ӥ      E   vendor/codeception/codeception/src/Codeception/Attribute/Examples.php          2d      B   vendor/codeception/codeception/src/Codeception/Attribute/After.php          Y      G   vendor/codeception/codeception/src/Codeception/Attribute/Incomplete.php          qӤ      C   vendor/codeception/codeception/src/Codeception/Attribute/Before.php          %ɤ      G   vendor/codeception/codeception/src/Codeception/Attribute/AfterClass.php          Dͤ      I   vendor/codeception/codeception/src/Codeception/Attribute/DataProvider.php          ljB      A   vendor/codeception/codeception/src/Codeception/Attribute/Skip.php                @   vendor/codeception/codeception/src/Codeception/Attribute/Env.php          ]Ť      B   vendor/codeception/codeception/src/Codeception/Attribute/Given.php          	      A   vendor/codeception/codeception/src/Codeception/Attribute/When.php          x      7   vendor/codeception/codeception/src/Codeception/Step.php],      ],  ' G      ;   vendor/codeception/codeception/src/Codeception/Snapshot.php]      ]  W.      B   vendor/codeception/codeception/src/Codeception/Exception/Error.phpg      g  ȏ;      O   vendor/codeception/codeception/src/Codeception/Exception/InjectionException.php          {DΤ      O   vendor/codeception/codeception/src/Codeception/Exception/ExtensionException.php4      4  ٤      K   vendor/codeception/codeception/src/Codeception/Exception/ParseException.php~       ~   ͡$      M   vendor/codeception/codeception/src/Codeception/Exception/ThrowableWrapper.php        '3kh      L   vendor/codeception/codeception/src/Codeception/Exception/RemoteException.php,      ,  D      L   vendor/codeception/codeception/src/Codeception/Exception/ContentNotFound.php          ¥      Q   vendor/codeception/codeception/src/Codeception/Exception/InvalidTestException.php          {ä      O   vendor/codeception/codeception/src/Codeception/Exception/TestParseException.php        *̤      Q   vendor/codeception/codeception/src/Codeception/Exception/TestRuntimeException.php           ȳ      H   vendor/codeception/codeception/src/Codeception/Exception/Deprecation.phpg       g   >F      C   vendor/codeception/codeception/src/Codeception/Exception/Notice.phpb       b   d      S   vendor/codeception/codeception/src/Codeception/Exception/ModuleRequireException.php              S   vendor/codeception/codeception/src/Codeception/Exception/ConfigurationException.php          uw      T   vendor/codeception/codeception/src/Codeception/Exception/ModuleConflictException.php        a      R   vendor/codeception/codeception/src/Codeception/Exception/ModuleConfigException.php        Kq      D   vendor/codeception/codeception/src/Codeception/Exception/Warning.phpc       c   `ä      W   vendor/codeception/codeception/src/Codeception/Exception/ConditionalAssertionFailed.php          XH      Q   vendor/codeception/codeception/src/Codeception/Exception/UselessTestException.php          '#0      L   vendor/codeception/codeception/src/Codeception/Exception/ModuleException.php        L?      7   vendor/codeception/codeception/src/PHPUnit/TestCase.php        &      D   vendor/codeception/codeception/src/PHPUnit/Wrapper/PhpUnit9/Test.php        (      E   vendor/codeception/codeception/src/PHPUnit/Wrapper/PhpUnit10/Test.php          :$      ,   vendor/codeception/codeception/functions.php        Jr      &   vendor/codeception/codeception/app.phpT      T  AC      0   vendor/codeception/codeception/ext/RunBefore.php        M      /   vendor/codeception/codeception/ext/Recorder.phpU      U  d0H      1   vendor/codeception/codeception/ext/RunProcess.php        [Ϭ"      0   vendor/codeception/codeception/ext/RunFailed.php
      
  ؇      2   vendor/codeception/codeception/ext/DotReporter.php        Fk:      5   vendor/codeception/codeception/ext/SimpleReporter.php        RA      -   vendor/codeception/codeception/ext/Logger.php        7?      +   vendor/codeception/codeception/autoload.php2      2        L   vendor/codeception/module-asserts/src/Codeception/Module/AbstractAsserts.php        K      D   vendor/codeception/module-asserts/src/Codeception/Module/Asserts.php        飤      H   vendor/codeception/module-webdriver/src/Codeception/Module/WebDriver.php              L   vendor/codeception/module-webdriver/src/Codeception/Constraint/WebDriver.phpV      V  ,٤      O   vendor/codeception/module-webdriver/src/Codeception/Constraint/WebDriverNot.php)      )  ~i      U   vendor/codeception/module-webdriver/src/Codeception/Exception/ConnectionException.php          , Y      J   vendor/codeception/module-filesystem/src/Codeception/Module/Filesystem.php%      %  uS[      $   vendor/codeception/stub/src/Stub.phpR      R  *|      1   vendor/codeception/stub/src/Test/Feature/Stub.php&      &   ~*      -   vendor/codeception/stub/src/Stub/Expected.php-      -  *Ф      3   vendor/codeception/stub/src/Stub/ConsecutiveMap.php        wN      2   vendor/codeception/stub/src/Stub/StubMarshaler.phpY      Y        $   vendor/codeception/stub/RoboFile.phpY      Y  ]A      ,   vendor/codeception/lib-xml/src/Util/Soap.php        xmj4      +   vendor/codeception/lib-xml/src/Util/Xml.php              2   vendor/codeception/lib-xml/src/Util/XmlBuilder.php        l      4   vendor/codeception/lib-xml/src/Util/XmlStructure.php        +      O   vendor/codeception/lib-asserts/src/Codeception/Util/Shared/InheritedAsserts.php1      1  ϖu      F   vendor/codeception/lib-asserts/src/Codeception/Util/Shared/Asserts.php$      $  g      <   vendor/codeception/module-apc/src/Codeception/Module/Apc.php^      ^  s?      D   vendor/codeception/module-mongodb/src/Codeception/Module/MongoDb.php9      9        H   vendor/codeception/module-mongodb/src/Codeception/Lib/Driver/MongoDb.php        miJ      @   vendor/codeception/module-redis/src/Codeception/Module/Redis.php/X      /X  O      >   vendor/codeception/module-soap/src/Codeception/Module/SOAP.php}:      }:  "_4      *   vendor/phpunit/php-invoker/src/Invoker.php@      @  h<ˤ      W   vendor/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php           䊤      7   vendor/phpunit/php-invoker/src/exceptions/Exception.php          󂔤      >   vendor/phpunit/php-invoker/src/exceptions/TimeoutException.php          0      0   vendor/phpunit/php-file-iterator/src/Factory.php        V      1   vendor/phpunit/php-file-iterator/src/Iterator.php              /   vendor/phpunit/php-file-iterator/src/Facade.phpN      N        &   vendor/phpunit/php-timer/src/Timer.php        ݀¤      7   vendor/phpunit/php-timer/src/ResourceUsageFormatter.phpa      a  " Z      X   vendor/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php                5   vendor/phpunit/php-timer/src/exceptions/Exception.php          w`\x      B   vendor/phpunit/php-timer/src/exceptions/NoActiveTimerException.php          Y _Y      )   vendor/phpunit/php-timer/src/Duration.php+      +  ̤      (   vendor/phpunit/phpunit/src/Exception.phpx       x   (
l      2   vendor/phpunit/phpunit/src/Runner/PhptTestCase.php?=      ?=  f      :   vendor/phpunit/phpunit/src/Runner/ResultCacheExtension.php	      	  ߤ      5   vendor/phpunit/phpunit/src/Runner/TestSuiteLoader.php          `      5   vendor/phpunit/phpunit/src/Runner/TestResultCache.php              =   vendor/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php	      	  L=      5   vendor/phpunit/phpunit/src/Runner/TestSuiteSorter.php        |ߤ      -   vendor/phpunit/phpunit/src/Runner/Version.php        Hv      /   vendor/phpunit/phpunit/src/Runner/Exception.php          	Um      @   vendor/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php        04      :   vendor/phpunit/phpunit/src/Runner/Extension/PharLoader.phpx      x  !{      <   vendor/phpunit/phpunit/src/Runner/DefaultTestResultCache.php	      	  4S}      B   vendor/phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php          l^      3   vendor/phpunit/phpunit/src/Runner/Hook/TestHook.phpo       o   Ƥ      >   vendor/phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php          {w      ?   vendor/phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.php          R      8   vendor/phpunit/phpunit/src/Runner/Hook/AfterTestHook.php          i      9   vendor/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php          %1      =   vendor/phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.php          Ivʤ      B   vendor/phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php          5FҤ      ?   vendor/phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.php          B      /   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          .      4   vendor/phpunit/phpunit/src/Runner/BaseTestRunner.php        8      9   vendor/phpunit/phpunit/src/Runner/NullTestResultCache.php              4   vendor/phpunit/phpunit/src/Runner/Filter/Factory.phph      h  ȅ~      G   vendor/phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.php        m      G   vendor/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php        Jߤ      ?   vendor/phpunit/phpunit/src/Runner/Filter/NameFilterIterator.php        )a      @   vendor/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php        5      8   vendor/phpunit/phpunit/src/Framework/WarningTestCase.phpU      U  $Q      7   vendor/phpunit/phpunit/src/Framework/IncompleteTest.php          _G      6   vendor/phpunit/phpunit/src/Framework/ErrorTestCase.phpM      M  \.      3   vendor/phpunit/phpunit/src/Framework/TestResult.phpQM      QM  ŉ      8   vendor/phpunit/phpunit/src/Framework/SkippedTestCase.php        >Dd      >   vendor/phpunit/phpunit/src/Framework/DataProviderTestSuite.phpW      W  Jr      1   vendor/phpunit/phpunit/src/Framework/TestCase.phpN      N  ,      ;   vendor/phpunit/phpunit/src/Framework/MockObject/Api/Api.php<      <  6w+      >   vendor/phpunit/phpunit/src/Framework/MockObject/Api/Method.phpp      p  @m      K   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.php        v K      K   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php        ؃      G   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.php        e      C   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.php        ǉ      H   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.php        Ĥ      C   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.phpL
      L
  J      N   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php-      -        E   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.php        E	Ǥ      H   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php        ΂      F   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.phpv      v  >xZ      L   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.php        s X      G   vendor/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php        r@      >   vendor/phpunit/phpunit/src/Framework/MockObject/MockMethod.php        ۤ      =   vendor/phpunit/phpunit/src/Framework/MockObject/Generator.phpZ      Z  w      8   vendor/phpunit/phpunit/src/Framework/MockObject/Stub.phpr      r  r*	      H   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.phpH      H  .okˤ      C   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php        䫣      =   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php        tv~      G   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php        Ǎ,b      I   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php        y쇤      B   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php]      ]  \=      G   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.phpU      U  8      G   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.php        e      C   vendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php%      %  ;缤      >   vendor/phpunit/phpunit/src/Framework/MockObject/Invocation.php              A   vendor/phpunit/phpunit/src/Framework/MockObject/MockMethodSet.phpB      B  +RY      L   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/proxied_method.tpl}      }  @ė      I   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/deprecation.tpl;       ;   O5s      Y   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_method_never_or_void.tpl        p      J   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_class.tpl          wZ      J   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/intersection.tplL       L   -X      R   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_static_method.tpl           4R      I   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/wsdl_method.tpl<       <   i      Z   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/proxied_method_never_or_void.tplv      v  T      H   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/wsdl_class.tpl                I   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/trait_class.tplQ       Q   <Ȥ      K   vendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_method.tplF      F  K      =   vendor/phpunit/phpunit/src/Framework/MockObject/MockTrait.php          +>      <   vendor/phpunit/phpunit/src/Framework/MockObject/MockType.php          "      H   vendor/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php]      ]  j      ?   vendor/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php        L|E      =   vendor/phpunit/phpunit/src/Framework/MockObject/MockClass.php        0(դ      >   vendor/phpunit/phpunit/src/Framework/MockObject/Verifiable.php          9      >   vendor/phpunit/phpunit/src/Framework/MockObject/MockObject.php        dU      [   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php        n      Q   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php          ?%      X   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php[      [  Ϥ      S   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.phpJ      J  Bu      ^   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.phpG      G  ͘      `   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php@      @  {XVZ      V   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsReadonlyException.phpk      k  ].B      S   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.phpe      e  N      [   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.phpa      a  0      V   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php4      4  >6      S   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.phpJ      J  (      ^   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php              G   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php          CE      R   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.phpL      L  aC      Y   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.phpP      P  Q?      N   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php          Dͤ      _   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php        ^F      b   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php        d      T   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php          -W      h   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php'      '  .      Z   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php        S\      _   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php        J`      l   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.phpN      N  ^      l   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php          h      _   vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.phpY      Y  ?8̤      L   vendor/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php        X      @   vendor/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php          vl      K   vendor/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php          gG      K   vendor/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php        s      D   vendor/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php           j      M   vendor/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php        (&      F   vendor/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php        Y      ;   vendor/phpunit/phpunit/src/Framework/MockObject/Matcher.phpv      v  k      E   vendor/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php	      	  pUQ      G   vendor/phpunit/phpunit/src/Framework/InvalidParameterGroupException.php          eOq      4   vendor/phpunit/phpunit/src/Framework/Reorderable.php          w      >   vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php        9/n      S   vendor/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php        '^      >   vendor/phpunit/phpunit/src/Framework/Constraint/IsAnything.php        $|$      I   vendor/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.php        a2      N   vendor/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.php        <D      I   vendor/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsWritable.php              I   vendor/phpunit/phpunit/src/Framework/Constraint/Filesystem/FileExists.php              S   vendor/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContains.phpS      S  6      X   vendor/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsEqual.php        f      \   vendor/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsIdentical.php        8Y      W   vendor/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsOnly.php              K   vendor/phpunit/phpunit/src/Framework/Constraint/Traversable/ArrayHasKey.php        Q      K   vendor/phpunit/phpunit/src/Framework/Constraint/Cardinality/GreaterThan.php        Z5i      G   vendor/phpunit/phpunit/src/Framework/Constraint/Cardinality/IsEmpty.php        ٤      E   vendor/phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php        A5<      H   vendor/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php           d-      H   vendor/phpunit/phpunit/src/Framework/Constraint/Cardinality/LessThan.php              <   vendor/phpunit/phpunit/src/Framework/Constraint/Callback.php        .2      D   vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php        x      P   vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php        d      R   vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php        zC2p      M   vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php        sӤ      L   vendor/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php        Z      A   vendor/phpunit/phpunit/src/Framework/Constraint/String/IsJson.php        |b!      Y   vendor/phpunit/phpunit/src/Framework/Constraint/String/StringMatchesFormatDescription.php]      ]  q      I   vendor/phpunit/phpunit/src/Framework/Constraint/String/StringEndsWith.php        .      I   vendor/phpunit/phpunit/src/Framework/Constraint/String/StringContains.phpW      W  +      K   vendor/phpunit/phpunit/src/Framework/Constraint/String/StringStartsWith.phpE      E  
      ?   vendor/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php        [      ?   vendor/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php        Hh?,      G   vendor/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php        ,V      L   vendor/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasProperty.php              L   vendor/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasAttribute.php        Fr      R   vendor/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasStaticAttribute.php        d      M   vendor/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php(      (        C   vendor/phpunit/phpunit/src/Framework/Constraint/Boolean/IsFalse.php        [      B   vendor/phpunit/phpunit/src/Framework/Constraint/Boolean/IsTrue.php        $o      ?   vendor/phpunit/phpunit/src/Framework/Constraint/Type/IsType.php	      	  =      ?   vendor/phpunit/phpunit/src/Framework/Constraint/Type/IsNull.php        Sʤ      E   vendor/phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php        s      _   vendor/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php        :      G   vendor/phpunit/phpunit/src/Framework/Constraint/Exception/Exception.php        "      K   vendor/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionCode.php        0      N   vendor/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessage.php              >   vendor/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php        WTŤ      A   vendor/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php$      $  ȺX      C   vendor/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php,      ,  )%)      G   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php        9~)      E   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php}      }  4`      G   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.php        Ok      K   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.php        Qq      J   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.php        p      F   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php        :xz      G   vendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php        -q      7   vendor/phpunit/phpunit/src/Framework/SelfDescribing.php          ú]      /   vendor/phpunit/phpunit/src/Framework/Assert.php        #      9   vendor/phpunit/phpunit/src/Framework/ExceptionWrapper.php        J9      5   vendor/phpunit/phpunit/src/Framework/TestListener.php          2hۤ      A   vendor/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php        Z      J   vendor/phpunit/phpunit/src/Framework/TestListenerDefaultImplementation.phpJ      J   ۤ      :   vendor/phpunit/phpunit/src/Framework/TestSuiteIterator.php-      -  (      ;   vendor/phpunit/phpunit/src/Framework/IncompleteTestCase.php        p&      4   vendor/phpunit/phpunit/src/Framework/TestFailure.php        .>      4   vendor/phpunit/phpunit/src/Framework/SkippedTest.php          (      9   vendor/phpunit/phpunit/src/Framework/Assert/Functions.php        @p      4   vendor/phpunit/phpunit/src/Framework/TestBuilder.php        r1      4   vendor/phpunit/phpunit/src/Framework/Error/Error.php`      `  L[6      9   vendor/phpunit/phpunit/src/Framework/Error/Deprecated.phpw       w   P      5   vendor/phpunit/phpunit/src/Framework/Error/Notice.phps       s   a7W      6   vendor/phpunit/phpunit/src/Framework/Error/Warning.phpt       t   cP      8   vendor/phpunit/phpunit/src/Framework/Exception/Error.php          T      O   vendor/phpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.php          Ś	      A   vendor/phpunit/phpunit/src/Framework/Exception/SyntheticError.php        ?V      m   vendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php        ?p;      R   vendor/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php          ؤ      S   vendor/phpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php          [Q      M   vendor/phpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php4      4  ~KeC      h   vendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php              <   vendor/phpunit/phpunit/src/Framework/Exception/Exception.php        ð      A   vendor/phpunit/phpunit/src/Framework/Exception/RiskyTestError.php~       ~   V      G   vendor/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php                f   vendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php        N      >   vendor/phpunit/phpunit/src/Framework/Exception/OutputError.php          )ä      H   vendor/phpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.php          XeƲ      K   vendor/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php        mf      O   vendor/phpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.php          T      K   vendor/phpunit/phpunit/src/Framework/Exception/InvalidArgumentException.php        yդ      L   vendor/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php                :   vendor/phpunit/phpunit/src/Framework/Exception/Warning.php          7Uޤ      R   vendor/phpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php          jOƤ      T   vendor/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.phpT      T  ,Y      H   vendor/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.phpz       z   v      g   vendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php        D      F   vendor/phpunit/phpunit/src/Framework/Exception/IncompleteTestError.php          ߀铤      C   vendor/phpunit/phpunit/src/Framework/Exception/SkippedTestError.php          ,R76      H   vendor/phpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php          N^      X   vendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php        
      2   vendor/phpunit/phpunit/src/Framework/TestSuite.php7      7  |9      -   vendor/phpunit/phpunit/src/Framework/Test.php          p      )   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.tpl8      8  h/      >   vendor/phpunit/phpunit/src/Util/PHP/Template/TestCaseClass.tplP      P  Npd      9   vendor/phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php        {.      :   vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php              9   vendor/phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.php        *E[      /   vendor/phpunit/phpunit/src/Util/GlobalState.php~      ~  \ܤ      5   vendor/phpunit/phpunit/src/Util/RegularExpression.phpQ      Q  ?4      7   vendor/phpunit/phpunit/src/Util/XmlTestListRenderer.php[      [  VH      7   vendor/phpunit/phpunit/src/Util/Annotation/DocBlock.php+      +  Q      7   vendor/phpunit/phpunit/src/Util/Annotation/Registry.php?      ?  z       +   vendor/phpunit/phpunit/src/Util/Printer.php        $4u      -   vendor/phpunit/phpunit/src/Util/Log/JUnit.php              0   vendor/phpunit/phpunit/src/Util/Log/TeamCity.php        3      8   vendor/phpunit/phpunit/src/Util/TextTestListRenderer.php              =   vendor/phpunit/phpunit/src/Util/TestDox/TextResultPrinter.phpP      P  GQ      <   vendor/phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.phpg      g  sۆ      :   vendor/phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.phpN      N  (zD      =   vendor/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php        Zɤ      :   vendor/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php        VT      9   vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter.phpl      l  e;R      =   vendor/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php	      	  h      6   vendor/phpunit/phpunit/src/Util/Xml/SchemaDetector.php.      .  ֨0      8   vendor/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.phpS      S  ~iH      1   vendor/phpunit/phpunit/src/Util/Xml/Exception.php          ]      1   vendor/phpunit/phpunit/src/Util/Xml/Validator.phpp      p        4   vendor/phpunit/phpunit/src/Util/Xml/SchemaFinder.php        ̢      8   vendor/phpunit/phpunit/src/Util/Xml/ValidationResult.php        n{W      =   vendor/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php        ?wf      G   vendor/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.phps      s  )tq      .   vendor/phpunit/phpunit/src/Util/Xml/Loader.php        "3      C   vendor/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.php          CZ      -   vendor/phpunit/phpunit/src/Util/Exception.php          m/r      (   vendor/phpunit/phpunit/src/Util/Json.php        LҤ      0   vendor/phpunit/phpunit/src/Util/ErrorHandler.php0
      0
  [	      .   vendor/phpunit/phpunit/src/Util/Reflection.phpy      y  1H      =   vendor/phpunit/phpunit/src/Util/VersionComparisonOperator.php        <      *   vendor/phpunit/phpunit/src/Util/Filter.php        y      ;   vendor/phpunit/phpunit/src/Util/InvalidDataSetException.php          qշ      ?   vendor/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php        ,Ӥ      .   vendor/phpunit/phpunit/src/Util/Filesystem.php        H      /   vendor/phpunit/phpunit/src/Util/ExcludeList.php        !      *   vendor/phpunit/phpunit/src/Util/Cloner.php          gœ]      '   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.php        L?      (   vendor/phpunit/phpunit/src/Util/Test.php?      ?  >;      0   vendor/phpunit/phpunit/src/TextUI/TestRunner.php               *   vendor/phpunit/phpunit/src/TextUI/Help.phpU*      U*  ښQ      >   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php        5$      U   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php        -&      E   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php
      
  hP      C   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.phpd      d  J      O   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.php        פ      U   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php        (	I      E   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.phpu      u  ؇      M   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php        ]      M   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.php        Qä      W   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php)      )  +	      C   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.php        e      I   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.phpl      l  @Ҥ      Z   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php          (      S   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php          (      _   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php        n      d   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.phpr      r  3M꼤      k   vendor/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.php        7e      b   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php        m7      b   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php        ߗ      l   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php        +}      w   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.phpb      b  U      ]   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php        gd      [   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php        ?      _   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php        |)$      f   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.phpY      Y  /      `   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php        f      Z   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php        Fn"      d   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php        qK      k   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php        VԤ      U   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.php          S      Q   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php        a7      @   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.phpZ      Z  B      K   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php          [      X   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php        \      P   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php         `      U   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php        "      F   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.php          ME
Ҥ      ]   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php&      &  ʳu:      P   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.phpQ      Q  $g=      P   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php              N   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.phpA      A  I%j      T   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.phpG      G        O   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php        U      Q   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.phpD      D  `      Q   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php        ,)i      N   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.phpP      P  W٤      O   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php        !a      T   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php|      |        ^   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php        ʐ      f   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php:      :  $      D   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php7      7  J3      K   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php>      >  [      J   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php=      =  r"      K   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php>      >  d4      C   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php6      6  7C3      G   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php:      :  V̤      F   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.php        F      N   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.php        M      I   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php        5       `   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php2      2  j4ɤ      T   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php        Ai      S   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php        L=      \   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php          o      X   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php        /'      [   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php        5      J   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.php        r      L   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.php        F^-F      B   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php          ]      C   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.phpI      I  9Τ      T   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php        w`      @   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php          n      R   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php=      =  f      Z   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php          D      F   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php-      -  :nݤ      H   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.php              D   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.php        X      =   vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.phpr      r  ؉W7      3   vendor/phpunit/phpunit/src/TextUI/ResultPrinter.php"      "  f<      <   vendor/phpunit/phpunit/src/TextUI/CliArguments/Exception.php          K      @   vendor/phpunit/phpunit/src/TextUI/CliArguments/Configuration.phpr      r  6O      9   vendor/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php!      !  A      :   vendor/phpunit/phpunit/src/TextUI/CliArguments/Builder.php5      5  Ɛ      :   vendor/phpunit/phpunit/src/TextUI/DefaultResultPrinter.phpj)      j)        -   vendor/phpunit/phpunit/src/TextUI/Command.phpO      O  [      5   vendor/phpunit/phpunit/src/TextUI/TestSuiteMapper.php        }<(      C   vendor/phpunit/phpunit/src/TextUI/Exception/ReflectionException.php          o      N   vendor/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.phpI      I  h	      9   vendor/phpunit/phpunit/src/TextUI/Exception/Exception.php          K      @   vendor/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php          1e      I   vendor/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php?      ?  5,      =   vendor/phpunit/php-text-template/src/exceptions/Exception.php          ?      D   vendor/phpunit/php-text-template/src/exceptions/RuntimeException.php          o      L   vendor/phpunit/php-text-template/src/exceptions/InvalidArgumentException.php          j      1   vendor/phpunit/php-text-template/src/Template.php        +      5   vendor/phpunit/php-code-coverage/src/CodeCoverage.php/      /  j      B   vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php        YQT      0   vendor/phpunit/php-code-coverage/src/Version.phpw      w  h      <   vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.phpz      z  -      =   vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php        ON      :   vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php        >b      =   vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.phpp	      p	  ệ      8   vendor/phpunit/php-code-coverage/src/Driver/Selector.phpI      I  *ڤ      <   vendor/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php        kw      6   vendor/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.dist        D      ]   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item_branch.html.distl      l  -      R   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/nv.d3.min.jsR     R <Ms      V   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/bootstrap.min.jsc      c  "#      S   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/popper.min.jsR      R  Qd      O   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/d3.min.jsP     P hb      S   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/jquery.min.js@^     @^        M   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/file.js        b䆤      Q   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/line.html.dist          {      R   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/paths.html.dist        *'ݤ      U   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/branches.html.dist        h2+      Q   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file.html.distP	      P	  j*      S   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/octicons.cssX       X   '#      T   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/nv.d3.min.cssX%      X%  0,      X   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/bootstrap.min.cssy     y Ĥ      P   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css
      
        Q   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/custom.css                      X   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_branch.html.dist	      	  ㉞      V   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item.html.distr      r  /y      ]   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_branch.html.dist        n2]      X   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item.html.dist        jפ      `   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/coverage_bar_branch.html.dist'      '  O}      _   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item_branch.html.dist        yĎk      R   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/lines.html.diste       e   df      [   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_item.html.distA      A  ds      Y   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/coverage_bar.html.dist'      '  O}      b   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_item_branch.html.dist;      ;  mۤ      V   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard.html.dist        D      V   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory.html.dist        Ն      [   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/icons/file-directory.svg          Z      V   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/icons/file-code.svg0      0  QUU      G   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.phpz      z  N      G   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php        ƙ      B   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.phpn      n  ĊT      3   vendor/phpunit/php-code-coverage/src/Report/PHP.php        U.      =   vendor/phpunit/php-code-coverage/src/Report/Xml/Directory.php                9   vendor/phpunit/php-code-coverage/src/Report/Xml/Tests.php:      :  8?      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Totals.php        
      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Source.php        uۤ      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Facade.php        +s      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Report.php        u      ;   vendor/phpunit/php-code-coverage/src/Report/Xml/Project.php,      ,  3d      8   vendor/phpunit/php-code-coverage/src/Report/Xml/Unit.phpk      k  O      D   vendor/phpunit/php-code-coverage/src/Report/Xml/BuildInformation.php<      <  "Ȥ      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Method.php%      %  <      8   vendor/phpunit/php-code-coverage/src/Report/Xml/File.php        @      <   vendor/phpunit/php-code-coverage/src/Report/Xml/Coverage.phpq      q  '"      8   vendor/phpunit/php-code-coverage/src/Report/Xml/Node.php        { hH      9   vendor/phpunit/php-code-coverage/src/Report/Cobertura.php&      &  LA      4   vendor/phpunit/php-code-coverage/src/Report/Text.php              6   vendor/phpunit/php-code-coverage/src/Report/Clover.php        ƨ      6   vendor/phpunit/php-code-coverage/src/Report/Crap4j.php        P=ʓ      /   vendor/phpunit/php-code-coverage/src/Filter.php        t      K   vendor/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.phpO      O  ڕˤ      U   vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php        sDx      N   vendor/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.phpi      i  Q'      R   vendor/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.phpR      R  l      K   vendor/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.phpj      j  E9      C   vendor/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php<      <  ڍ      D   vendor/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php        Ùl      8   vendor/phpunit/php-code-coverage/src/Util/Percentage.php$      $   NՐ      8   vendor/phpunit/php-code-coverage/src/Util/Filesystem.php        G      7   vendor/phpunit/php-code-coverage/src/Node/Directory.php        x鬪      6   vendor/phpunit/php-code-coverage/src/Node/Iterator.phpo      o  v      :   vendor/phpunit/php-code-coverage/src/Node/AbstractNode.phpg      g  mڤ      7   vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php        
ձ      2   vendor/phpunit/php-code-coverage/src/Node/File.php+9      +9        5   vendor/phpunit/php-code-coverage/src/Node/Builder.php        (      F   vendor/phpunit/php-code-coverage/src/Exception/ReflectionException.php          1      R   vendor/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php5      5  dMz      p   vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php\      \  Z      M   vendor/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.phpl      l  ]ݤ      \   vendor/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php          V      Y   vendor/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php          p      V   vendor/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php          .      Y   vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php*      *  xˠ      <   vendor/phpunit/php-code-coverage/src/Exception/Exception.php          ܼ!      L   vendor/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.phpT      T  -m      B   vendor/phpunit/php-code-coverage/src/Exception/ParserException.php          T      I   vendor/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php        Jh      N   vendor/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.phpX      X  SS      K   vendor/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php                W   vendor/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php        S      V   vendor/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php        [      ]   vendor/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php          &      N   vendor/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.phpS      S  Vt0      N   vendor/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php          l      P   vendor/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php{      {  P      M   vendor/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.phpY      Y  P      ?   vendor/phpunit/php-code-coverage/src/Exception/XmlException.php          ,Ϥ      4   vendor/ralouphie/getallheaders/src/getallheaders.phpO      O  }r      2   vendor/symfony/polyfill-intl-grapheme/Grapheme.php              3   vendor/symfony/polyfill-intl-grapheme/bootstrap.php        9|      5   vendor/symfony/polyfill-intl-grapheme/bootstrap80.phpY	      Y	  !      0   vendor/symfony/event-dispatcher/GenericEvent.php        ǔ      3   vendor/symfony/event-dispatcher/EventDispatcher.php        7      K   vendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php              M   vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php        ڡK?      <   vendor/symfony/event-dispatcher/EventSubscriberInterface.php          !jcK      <   vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php        <      <   vendor/symfony/event-dispatcher/EventDispatcherInterface.php        Tp<      9   vendor/symfony/event-dispatcher/Debug/WrappedListener.php               B   vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php        A      =   vendor/symfony/event-dispatcher/Attribute/AsEventListener.phpd      d        1   vendor/symfony/deprecation-contracts/function.phpC      C  '      F   vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php.      .  Qs$      T   vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.phpa      a  R}      L   vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.phpt.      t.  qܤ      X   vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php        oe)      R   vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php=      =  *o?      5   vendor/symfony/polyfill-intl-normalizer/bootstrap.php        PD      6   vendor/symfony/polyfill-intl-normalizer/Normalizer.php        ie      7   vendor/symfony/polyfill-intl-normalizer/bootstrap80.php        =r      ?   vendor/symfony/browser-kit/Test/Constraint/BrowserHasCookie.php        6q얤      E   vendor/symfony/browser-kit/Test/Constraint/BrowserCookieValueSame.php        \>A      .   vendor/symfony/browser-kit/AbstractBrowser.php{3      {3  :      *   vendor/symfony/browser-kit/HttpBrowser.phpL      L  z      &   vendor/symfony/browser-kit/Request.phph      h  8?      &   vendor/symfony/browser-kit/History.php        /9+'      '   vendor/symfony/browser-kit/Response.php        7e`      %   vendor/symfony/browser-kit/Cookie.phpz      z  9\ؤ      (   vendor/symfony/browser-kit/CookieJar.php        (      ?   vendor/symfony/browser-kit/Exception/BadMethodCallException.php          Pa      F   vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php2      2  sv      3   vendor/symfony/event-dispatcher-contracts/Event.php        t      :   vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php          ڔݤ      <   vendor/symfony/polyfill-php80/Resources/stubs/Stringable.phpk       k   +      ;   vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php              E   vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php]       ]   g      <   vendor/symfony/polyfill-php80/Resources/stubs/ValueError.phpT       T   w      '   vendor/symfony/polyfill-php80/Php80.php	      	  [k      +   vendor/symfony/polyfill-php80/bootstrap.php              *   vendor/symfony/polyfill-php80/PhpToken.php        M I      @   vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php8[      8[  +R*      F   vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php        y_      B   vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php              @   vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpT      T  +      .   vendor/symfony/polyfill-mbstring/bootstrap.php        'w@0      0   vendor/symfony/polyfill-mbstring/bootstrap80.php!      !  .Ѥ      -   vendor/symfony/polyfill-mbstring/Mbstring.phpS      S  3 f      4   vendor/symfony/css-selector/CssSelectorConverter.php        )\      9   vendor/symfony/css-selector/XPath/TranslatorInterface.phpq      q  t_6      0   vendor/symfony/css-selector/XPath/Translator.php        群      =   vendor/symfony/css-selector/XPath/Extension/NodeExtension.php8      8  k܃      D   vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php        6$n      =   vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php              A   vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php        >:      D   vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php        zä      B   vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php        ZƼ      J   vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.phpb
      b
  }      A   vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php6      6  4\      /   vendor/symfony/css-selector/XPath/XPathExpr.php        Ȍ      A   vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php        N      =   vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php        %+      :   vendor/symfony/css-selector/Parser/Shortcut/HashParser.phps      s  4a      ;   vendor/symfony/css-selector/Parser/Shortcut/ClassParser.phpw      w  )      -   vendor/symfony/css-selector/Parser/Parser.php@      @  յ      6   vendor/symfony/css-selector/Parser/ParserInterface.php          {偤      B   vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php        fH`/      B   vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php        ,      :   vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php        _y      2   vendor/symfony/css-selector/Parser/TokenStream.phpP      P  :      ,   vendor/symfony/css-selector/Parser/Token.php        )d      -   vendor/symfony/css-selector/Parser/Reader.php{      {  wM      @   vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.phpU      U  ۋ      =   vendor/symfony/css-selector/Parser/Handler/CommentHandler.php        Ճ      @   vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php        o      ?   vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php        c n      <   vendor/symfony/css-selector/Parser/Handler/NumberHandler.php        ].R      <   vendor/symfony/css-selector/Parser/Handler/StringHandler.phpa      a  R9      :   vendor/symfony/css-selector/Parser/Handler/HashHandler.php        :P       2   vendor/symfony/css-selector/Node/NodeInterface.php          o"F      -   vendor/symfony/css-selector/Node/HashNode.php        $3ˤ      1   vendor/symfony/css-selector/Node/AbstractNode.php          ݰ̤      9   vendor/symfony/css-selector/Node/CombinedSelectorNode.php        q      0   vendor/symfony/css-selector/Node/ElementNode.php        _Ҥ      .   vendor/symfony/css-selector/Node/ClassNode.php        KT      0   vendor/symfony/css-selector/Node/Specificity.php        g_      1   vendor/symfony/css-selector/Node/FunctionNode.php        8j      1   vendor/symfony/css-selector/Node/NegationNode.php        ғ       1   vendor/symfony/css-selector/Node/SelectorNode.php        .      /   vendor/symfony/css-selector/Node/PseudoNode.php        /]K      2   vendor/symfony/css-selector/Node/AttributeNode.phpA      A  Θ      >   vendor/symfony/css-selector/Exception/SyntaxErrorException.php        ]d0      8   vendor/symfony/css-selector/Exception/ParseException.php          /X      <   vendor/symfony/css-selector/Exception/ExceptionInterface.php          B|      B   vendor/symfony/css-selector/Exception/ExpressionErrorException.php          6;      @   vendor/symfony/css-selector/Exception/InternalErrorException.php          &O      &   vendor/symfony/process/InputStream.php'      '  ,      '   vendor/symfony/process/ProcessUtils.php        I      .   vendor/symfony/process/Pipes/AbstractPipes.php*      *  &df      -   vendor/symfony/process/Pipes/WindowsPipes.php2      2  zژ      *   vendor/symfony/process/Pipes/UnixPipes.php        Y<      /   vendor/symfony/process/Pipes/PipesInterface.php        fQ      .   vendor/symfony/process/PhpExecutableFinder.php        m7      %   vendor/symfony/process/PhpProcess.php          }0      +   vendor/symfony/process/ExecutableFinder.php        &i      "   vendor/symfony/process/Process.phpf      f  tߤ      =   vendor/symfony/process/Exception/ProcessSignaledException.php        Yש      3   vendor/symfony/process/Exception/LogicException.php                 7   vendor/symfony/process/Exception/ExceptionInterface.phpy       y   qVXJ      ;   vendor/symfony/process/Exception/ProcessFailedException.phpx      x  zy      5   vendor/symfony/process/Exception/RuntimeException.php          :      =   vendor/symfony/process/Exception/InvalidArgumentException.php          +_      =   vendor/symfony/process/Exception/ProcessTimedOutException.php1      1  'Z         vendor/symfony/finder/Glob.php        ˤ      3   vendor/symfony/finder/Comparator/DateComparator.php        %e      /   vendor/symfony/finder/Comparator/Comparator.phpd      d  uE      5   vendor/symfony/finder/Comparator/NumberComparator.phpz      z  + 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.php        F      ;   vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.phpw
      w
  ŵ3      A   vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php        #Cߤ      9   vendor/symfony/finder/Iterator/FileTypeFilterIterator.phpw      w  7m      3   vendor/symfony/finder/Iterator/SortableIterator.phpY
      Y
  By      7   vendor/symfony/finder/Iterator/CustomFilterIterator.phpT      T  Q      =   vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php	      	  o      <   vendor/symfony/finder/Iterator/FilecontentFilterIterator.phpH      H  Ҥ      /   vendor/symfony/finder/Iterator/LazyIterator.php        /yJ      :   vendor/symfony/finder/Iterator/SizeRangeFilterIterator.phpm      m  =      9   vendor/symfony/finder/Iterator/FilenameFilterIterator.php        )Nޤ      5   vendor/symfony/finder/Iterator/PathFilterIterator.php        5$S      #   vendor/symfony/finder/Gitignore.php        s	4      9   vendor/symfony/finder/Exception/AccessDeniedException.php          s      >   vendor/symfony/finder/Exception/DirectoryNotFoundException.php          a)      %   vendor/symfony/finder/SplFileInfo.php        vҘ:      #   vendor/symfony/console/Terminal.php
      
  o,w          vendor/symfony/console/Color.php|      |  \ք      8   vendor/symfony/console/SignalRegistry/SignalRegistry.php'      '  $Y      3   vendor/symfony/console/SingleCommandApplication.phpJ      J  #Y      D   vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php        ~      ,   vendor/symfony/console/Question/Question.php'      '  t8      2   vendor/symfony/console/Question/ChoiceQuestion.php#      #  eˤ      8   vendor/symfony/console/Question/ConfirmationQuestion.php	      	  5      (   vendor/symfony/console/Helper/Helper.phpJ
      J
  	5      +   vendor/symfony/console/Helper/TableCell.phpZ      Z  tSФ      ,   vendor/symfony/console/Helper/TableStyle.php        Nݚe      6   vendor/symfony/console/Helper/DebugFormatterHelper.php        ؞      2   vendor/symfony/console/Helper/DescriptorHelper.php        5      +   vendor/symfony/console/Helper/HelperSet.php        ̠{      3   vendor/symfony/console/Helper/ProgressIndicator.php        aP      1   vendor/symfony/console/Helper/HelperInterface.php          IC3      -   vendor/symfony/console/Helper/ProgressBar.php/      /  ?      '   vendor/symfony/console/Helper/Table.phpJ      J        0   vendor/symfony/console/Helper/TableCellStyle.php        ˩l      1   vendor/symfony/console/Helper/FormatterHelper.php        j      7   vendor/symfony/console/Helper/SymfonyQuestionHelper.php	      	  d      (   vendor/symfony/console/Helper/Dumper.php!      !  R      +   vendor/symfony/console/Helper/TableRows.php2      2  ?Ӥ      0   vendor/symfony/console/Helper/QuestionHelper.php(.      (.  92      /   vendor/symfony/console/Helper/ProcessHelper.php        sQ      2   vendor/symfony/console/Helper/InputAwareHelper.phpc      c        0   vendor/symfony/console/Helper/TableSeparator.php                ?   vendor/symfony/console/CommandLoader/ContainerCommandLoader.php        
ە      =   vendor/symfony/console/CommandLoader/FactoryCommandLoader.php        2Yդ      ?   vendor/symfony/console/CommandLoader/CommandLoaderInterface.phpc      c  $      >   vendor/symfony/console/Formatter/OutputFormatterStyleStack.php        ?      =   vendor/symfony/console/Formatter/OutputFormatterInterface.php        J4      =   vendor/symfony/console/Formatter/NullOutputFormatterStyle.php
      
  %h      B   vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php        +      F   vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php          Z      8   vendor/symfony/console/Formatter/NullOutputFormatter.php        !&u      4   vendor/symfony/console/Formatter/OutputFormatter.php        L"      9   vendor/symfony/console/Formatter/OutputFormatterStyle.phpK      K  K      2   vendor/symfony/console/Event/ConsoleErrorEvent.php        V      6   vendor/symfony/console/Event/ConsoleTerminateEvent.php        Jj      3   vendor/symfony/console/Event/ConsoleSignalEvent.phpK      K        -   vendor/symfony/console/Event/ConsoleEvent.php        ]ܩ      4   vendor/symfony/console/Event/ConsoleCommandEvent.php        "Xx2      6   vendor/symfony/console/EventListener/ErrorListener.php'      '  EФ      (   vendor/symfony/console/ConsoleEvents.php        gw]      &   vendor/symfony/console/Application.phpq      q  0      9   vendor/symfony/console/Descriptor/DescriptorInterface.php          @      0   vendor/symfony/console/Descriptor/Descriptor.php}      }  3^      4   vendor/symfony/console/Descriptor/TextDescriptor.php"      "  Z       3   vendor/symfony/console/Descriptor/XmlDescriptor.php              8   vendor/symfony/console/Descriptor/MarkdownDescriptor.phpQ      Q  tv      4   vendor/symfony/console/Descriptor/JsonDescriptor.php        1%菤      <   vendor/symfony/console/Descriptor/ApplicationDescription.php	      	  w\      0   vendor/symfony/console/Command/LockableTrait.php        ]t      2   vendor/symfony/console/Command/CompleteCommand.php              .   vendor/symfony/console/Command/ListCommand.php6	      6	  G      .   vendor/symfony/console/Command/LazyCommand.php        JYդ      8   vendor/symfony/console/Command/DumpCompletionCommand.phpS      S  V2i      .   vendor/symfony/console/Command/HelpCommand.php        ׆)      *   vendor/symfony/console/Command/Command.php(      (  z      =   vendor/symfony/console/Command/SignalableCommandInterface.php          B`      !   vendor/symfony/console/Cursor.php	      	  Q      .   vendor/symfony/console/Attribute/AsCommand.php        3      /   vendor/symfony/console/Tester/CommandTester.php        t1      9   vendor/symfony/console/Tester/CommandCompletionTester.php        ʛ;      3   vendor/symfony/console/Tester/ApplicationTester.php        g+r      @   vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php        ƃ      -   vendor/symfony/console/Tester/TesterTrait.php        w/      /   vendor/symfony/console/Style/StyleInterface.phpf      f  3       ,   vendor/symfony/console/Style/OutputStyle.php        S      -   vendor/symfony/console/Style/SymfonyStyle.php'      '  *      :   vendor/symfony/console/Exception/MissingInputException.php          S
      3   vendor/symfony/console/Exception/LogicException.php          O\e      ;   vendor/symfony/console/Exception/InvalidOptionException.php          H      =   vendor/symfony/console/Exception/CommandNotFoundException.php               7   vendor/symfony/console/Exception/ExceptionInterface.phpy       y   9[&      ?   vendor/symfony/console/Exception/NamespaceNotFoundException.php          n      5   vendor/symfony/console/Exception/RuntimeException.php          ,6      =   vendor/symfony/console/Exception/InvalidArgumentException.php          ̽Z      0   vendor/symfony/console/Completion/Suggestion.php:      :  زŬ      5   vendor/symfony/console/Completion/CompletionInput.php        $k      ;   vendor/symfony/console/Completion/CompletionSuggestions.php        ##      A   vendor/symfony/console/Completion/Output/BashCompletionOutput.phpg      g  W      F   vendor/symfony/console/Completion/Output/CompletionOutputInterface.phpF      F  2nM      /   vendor/symfony/console/Logger/ConsoleLogger.php        .      0   vendor/symfony/console/Input/InputDefinition.php        L      /   vendor/symfony/console/Input/InputInterface.phpd      d  j:      ,   vendor/symfony/console/Input/InputOption.php        U      .   vendor/symfony/console/Input/InputArgument.php        c6      ,   vendor/symfony/console/Input/StringInput.php?      ?  ˤ      9   vendor/symfony/console/Input/StreamableInputInterface.php          B      &   vendor/symfony/console/Input/Input.php        6      4   vendor/symfony/console/Input/InputAwareInterface.php          O      +   vendor/symfony/console/Input/ArrayInput.php~      ~  Y      *   vendor/symfony/console/Input/ArgvInput.php        =      2   vendor/symfony/console/CI/GithubActionReporter.php        5      1   vendor/symfony/console/Output/OutputInterface.php:      :  5p%      8   vendor/symfony/console/Output/ConsoleOutputInterface.php!      !  C5j      .   vendor/symfony/console/Output/StreamOutput.php        _'{      (   vendor/symfony/console/Output/Output.php(
      (
  <lۤ      6   vendor/symfony/console/Output/ConsoleSectionOutput.php	      	  gd      5   vendor/symfony/console/Output/TrimmedBufferOutput.php        BȞ      0   vendor/symfony/console/Output/BufferedOutput.phpy      y  ,7X      ,   vendor/symfony/console/Output/NullOutput.phpn      n  YƧ      /   vendor/symfony/console/Output/ConsoleOutput.php	      	  .      -   vendor/symfony/string/Resources/functions.php        cڪ"      <   vendor/symfony/string/Resources/data/wcswidth_table_zero.php         k      <   vendor/symfony/string/Resources/data/wcswidth_table_wide.php        -A      4   vendor/symfony/string/Inflector/EnglishInflector.php        3d      3   vendor/symfony/string/Inflector/FrenchInflector.php
      
  H      6   vendor/symfony/string/Inflector/InflectorInterface.php          hD      2   vendor/symfony/string/Slugger/SluggerInterface.php        Q      .   vendor/symfony/string/Slugger/AsciiSlugger.php7      7  +?      )   vendor/symfony/string/CodePointString.php        ڸ      $   vendor/symfony/string/LazyString.php        rc      $   vendor/symfony/string/ByteString.php*      *  ,5      (   vendor/symfony/string/AbstractString.php74      74  %      '   vendor/symfony/string/UnicodeString.php,$      ,$  !      /   vendor/symfony/string/AbstractUnicodeString.phpQ      Q  fiޤ      6   vendor/symfony/string/Exception/ExceptionInterface.phps       s   qHV      4   vendor/symfony/string/Exception/RuntimeException.php          ug      <   vendor/symfony/string/Exception/InvalidArgumentException.php          A      @   vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php        h      F   vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php                +   vendor/symfony/polyfill-php81/bootstrap.php        \      '   vendor/symfony/polyfill-php81/Php81.php;      ;  d      8   vendor/symfony/var-dumper/Resources/js/htmlDescriptor.jsb      b  h|      6   vendor/symfony/var-dumper/Resources/functions/dump.php              :   vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css        Dva      /   vendor/symfony/var-dumper/Server/Connection.php        DYj      /   vendor/symfony/var-dumper/Server/DumpServer.phpI      I  FF      5   vendor/symfony/var-dumper/Test/VarDumperTestTrait.php        ]Ҩ      +   vendor/symfony/var-dumper/Caster/Caster.php5      5  t:      -   vendor/symfony/var-dumper/Caster/EnumStub.php'      '  N      4   vendor/symfony/var-dumper/Caster/MemcachedCaster.php        W0      1   vendor/symfony/var-dumper/Caster/MysqliCaster.phpL      L  P      4   vendor/symfony/var-dumper/Caster/ExceptionCaster.phpf.      f.  G      .   vendor/symfony/var-dumper/Caster/GmpCaster.phpU      U  ``      -   vendor/symfony/var-dumper/Caster/DsCaster.php        "      3   vendor/symfony/var-dumper/Caster/DoctrineCaster.php0      0  =`      .   vendor/symfony/var-dumper/Caster/SplCaster.phpH      H  ޹      1   vendor/symfony/var-dumper/Caster/CutArrayStub.phpg      g  +|&J      0   vendor/symfony/var-dumper/Caster/FiberCaster.phpI      I  3&      /   vendor/symfony/var-dumper/Caster/StubCaster.php&      &  V+      .   vendor/symfony/var-dumper/Caster/TraceStub.php        q.      0   vendor/symfony/var-dumper/Caster/RedisCaster.php        /      .   vendor/symfony/var-dumper/Caster/FrameStub.phpH      H  h2=      .   vendor/symfony/var-dumper/Caster/ClassStub.phpt	      t	  (      /   vendor/symfony/var-dumper/Caster/UuidCaster.phpX      X  .פ      2   vendor/symfony/var-dumper/Caster/SymfonyCaster.php        H      3   vendor/symfony/var-dumper/Caster/ResourceCaster.phpv	      v	  Y      4   vendor/symfony/var-dumper/Caster/XmlReaderCaster.php        Ǵ*      /   vendor/symfony/var-dumper/Caster/DsPairStub.php5      5  w\      ,   vendor/symfony/var-dumper/Caster/CutStub.phpP      P  B,[      .   vendor/symfony/var-dumper/Caster/ConstStub.phpu      u  Y^      -   vendor/symfony/var-dumper/Caster/LinkStub.php        gZ      0   vendor/symfony/var-dumper/Caster/PgSqlCaster.php5      5  sa      /   vendor/symfony/var-dumper/Caster/AmqpCaster.php        !;D      7   vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php        D      .   vendor/symfony/var-dumper/Caster/PdoCaster.phpW	      W	  _踤      /   vendor/symfony/var-dumper/Caster/DateCaster.php        cpoI      ,   vendor/symfony/var-dumper/Caster/ImgStub.php=      =  ,I-F      2   vendor/symfony/var-dumper/Caster/ImagineCaster.php        v{Ф      /   vendor/symfony/var-dumper/Caster/IntlCaster.php        k7      .   vendor/symfony/var-dumper/Caster/DOMCaster.php+      +  _3      2   vendor/symfony/var-dumper/Caster/RdKafkaCaster.phpp      p  bפ      6   vendor/symfony/var-dumper/Caster/XmlResourceCaster.php        |-w      -   vendor/symfony/var-dumper/Caster/ArgsStub.php        sڤ      5   vendor/symfony/var-dumper/Caster/ReflectionCaster.php+      +        9   vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php        0Ua      3   vendor/symfony/var-dumper/Dumper/AbstractDumper.php        }      /   vendor/symfony/var-dumper/Dumper/HtmlDumper.phpw      w  783n      M   vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php                G   vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php        NX      K   vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php        z^ˤ      J   vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php<      <        1   vendor/symfony/var-dumper/Dumper/ServerDumper.php,      ,  F      8   vendor/symfony/var-dumper/Dumper/DataDumperInterface.php          F      .   vendor/symfony/var-dumper/Dumper/CliDumper.php3      3  P:      '   vendor/symfony/var-dumper/VarDumper.php        5      )   vendor/symfony/var-dumper/Cloner/Stub.php        ©${      .   vendor/symfony/var-dumper/Cloner/VarCloner.phps      s  CEaa      +   vendor/symfony/var-dumper/Cloner/Cursor.php        zC      4   vendor/symfony/var-dumper/Cloner/DumperInterface.php        =e      4   vendor/symfony/var-dumper/Cloner/ClonerInterface.php          A      )   vendor/symfony/var-dumper/Cloner/Data.php#      #  F      3   vendor/symfony/var-dumper/Cloner/AbstractCloner.php
B      
B  GD      >   vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php~      ~  9/	      ?   vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php                H   vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php<      <  ސ      7   vendor/symfony/var-dumper/Command/ServerDumpCommand.php        2Efb      ?   vendor/symfony/var-dumper/Exception/ThrowingCasterException.php        m-       +   vendor/symfony/polyfill-ctype/bootstrap.php2      2  a8      -   vendor/symfony/polyfill-ctype/bootstrap80.phph      h  Ky      '   vendor/symfony/polyfill-ctype/Ctype.php
      
  ~d      <   vendor/symfony/service-contracts/Test/ServiceLocatorTest.php        %$դ      8   vendor/symfony/service-contracts/ServiceLocatorTrait.php	      	  Jqb      3   vendor/symfony/service-contracts/ResetInterface.phpy       y   j      =   vendor/symfony/service-contracts/ServiceProviderInterface.php          Rk      ?   vendor/symfony/service-contracts/ServiceSubscriberInterface.php          x      ;   vendor/symfony/service-contracts/ServiceSubscriberTrait.php        @ä      @   vendor/symfony/service-contracts/Attribute/SubscribedService.php
      
  1IT      7   vendor/symfony/service-contracts/Attribute/Required.php          xj네      $   vendor/symfony/dom-crawler/Image.php        >]      0   vendor/symfony/dom-crawler/FormFieldRegistry.php7
      7
        &   vendor/symfony/dom-crawler/Crawler.php
R      
R  i`t      *   vendor/symfony/dom-crawler/UriResolver.phpX      X  ݶC`      P   vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorAttributeValueSame.php        d      F   vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextSame.phpp      p  u?      D   vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorExists.php        4-      J   vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextContains.php        <      1   vendor/symfony/dom-crawler/AbstractUriElement.phpP      P  'Ĥ      #   vendor/symfony/dom-crawler/Form.phpD      D  >ܤ      #   vendor/symfony/dom-crawler/Link.php              .   vendor/symfony/dom-crawler/Field/FormField.php        tAj
      2   vendor/symfony/dom-crawler/Field/FileFormField.php*      *  ,#      6   vendor/symfony/dom-crawler/Field/TextareaFormField.php        4^      4   vendor/symfony/dom-crawler/Field/ChoiceFormField.phpg      g  sF~      3   vendor/symfony/dom-crawler/Field/InputFormField.php        !O         vendor/symfony/yaml/Parser.phpVz      Vz  ä         vendor/symfony/yaml/Yaml.phpW      W  -         vendor/symfony/yaml/Inline.php"M      "M  ø      !   vendor/symfony/yaml/Unescaper.php        .K      '   vendor/symfony/yaml/Tag/TaggedValue.phpV      V  vX      +   vendor/symfony/yaml/Command/LintCommand.php[       [   =򲌤         vendor/symfony/yaml/Dumper.phpV      V  I         vendor/symfony/yaml/Escaper.php        Gl      0   vendor/symfony/yaml/Exception/ParseException.php        >      4   vendor/symfony/yaml/Exception/ExceptionInterface.phpv       v   0ʤ      2   vendor/symfony/yaml/Exception/RuntimeException.php          $      /   vendor/symfony/yaml/Exception/DumpException.phps       s   Ƥ      7   vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.phpV      V  7      7   vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php~      ~        3   vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php8      8  LĜ      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.phpc      c  騤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php        22/      6   vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.phpn      n  >      3   vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php        ɹ5      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php
      
  ~on      8   vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.phpA      A  SsB      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php        Db      5   vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php         	      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php1      1  e      2   vendor/phpseclib/phpseclib/phpseclib/bootstrap.php        +\      B   vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.phpo	      o	  令      9   vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.phph      h  Vg      2   vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php)d      )d  S      2   vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php"      "  2F      2   vendor/phpseclib/phpseclib/phpseclib/File/X509.phpw     w !C      :   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php          _*ё      0   vendor/phpseclib/phpseclib/phpseclib/Net/SCP.phpW      W  ˬ?      8   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php"      "  b      1   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.phpo     o E1      1   vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php'^      '^         1   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php        b      8   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php        8.w      /   vendor/mongodb/mongodb/src/InsertManyResult.php4      4  x0      '   vendor/mongodb/mongodb/src/Database.php+      +  UL2      (   vendor/mongodb/mongodb/src/functions.php!      !  J|      3   vendor/mongodb/mongodb/src/GridFS/StreamWrapper.php9      9  8y      4   vendor/mongodb/mongodb/src/GridFS/ReadableStream.phpp      p        7   vendor/mongodb/mongodb/src/GridFS/CollectionWrapper.phpk      k  eLZ      ,   vendor/mongodb/mongodb/src/GridFS/Bucket.php"0      "0  J%)      E   vendor/mongodb/mongodb/src/GridFS/Exception/FileNotFoundException.php        d      D   vendor/mongodb/mongodb/src/GridFS/Exception/CorruptFileException.php@      @  -:      ?   vendor/mongodb/mongodb/src/GridFS/Exception/StreamException.php2      2  iH      4   vendor/mongodb/mongodb/src/GridFS/WritableStream.php        nɤ      B   vendor/mongodb/mongodb/src/Model/CollectionInfoCommandIterator.php        u      6   vendor/mongodb/mongodb/src/Model/IndexInfoIterator.php          >%      0   vendor/mongodb/mongodb/src/Model/CodecCursor.php        
y      9   vendor/mongodb/mongodb/src/Model/DatabaseInfoIterator.php          b      1   vendor/mongodb/mongodb/src/Model/BSONIterator.php        K      5   vendor/mongodb/mongodb/src/Model/SearchIndexInput.php|      |  "      1   vendor/mongodb/mongodb/src/Model/DatabaseInfo.phpC      C  4$      >   vendor/mongodb/mongodb/src/Model/IndexInfoIteratorIterator.php=      =  8]      ;   vendor/mongodb/mongodb/src/Model/CollectionInfoIterator.php          RV5      1   vendor/mongodb/mongodb/src/Model/BSONDocument.php        ǵ      9   vendor/mongodb/mongodb/src/Model/ChangeStreamIterator.php              4   vendor/mongodb/mongodb/src/Model/CachingIterator.phpB      B  _      .   vendor/mongodb/mongodb/src/Model/IndexInfo.php        l      3   vendor/mongodb/mongodb/src/Model/CollectionInfo.phpo      o  T\      5   vendor/mongodb/mongodb/src/Model/CallbackIterator.php        !      .   vendor/mongodb/mongodb/src/Model/BSONArray.php        u{      /   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.phpL      L  Jq      .   vendor/mongodb/mongodb/src/BulkWriteResult.php        ѨG      6   vendor/mongodb/mongodb/src/Codec/DecodeIfSupported.php_      _  $      6   vendor/mongodb/mongodb/src/Codec/EncodeIfSupported.php_      _  ?_      *   vendor/mongodb/mongodb/src/Codec/Codec.phpf       f   ;Fn      2   vendor/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.php        5Τ      ,   vendor/mongodb/mongodb/src/PsrLogAdapter.php        
4      +   vendor/mongodb/mongodb/src/UpdateResult.php        ;5      6   vendor/mongodb/mongodb/src/Command/ListCollections.php	      	  bHI      4   vendor/mongodb/mongodb/src/Command/ListDatabases.php
      
  Ԥ      %   vendor/mongodb/mongodb/src/Client.phpo      o  z      .   vendor/mongodb/mongodb/src/InsertOneResult.php%      %  @      B   vendor/mongodb/mongodb/src/Exception/UnsupportedValueException.php        7:l      =   vendor/mongodb/mongodb/src/Exception/ResumeTokenException.php        Am      =   vendor/mongodb/mongodb/src/Exception/UnsupportedException.php              K   vendor/mongodb/mongodb/src/Exception/CreateEncryptedCollectionException.phpR      R  6ն      2   vendor/mongodb/mongodb/src/Exception/Exception.php          jܤ      9   vendor/mongodb/mongodb/src/Exception/RuntimeException.php          $      A   vendor/mongodb/mongodb/src/Exception/InvalidArgumentException.php        ao      A   vendor/mongodb/mongodb/src/Exception/UnexpectedValueException.php          ㌿      ?   vendor/mongodb/mongodb/src/Exception/BadMethodCallException.php        G      +   vendor/mongodb/mongodb/src/ChangeStream.php$      $  ^      :   vendor/mongodb/mongodb/src/Operation/UpdateSearchIndex.phpB      B  Ć       8   vendor/mongodb/mongodb/src/Operation/DropSearchIndex.php        `ä      5   vendor/mongodb/mongodb/src/Operation/DropDatabase.php          S      0   vendor/mongodb/mongodb/src/Operation/FindOne.php~      ~  0_      3   vendor/mongodb/mongodb/src/Operation/InsertMany.php              8   vendor/mongodb/mongodb/src/Operation/ListCollections.phpv      v        .   vendor/mongodb/mongodb/src/Operation/Watch.php#      #  Q}פ      2   vendor/mongodb/mongodb/src/Operation/MapReduce.phpZ#      Z#  Q̤      4   vendor/mongodb/mongodb/src/Operation/DropIndexes.phpS      S  et      /   vendor/mongodb/mongodb/src/Operation/Update.php        #-.Q      9   vendor/mongodb/mongodb/src/Operation/FindOneAndUpdate.php	      	  H      8   vendor/mongodb/mongodb/src/Operation/DatabaseCommand.php        lV      0   vendor/mongodb/mongodb/src/Operation/Explain.php
      
  0      6   vendor/mongodb/mongodb/src/Operation/ListDatabases.php              2   vendor/mongodb/mongodb/src/Operation/InsertOne.php;      ;        2   vendor/mongodb/mongodb/src/Operation/UpdateOne.php         ˤ      B   vendor/mongodb/mongodb/src/Operation/CreateEncryptedCollection.phpE      E  g&      8   vendor/mongodb/mongodb/src/Operation/WithTransaction.php        [      3   vendor/mongodb/mongodb/src/Operation/Executable.php          왏-      /   vendor/mongodb/mongodb/src/Operation/Delete.php              4   vendor/mongodb/mongodb/src/Operation/ListIndexes.phpJ	      J	  !P9      :   vendor/mongodb/mongodb/src/Operation/ListDatabaseNames.php          tB      9   vendor/mongodb/mongodb/src/Operation/ModifyCollection.phpa	      a	  [Z      4   vendor/mongodb/mongodb/src/Operation/Explainable.php          {^5      <   vendor/mongodb/mongodb/src/Operation/ListCollectionNames.php?      ?  7T      ?   vendor/mongodb/mongodb/src/Operation/EstimatedDocumentCount.php        i      6   vendor/mongodb/mongodb/src/Operation/CreateIndexes.php!      !  prk      :   vendor/mongodb/mongodb/src/Operation/ListSearchIndexes.php1      1  x)      6   vendor/mongodb/mongodb/src/Operation/FindAndModify.php        [JӤ      -   vendor/mongodb/mongodb/src/Operation/Find.php&      &  XW      2   vendor/mongodb/mongodb/src/Operation/BulkWrite.php#      #  K      :   vendor/mongodb/mongodb/src/Operation/FindOneAndReplace.php        !/m      9   vendor/mongodb/mongodb/src/Operation/FindOneAndDelete.php        נ      2   vendor/mongodb/mongodb/src/Operation/DeleteOne.php        6c      9   vendor/mongodb/mongodb/src/Operation/CreateCollection.php        e)j      3   vendor/mongodb/mongodb/src/Operation/UpdateMany.php        5%      9   vendor/mongodb/mongodb/src/Operation/RenameCollection.php        tˤ      <   vendor/mongodb/mongodb/src/Operation/CreateSearchIndexes.php        6\      @   vendor/mongodb/mongodb/src/Operation/DropEncryptedCollection.php        !Z      7   vendor/mongodb/mongodb/src/Operation/CountDocuments.php4
      4
  dF      2   vendor/mongodb/mongodb/src/Operation/Aggregate.php              .   vendor/mongodb/mongodb/src/Operation/Count.php+      +  ,      1   vendor/mongodb/mongodb/src/Operation/Distinct.php        a!      3   vendor/mongodb/mongodb/src/Operation/ReplaceOne.php+      +  
T      7   vendor/mongodb/mongodb/src/Operation/DropCollection.php
      
  C      3   vendor/mongodb/mongodb/src/Operation/DeleteMany.php        P9      !   vendor/mongodb/mongodb/rector.php        4         vendor/autoload.phpy      y  O          vendor/psy/psysh/src/Context.php        3ͤ      "   vendor/psy/psysh/src/SystemEnv.php        C      )   vendor/psy/psysh/src/Sudo/SudoVisitor.phpC      C  $      )   vendor/psy/psysh/src/ExecutionClosure.php
      
  cƤ      $   vendor/psy/psysh/src/CodeCleaner.php        o;      "   vendor/psy/psysh/src/functions.php'      '  W      -   vendor/psy/psysh/src/Readline/GNUReadline.php6      6  $Z&      *   vendor/psy/psysh/src/Readline/Userland.php 	       	  H'      *   vendor/psy/psysh/src/Readline/Readline.phpL      L  ̒?      )   vendor/psy/psysh/src/Readline/Libedit.php4      4  ̛      +   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.php        6      3   vendor/psy/psysh/src/Readline/Hoa/ExceptionIdle.php               3   vendor/psy/psysh/src/Readline/Hoa/FileDirectory.php
      
  9      3   vendor/psy/psysh/src/Readline/Hoa/StreamContext.php              -   vendor/psy/psysh/src/Readline/Hoa/IStream.php          K      ,   vendor/psy/psysh/src/Readline/Hoa/Stream.php        p>      6   vendor/psy/psysh/src/Readline/Hoa/ConsoleProcessus.php(      (  Wr      4   vendor/psy/psysh/src/Readline/Hoa/StreamLockable.phpD      D  s@,      7   vendor/psy/psysh/src/Readline/Hoa/FileLinkReadWrite.php        TT0      3   vendor/psy/psysh/src/Readline/Hoa/FileException.phpw       w   BX      7   vendor/psy/psysh/src/Readline/Hoa/ProtocolException.phpw       w   .      2   vendor/psy/psysh/src/Readline/Hoa/ConsoleInput.php        uY      .   vendor/psy/psysh/src/Readline/Hoa/Readline.php
;      
;        .   vendor/psy/psysh/src/Readline/Hoa/FileLink.php        mN      3   vendor/psy/psysh/src/Readline/Hoa/ConsoleCursor.php-#      -#  c      3   vendor/psy/psysh/src/Readline/Hoa/ConsoleWindow.phpo      o        3   vendor/psy/psysh/src/Readline/Hoa/Autocompleter.php          @;      7   vendor/psy/psysh/src/Readline/Hoa/AutocompleterWord.php          9g      0   vendor/psy/psysh/src/Readline/Hoa/FileFinder.php        k      9   vendor/psy/psysh/src/Readline/Hoa/ProtocolNodeLibrary.php        58      5   vendor/psy/psysh/src/Readline/Hoa/EventListenable.php          Y1'      /   vendor/psy/psysh/src/Readline/Hoa/Exception.php4      4  pu      -   vendor/psy/psysh/src/Readline/Hoa/Ustring.php        I,ڤ      1   vendor/psy/psysh/src/Readline/Hoa/EventBucket.php        dä      6   vendor/psy/psysh/src/Readline/Hoa/StreamBufferable.php`      `  .      4   vendor/psy/psysh/src/Readline/Hoa/StreamPathable.php          'd      1   vendor/psy/psysh/src/Readline/Hoa/EventSource.phpc       c   4ѧ      <   vendor/psy/psysh/src/Readline/Hoa/AutocompleterAggregate.php              7   vendor/psy/psysh/src/Readline/Hoa/AutocompleterPath.php        k`      2   vendor/psy/psysh/src/Readline/Hoa/ProtocolNode.php        '      5   vendor/psy/psysh/src/Readline/Hoa/StreamPointable.phpk      k  =      4   vendor/psy/psysh/src/Readline/Hoa/StreamStatable.php        o      2   vendor/psy/psysh/src/Readline/Hoa/EventListens.php        eդ      6   vendor/psy/psysh/src/Readline/Hoa/ConsoleException.phpx       x   ;      3   vendor/psy/psysh/src/Readline/Hoa/FileReadWrite.php        AB#      .   vendor/psy/psysh/src/Readline/Hoa/FileRead.php        h(      +   vendor/psy/psysh/src/Readline/Hoa/Event.php	      	  /W      5   vendor/psy/psysh/src/Readline/Hoa/StreamTouchable.php        i      4   vendor/psy/psysh/src/Readline/Hoa/EventException.phpt       t   ݤ      8   vendor/psy/psysh/src/Readline/Hoa/IteratorFileSystem.php        -       2   vendor/psy/psysh/src/Readline/Hoa/FileLinkRead.php        ^c      1   vendor/psy/psysh/src/Readline/Hoa/FileGeneric.php        &[d      3   vendor/psy/psysh/src/Readline/Hoa/EventListener.php        W      .   vendor/psy/psysh/src/Readline/Hoa/StreamIn.php3      3  g      *   vendor/psy/psysh/src/Readline/Hoa/File.php
      
  ܪ      5   vendor/psy/psysh/src/Readline/Hoa/ProtocolWrapper.php        5      /   vendor/psy/psysh/src/Readline/Hoa/Xcallable.phpi
      i
  CԲ      1   vendor/psy/psysh/src/Readline/Hoa/ConsoleTput.phpL2      L2  (
      -   vendor/psy/psysh/src/Readline/Hoa/Console.php              3   vendor/psy/psysh/src/Readline/Hoa/ConsoleOutput.php        |_֭      /   vendor/psy/psysh/src/Readline/Hoa/StreamOut.phpY      Y  uP      5   vendor/psy/psysh/src/Readline/Hoa/StreamException.phpw       w   CV      9   vendor/psy/psysh/src/Readline/Hoa/IteratorSplFileInfo.php        ݌8      ,   vendor/psy/psysh/src/VarDumper/Presenter.php        9̤      )   vendor/psy/psysh/src/VarDumper/Dumper.phpJ      J  tO'      1   vendor/psy/psysh/src/VarDumper/PresenterAware.php          Z}      )   vendor/psy/psysh/src/VarDumper/Cloner.phpA      A  =p      4   vendor/psy/psysh/src/TabCompletion/AutoCompleter.php          s>2      C   vendor/psy/psysh/src/TabCompletion/Matcher/MongoDatabaseMatcher.php        wQ      B   vendor/psy/psysh/src/TabCompletion/Matcher/ClassMethodsMatcher.php1      1  8HT      J   vendor/psy/psysh/src/TabCompletion/Matcher/AbstractContextAwareMatcher.php        [3      >   vendor/psy/psysh/src/TabCompletion/Matcher/CommandsMatcher.php        ڝ      ?   vendor/psy/psysh/src/TabCompletion/Matcher/FunctionsMatcher.php        M      ?   vendor/psy/psysh/src/TabCompletion/Matcher/ConstantsMatcher.php        :<      O   vendor/psy/psysh/src/TabCompletion/Matcher/FunctionDefaultParametersMatcher.php"      "  셤      >   vendor/psy/psysh/src/TabCompletion/Matcher/AbstractMatcher.php	      	  8X      F   vendor/psy/psysh/src/TabCompletion/Matcher/ObjectAttributesMatcher.phpK      K  _      S   vendor/psy/psysh/src/TabCompletion/Matcher/ObjectMethodDefaultParametersMatcher.php        +f      O   vendor/psy/psysh/src/TabCompletion/Matcher/AbstractDefaultParametersMatcher.php        ^(Ϥ      >   vendor/psy/psysh/src/TabCompletion/Matcher/KeywordsMatcher.php        yr      A   vendor/psy/psysh/src/TabCompletion/Matcher/MongoClientMatcher.php              R   vendor/psy/psysh/src/TabCompletion/Matcher/ClassMethodDefaultParametersMatcher.php-      -  `4      C   vendor/psy/psysh/src/TabCompletion/Matcher/ObjectMethodsMatcher.php_      _  -j      ?   vendor/psy/psysh/src/TabCompletion/Matcher/VariablesMatcher.php        G      E   vendor/psy/psysh/src/TabCompletion/Matcher/ClassAttributesMatcher.php        RwU      @   vendor/psy/psysh/src/TabCompletion/Matcher/ClassNamesMatcher.php        O      4   vendor/psy/psysh/src/Formatter/DocblockFormatter.php
      
  pE      1   vendor/psy/psysh/src/Formatter/TraceFormatter.php        SX      5   vendor/psy/psysh/src/Formatter/SignatureFormatter.php        %q      5   vendor/psy/psysh/src/Formatter/ReflectorFormatter.php          lA      0   vendor/psy/psysh/src/Formatter/CodeFormatter.php`      `  H      4   vendor/psy/psysh/src/ExecutionLoop/ProcessForker.php        v1٤      5   vendor/psy/psysh/src/ExecutionLoop/RunkitReloader.php        %N      /   vendor/psy/psysh/src/ExecutionLoop/Listener.php        #      7   vendor/psy/psysh/src/ExecutionLoop/AbstractListener.php        3         vendor/psy/psysh/src/Sudo.php	      	  Y         vendor/psy/psysh/src/Shell.php_      _  !=      1   vendor/psy/psysh/src/VersionUpdater/Installer.php        1w      /   vendor/psy/psysh/src/VersionUpdater/Checker.php	      	  ;k}      7   vendor/psy/psysh/src/VersionUpdater/IntervalChecker.php              :   vendor/psy/psysh/src/VersionUpdater/Downloader/Factory.php        $      A   vendor/psy/psysh/src/VersionUpdater/Downloader/FileDownloader.php6      6  L5      A   vendor/psy/psysh/src/VersionUpdater/Downloader/CurlDownloader.phpb      b  #@      2   vendor/psy/psysh/src/VersionUpdater/Downloader.php              2   vendor/psy/psysh/src/VersionUpdater/SelfUpdate.phpR      R  io      5   vendor/psy/psysh/src/VersionUpdater/GitHubChecker.php        ʽ      3   vendor/psy/psysh/src/VersionUpdater/NoopChecker.php          ,       -   vendor/psy/psysh/src/ExecutionLoopClosure.php        c      &   vendor/psy/psysh/src/ParserFactory.php              %   vendor/psy/psysh/src/EnvInterface.phpe       e         (   vendor/psy/psysh/src/SuperglobalsEnv.php                ?   vendor/psy/psysh/src/Reflection/ReflectionLanguageConstruct.php        g:      H   vendor/psy/psysh/src/Reflection/ReflectionLanguageConstructParameter.phpu      u  '1       6   vendor/psy/psysh/src/Reflection/ReflectionConstant.php        Oq      7   vendor/psy/psysh/src/Reflection/ReflectionNamespace.php        $t      ;   vendor/psy/psysh/src/CodeCleaner/EmptyArrayDimFetchPass.php        \
4      7   vendor/psy/psysh/src/CodeCleaner/MagicConstantsPass.php         `w      <   vendor/psy/psysh/src/CodeCleaner/PassableByReferencePass.php        '~      2   vendor/psy/psysh/src/CodeCleaner/NamespacePass.php1      1  ?      5   vendor/psy/psysh/src/CodeCleaner/LabelContextPass.php        =      7   vendor/psy/psysh/src/CodeCleaner/ImplicitReturnPass.php        R-Ƥ      6   vendor/psy/psysh/src/CodeCleaner/AbstractClassPass.php        `2ߤ      4   vendor/psy/psysh/src/CodeCleaner/CalledClassPass.php'      '        E   vendor/psy/psysh/src/CodeCleaner/FunctionReturnInWriteContextPass.php              7   vendor/psy/psysh/src/CodeCleaner/NamespaceAwarePass.php        m      3   vendor/psy/psysh/src/CodeCleaner/ReturnTypePass.php@	      @	  g      .   vendor/psy/psysh/src/CodeCleaner/IssetPass.php<      <  (      9   vendor/psy/psysh/src/CodeCleaner/ValidConstructorPass.php/      /  ~̤      2   vendor/psy/psysh/src/CodeCleaner/NoReturnValue.php        8hs      -   vendor/psy/psysh/src/CodeCleaner/ExitPass.php        Ǥ      0   vendor/psy/psysh/src/CodeCleaner/RequirePass.php        Ԥ      -   vendor/psy/psysh/src/CodeCleaner/ListPass.phpU      U  v&1      8   vendor/psy/psysh/src/CodeCleaner/LeavePsyshAlonePass.php        ^r=      ;   vendor/psy/psysh/src/CodeCleaner/AssignThisVariablePass.php        _h      @   vendor/psy/psysh/src/CodeCleaner/CallTimePassByReferencePass.php        [2      4   vendor/psy/psysh/src/CodeCleaner/CodeCleanerPass.php          h      7   vendor/psy/psysh/src/CodeCleaner/ValidClassNamePass.php        ;      :   vendor/psy/psysh/src/CodeCleaner/ValidFunctionNamePass.php              3   vendor/psy/psysh/src/CodeCleaner/FinalClassPass.php              4   vendor/psy/psysh/src/CodeCleaner/LoopContextPass.php        NR      4   vendor/psy/psysh/src/CodeCleaner/StrictTypesPass.php        I*      8   vendor/psy/psysh/src/CodeCleaner/FunctionContextPass.php        Y[      5   vendor/psy/psysh/src/CodeCleaner/UseStatementPass.php        K      &   vendor/psy/psysh/src/Configuration.phpk      k  W      3   vendor/psy/psysh/src/Command/CodeArgumentParser.php        X/      -   vendor/psy/psysh/src/Command/ClearCommand.phpd      d  ڤ      ,   vendor/psy/psysh/src/Command/EditCommand.php0      0  4      -   vendor/psy/psysh/src/Command/TraceCommand.php0      0        .   vendor/psy/psysh/src/Command/BufferCommand.php        |'
      ,   vendor/psy/psysh/src/Command/SudoCommand.php        Qr      +   vendor/psy/psysh/src/Command/WtfCommand.php'
      '
  )      +   vendor/psy/psysh/src/Command/DocCommand.php        oݤ      ?   vendor/psy/psysh/src/Command/ListCommand/VariableEnumerator.php+      +  eR=      =   vendor/psy/psysh/src/Command/ListCommand/MethodEnumerator.php        vx汤      <   vendor/psy/psysh/src/Command/ListCommand/ClassEnumerator.php        U      D   vendor/psy/psysh/src/Command/ListCommand/ClassConstantEnumerator.php|      |  (FfŤ      7   vendor/psy/psysh/src/Command/ListCommand/Enumerator.php        ?pԤ      ?   vendor/psy/psysh/src/Command/ListCommand/FunctionEnumerator.phpt      t  {      E   vendor/psy/psysh/src/Command/ListCommand/GlobalVariableEnumerator.php        YP      ?   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.php        RR      ,   vendor/psy/psysh/src/Command/HelpCommand.php,      ,  :      0   vendor/psy/psysh/src/Command/WhereamiCommand.phpa      a        ,   vendor/psy/psysh/src/Command/ExitCommand.phpz      z  9t      2   vendor/psy/psysh/src/Command/PsyVersionCommand.php        r      ,   vendor/psy/psysh/src/Command/ShowCommand.php        Kخ      -   vendor/psy/psysh/src/Command/ParseCommand.php        >2]      2   vendor/psy/psysh/src/Command/ReflectingCommand.php        2Yw      <   vendor/psy/psysh/src/Command/TimeitCommand/TimeitVisitor.php&      &  j      ,   vendor/psy/psysh/src/Command/DumpCommand.php        1      .   vendor/psy/psysh/src/Command/TimeitCommand.php6
      6
  x      /   vendor/psy/psysh/src/Command/HistoryCommand.php        H%      (   vendor/psy/psysh/src/Command/Command.php        ܤ      $   vendor/psy/psysh/src/Util/Mirror.phpM
      M
  *C      !   vendor/psy/psysh/src/Util/Str.php        dkC      "   vendor/psy/psysh/src/Util/Json.php          0      &   vendor/psy/psysh/src/Util/Docblock.php              <   vendor/psy/psysh/src/Exception/UnexpectedTargetException.phpn      n  +_      6   vendor/psy/psysh/src/Exception/FatalErrorException.phpq      q  ]      6   vendor/psy/psysh/src/Exception/DeprecatedException.phpe       e   [J      ,   vendor/psy/psysh/src/Exception/Exception.phpm       m   ϲ      3   vendor/psy/psysh/src/Exception/RuntimeException.php        Pפ      6   vendor/psy/psysh/src/Exception/ParseErrorException.php        %ڄ      1   vendor/psy/psysh/src/Exception/ErrorException.phpb      b  =탤      1   vendor/psy/psysh/src/Exception/BreakException.php        Ȥ      3   vendor/psy/psysh/src/Exception/ThrowUpException.php5      5  ρ(      %   vendor/psy/psysh/src/ContextAware.phpt       t   !i-      $   vendor/psy/psysh/src/ConfigPaths.php[      [  _mK      *   vendor/psy/psysh/src/Input/SilentInput.php          ,h      ,   vendor/psy/psysh/src/Input/FilterOptions.php        DJ      +   vendor/psy/psysh/src/Input/CodeArgument.php        p      )   vendor/psy/psysh/src/Input/ShellInput.php        vY1      /   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.php          I      +   vendor/psy/psysh/src/Output/ShellOutput.phpQ      Q  m=      5   vendor/jean85/pretty-package-versions/src/Version.php        {      <   vendor/jean85/pretty-package-versions/src/PrettyVersions.php-      -  .K      X   vendor/jean85/pretty-package-versions/src/Exception/VersionMissingExceptionInterface.php          *Q      P   vendor/jean85/pretty-package-versions/src/Exception/ReplacedPackageException.phps      s  τ      P   vendor/jean85/pretty-package-versions/src/Exception/ProvidedPackageException.phpu      u        8   vendor/php-webdriver/webdriver/lib/WebDriverUpAction.php              8   vendor/php-webdriver/webdriver/lib/WebDriverTimeouts.php        D	      D   vendor/php-webdriver/webdriver/lib/Interactions/WebDriverActions.php        A      W   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverDoubleClickAction.php              Q   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverCoordinates.php`      `  q      Q   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAction.php        [R_      W   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeysRelatedAction.php        ?ߤ      U   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSingleKeyAction.php        h      X   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverContextClickAction.php        1ӌ      Q   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseAction.php        RX}      X   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAndHoldAction.php        pAz      Y   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverButtonReleaseAction.php        eV$      S   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyDownAction.php          4v      T   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSendKeysAction.php        WB      Q   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyUpAction.php          D      X   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php        .pQ      U   vendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseMoveAction.php        T]-      I   vendor/php-webdriver/webdriver/lib/Interactions/WebDriverTouchActions.php:
      :
  my8      L   vendor/php-webdriver/webdriver/lib/Interactions/WebDriverCompositeAction.php        Jr      Z   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollFromElementAction.php5      5  ˙      M   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverMoveAction.php        'q      M   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDownAction.php        6      R   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDoubleTapAction.php        Q      N   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickAction.php        )쓤      O   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollAction.php        o}      N   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchScreen.php        Lr      Y   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickFromElementAction.phpz      z  <I	6      N   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchAction.phpi      i  !N      R   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverLongPressAction.php        m%jq      L   vendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTapAction.php        +"8      :   vendor/php-webdriver/webdriver/lib/WebDriverDispatcher.phpH      H  i'W      4   vendor/php-webdriver/webdriver/lib/WebDriverWait.php                8   vendor/php-webdriver/webdriver/lib/WebDriverPlatform.phpT      T  IOD      6   vendor/php-webdriver/webdriver/lib/WebDriverAction.phpb       b         ;   vendor/php-webdriver/webdriver/lib/Local/LocalWebDriver.phpx      x  󛕤      8   vendor/php-webdriver/webdriver/lib/WebDriverKeyboard.php          e%      <   vendor/php-webdriver/webdriver/lib/WebDriverCapabilities.php=      =  7Q      6   vendor/php-webdriver/webdriver/lib/WebDriverWindow.php
      
  OǤ      @   vendor/php-webdriver/webdriver/lib/scripts/isElementDisplayed.jst%      t%  
]U      6   vendor/php-webdriver/webdriver/lib/WebDriverRadios.php        	      =   vendor/php-webdriver/webdriver/lib/WebDriverTargetLocator.phpp      p  g      A   vendor/php-webdriver/webdriver/lib/WebDriverExpectedCondition.phpG      G  	      9   vendor/php-webdriver/webdriver/lib/Remote/RemoteMouse.php,      ,   A=      ?   vendor/php-webdriver/webdriver/lib/Remote/WebDriverResponse.php        r-      C   vendor/php-webdriver/webdriver/lib/Remote/Service/DriverService.phpa	      a	  
      K   vendor/php-webdriver/webdriver/lib/Remote/Service/DriverCommandExecutor.php              ;   vendor/php-webdriver/webdriver/lib/Remote/DriverCommand.php-      -  Vj<      <   vendor/php-webdriver/webdriver/lib/Remote/JsonWireCompat.php        #C      A   vendor/php-webdriver/webdriver/lib/Remote/RemoteExecuteMethod.phpN      N        >   vendor/php-webdriver/webdriver/lib/Remote/RemoteWebElement.php&      &  pc      <   vendor/php-webdriver/webdriver/lib/Remote/RemoteKeyboard.php          ygҤ      A   vendor/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php>      >  mA      =   vendor/php-webdriver/webdriver/lib/Remote/RemoteWebDriver.php7+      7+  Iw@      A   vendor/php-webdriver/webdriver/lib/Remote/UselessFileDetector.php          k&      :   vendor/php-webdriver/webdriver/lib/Remote/FileDetector.phpu       u         A   vendor/php-webdriver/webdriver/lib/Remote/DesiredCapabilities.php        z      B   vendor/php-webdriver/webdriver/lib/Remote/WebDriverBrowserType.php^      ^  3      E   vendor/php-webdriver/webdriver/lib/Remote/WebDriverCapabilityType.php        Z7¤      :   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.php          n.p      A   vendor/php-webdriver/webdriver/lib/Remote/RemoteTargetLocator.php
      
  @Xb      ?   vendor/php-webdriver/webdriver/lib/Remote/RemoteTouchScreen.php/      /  FԤ      D   vendor/php-webdriver/webdriver/lib/Remote/CustomWebDriverCommand.php        Y      8   vendor/php-webdriver/webdriver/lib/Remote/ShadowRoot.php1      1  G      -   vendor/php-webdriver/webdriver/lib/Cookie.php        ] ä      J   vendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriver.php        ݠ      K   vendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebElement.phps      s        T   vendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriverNavigation.phpE      E  QN      ?   vendor/php-webdriver/webdriver/lib/Support/ScreenshotHelper.php        a      ;   vendor/php-webdriver/webdriver/lib/Support/XPathEscaper.php        	d      E   vendor/php-webdriver/webdriver/lib/Support/IsElementDisplayedAtom.php        6I      7   vendor/php-webdriver/webdriver/lib/WebDriverOptions.php         ~wΤ      4   vendor/php-webdriver/webdriver/lib/WebDriverKeys.php        *Qͤ      :   vendor/php-webdriver/webdriver/lib/WebDriverNavigation.php        f      C   vendor/php-webdriver/webdriver/lib/WebDriverNavigationInterface.php          vä      6   vendor/php-webdriver/webdriver/lib/WebDriverSelect.php        mk      =   vendor/php-webdriver/webdriver/lib/WebDriverSearchContext.php                ?   vendor/php-webdriver/webdriver/lib/WebDriverSelectInterface.php@      @  _      ?   vendor/php-webdriver/webdriver/lib/WebDriverCommandExecutor.php                B   vendor/php-webdriver/webdriver/lib/Internal/WebDriverLocatable.php          Fz      2   vendor/php-webdriver/webdriver/lib/WebDriverBy.php[      [  	(      A   vendor/php-webdriver/webdriver/lib/Chrome/ChromeDriverService.php        u$Ť      :   vendor/php-webdriver/webdriver/lib/Chrome/ChromeDriver.phpK      K  Flp      ;   vendor/php-webdriver/webdriver/lib/Chrome/ChromeOptions.php        Ȥ      B   vendor/php-webdriver/webdriver/lib/Chrome/ChromeDevToolsDriver.phpG      G  eЅ      5   vendor/php-webdriver/webdriver/lib/Net/URLChecker.php|      |  '      G   vendor/php-webdriver/webdriver/lib/AbstractWebDriverCheckboxOrRadio.php        M      5   vendor/php-webdriver/webdriver/lib/WebDriverAlert.php        Z=A      0   vendor/php-webdriver/webdriver/lib/WebDriver.php        e      7   vendor/php-webdriver/webdriver/lib/WebDriverElement.php          8E      :   vendor/php-webdriver/webdriver/lib/WebDriverCheckboxes.php        Fp      9   vendor/php-webdriver/webdriver/lib/JavaScriptExecutor.php          ⌤      9   vendor/php-webdriver/webdriver/lib/WebDriverDimension.php        Z,      =   vendor/php-webdriver/webdriver/lib/WebDriverEventListener.phpH      H  L      E   vendor/php-webdriver/webdriver/lib/Exception/NoSuchFrameException.phpm       m   Κ      K   vendor/php-webdriver/webdriver/lib/Exception/UnableToSetCookieException.phps       s         N   vendor/php-webdriver/webdriver/lib/Exception/ElementNotSelectableException.php          :       E   vendor/php-webdriver/webdriver/lib/Exception/XPathLookupException.phpm       m   aTW      F   vendor/php-webdriver/webdriver/lib/Exception/UnknownErrorException.phpn       n   8L      B   vendor/php-webdriver/webdriver/lib/Exception/NoStringException.phpj       j   uq      C   vendor/php-webdriver/webdriver/lib/Exception/WebDriverException.php        
      F   vendor/php-webdriver/webdriver/lib/Exception/NoSuchCookieException.phpo       o   e?h      J   vendor/php-webdriver/webdriver/lib/Exception/IndexOutOfBoundsException.phpr       r          J   vendor/php-webdriver/webdriver/lib/Exception/NoSuchCollectionException.phpr       r   _g      H   vendor/php-webdriver/webdriver/lib/Exception/NoSuchDocumentException.phps       s   MbФ      I   vendor/php-webdriver/webdriver/lib/Exception/JavascriptErrorException.phpq       q   i.      G   vendor/php-webdriver/webdriver/lib/Exception/UnknownServerException.phpr       r   -      G   vendor/php-webdriver/webdriver/lib/Exception/UnknownMethodException.phpo       o   j8      H   vendor/php-webdriver/webdriver/lib/Exception/UnknownCommandException.phpp       p         J   vendor/php-webdriver/webdriver/lib/Exception/InvalidSessionIdException.phps       s   >      B   vendor/php-webdriver/webdriver/lib/Exception/ExpectedException.phpj       j   j驤      K   vendor/php-webdriver/webdriver/lib/Exception/UnexpectedTagNameException.php2      2  l̃      F   vendor/php-webdriver/webdriver/lib/Exception/NoCollectionException.phpn       n   3      O   vendor/php-webdriver/webdriver/lib/Exception/PhpWebDriverExceptionInterface.php`       `   E⋤      F   vendor/php-webdriver/webdriver/lib/Exception/NoSuchWindowException.phpn       n   >      O   vendor/php-webdriver/webdriver/lib/Exception/StaleElementReferenceException.phpw       w   ޢR      H   vendor/php-webdriver/webdriver/lib/Exception/NoScriptResultException.phpp       p         I   vendor/php-webdriver/webdriver/lib/Exception/IMENotAvailableException.phpq       q   (Oe      E   vendor/php-webdriver/webdriver/lib/Exception/NoSuchAlertException.phpm       m   W      N   vendor/php-webdriver/webdriver/lib/Exception/UnexpectedJavascriptException.php|       |   ۤ      M   vendor/php-webdriver/webdriver/lib/Exception/InsecureCertificateException.phpv       v   }      E   vendor/php-webdriver/webdriver/lib/Exception/NoAlertOpenException.phpo       o   W      G   vendor/php-webdriver/webdriver/lib/Exception/NoSuchElementException.phpo       o   HA\      I   vendor/php-webdriver/webdriver/lib/Exception/NoStringWrapperException.phpq       q   dKw      H   vendor/php-webdriver/webdriver/lib/Exception/NoStringLengthException.phpp       p   ⬣      K   vendor/php-webdriver/webdriver/lib/Exception/ElementNotVisibleException.phps       s   -m      A   vendor/php-webdriver/webdriver/lib/Exception/TimeoutException.phpi       i         I   vendor/php-webdriver/webdriver/lib/Exception/InvalidArgumentException.phpq       q   Z      O   vendor/php-webdriver/webdriver/lib/Exception/MoveTargetOutOfBoundsException.phpw       w   (
~      P   vendor/php-webdriver/webdriver/lib/Exception/ElementNotInteractableException.phpx       x   w      H   vendor/php-webdriver/webdriver/lib/Exception/Internal/LogicException.phpd      d  tW      E   vendor/php-webdriver/webdriver/lib/Exception/Internal/IOException.phps      s  :w6      P   vendor/php-webdriver/webdriver/lib/Exception/Internal/WebDriverCurlException.php        DZ3q      J   vendor/php-webdriver/webdriver/lib/Exception/Internal/RuntimeException.php9      9  	      U   vendor/php-webdriver/webdriver/lib/Exception/Internal/UnexpectedResponseException.php        K      S   vendor/php-webdriver/webdriver/lib/Exception/Internal/DriverServerDiedException.php~      ~  C      M   vendor/php-webdriver/webdriver/lib/Exception/InvalidCookieDomainException.phpu       u   @8s      M   vendor/php-webdriver/webdriver/lib/Exception/InvalidElementStateException.phpv       v   sH      O   vendor/php-webdriver/webdriver/lib/Exception/UnrecognizedExceptionException.phpt       t   tmW      Q   vendor/php-webdriver/webdriver/lib/Exception/ElementClickInterceptedException.phpz       z   hZS      L   vendor/php-webdriver/webdriver/lib/Exception/InvalidCoordinatesException.phpt       t   ܤ      E   vendor/php-webdriver/webdriver/lib/Exception/NullPointerException.phpm       m   &#      O   vendor/php-webdriver/webdriver/lib/Exception/UnableToCaptureScreenException.phpw       w   L}>      G   vendor/php-webdriver/webdriver/lib/Exception/ScriptTimeoutException.phpo       o   3C      I   vendor/php-webdriver/webdriver/lib/Exception/InvalidSelectorException.phpq       q   vKr      L   vendor/php-webdriver/webdriver/lib/Exception/DetachedShadowRootException.phpt       t   
ۤ      N   vendor/php-webdriver/webdriver/lib/Exception/UnsupportedOperationException.phpv       v   :      M   vendor/php-webdriver/webdriver/lib/Exception/UnexpectedAlertOpenException.phpu       u         S   vendor/php-webdriver/webdriver/lib/Exception/IMEEngineActivationFailedException.php{       {   F      K   vendor/php-webdriver/webdriver/lib/Exception/SessionNotCreatedException.phps       s   ~j      J   vendor/php-webdriver/webdriver/lib/Exception/NoSuchShadowRootException.phpr       r   z;      F   vendor/php-webdriver/webdriver/lib/Exception/NoSuchDriverException.phpn       n         ?   vendor/php-webdriver/webdriver/lib/WebDriverHasInputDevices.php          Dh      5   vendor/php-webdriver/webdriver/lib/WebDriverMouse.php        KY      5   vendor/php-webdriver/webdriver/lib/WebDriverPoint.php_      _        <   vendor/php-webdriver/webdriver/lib/Firefox/FirefoxDriver.php        ^a      C   vendor/php-webdriver/webdriver/lib/Firefox/FirefoxDriverService.phpX      X  zF      A   vendor/php-webdriver/webdriver/lib/Firefox/FirefoxPreferences.php        =z      =   vendor/php-webdriver/webdriver/lib/Firefox/FirefoxOptions.phpg      g  鵫      =   vendor/php-webdriver/webdriver/lib/Firefox/FirefoxProfile.php        х      0   vendor/softcreatr/jsonpath/src/JSONPathToken.phpX      X  tVG      4   vendor/softcreatr/jsonpath/src/JSONPathException.phpe       e   =~[V      :   vendor/softcreatr/jsonpath/src/Filters/RecursiveFilter.php=      =  ^      9   vendor/softcreatr/jsonpath/src/Filters/AbstractFilter.php              8   vendor/softcreatr/jsonpath/src/Filters/IndexesFilter.php              6   vendor/softcreatr/jsonpath/src/Filters/SliceFilter.php        Ok      ;   vendor/softcreatr/jsonpath/src/Filters/QueryMatchFilter.php,      ,  ƖȤ      6   vendor/softcreatr/jsonpath/src/Filters/IndexFilter.phpI      I  0      <   vendor/softcreatr/jsonpath/src/Filters/QueryResultFilter.php6      6  y      /   vendor/softcreatr/jsonpath/src/AccessHelper.phpH      H  	2]      +   vendor/softcreatr/jsonpath/src/JSONPath.php        rL      0   vendor/softcreatr/jsonpath/src/JSONPathLexer.php        (]      6   vendor/laravel-zero/phar-updater/src/VersionParser.php
      
        E   vendor/laravel-zero/phar-updater/src/Strategy/ShaStrategyAbstract.php        uϤ      =   vendor/laravel-zero/phar-updater/src/Strategy/ShaStrategy.php        Su`      @   vendor/laravel-zero/phar-updater/src/Strategy/GithubStrategy.php*      *  %4      @   vendor/laravel-zero/phar-updater/src/Strategy/Sha256Strategy.php        F      P   vendor/laravel-zero/phar-updater/src/Strategy/DirectDownloadStrategyAbstract.php        `]7      C   vendor/laravel-zero/phar-updater/src/Strategy/StrategyInterface.php-      -  8>      @   vendor/laravel-zero/phar-updater/src/Strategy/Sha512Strategy.php        ؤ      0   vendor/laravel-zero/phar-updater/src/Updater.php0#      0#  .:      E   vendor/laravel-zero/phar-updater/src/Exception/ExceptionInterface.phpX       X   cu,      F   vendor/laravel-zero/phar-updater/src/Exception/FilesystemException.phpn       n          G   vendor/laravel-zero/phar-updater/src/Exception/NoSignatureException.phpo       o   "}<      C   vendor/laravel-zero/phar-updater/src/Exception/RuntimeException.php          Ћ      G   vendor/laravel-zero/phar-updater/src/Exception/HttpRequestException.phpo       o   D      K   vendor/laravel-zero/phar-updater/src/Exception/InvalidArgumentException.php          b"1      G   vendor/laravel-zero/phar-updater/src/Exception/JsonParsingException.phpo       o   Z?      <?php declare(strict_types=1);

namespace PhpParser;

class Error extends \RuntimeException {
protected string $rawMessage;

protected array $attributes;







public function __construct(string $message, array $attributes = []) {
$this->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();
}
}
}
<?php declare(strict_types=1);

namespace PhpParser;

interface Parser {










public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array;






public function getTokens(): array;
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Expr;

interface PrettyPrinter {







public function prettyPrint(array $stmts): string;








public function prettyPrintExpr(Expr $node): string;








public function prettyPrintFile(array $stmts): string;
















public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string;
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Use_;

class BuilderFactory {






public function attribute($name, array $args = []): Node\Attribute {
return new Node\Attribute(
BuilderHelpers::normalizeName($name),
$this->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');
}
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Internal\DiffElem;
use PhpParser\Internal\Differ;
use PhpParser\Internal\PrintableNewAnonClassNode;
use PhpParser\Internal\TokenStream;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\IntersectionType;
use PhpParser\Node\MatchArm;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
use PhpParser\Node\UnionType;

abstract class PrettyPrinterAbstract implements PrettyPrinter {
protected const FIXUP_PREC_LEFT = 0; 
protected const FIXUP_PREC_RIGHT = 1; 
protected const FIXUP_PREC_UNARY = 2; 
protected const FIXUP_CALL_LHS = 3; 
protected const FIXUP_DEREF_LHS = 4; 
protected const FIXUP_STATIC_DEREF_LHS = 5; 
protected const FIXUP_BRACED_NAME = 6; 
protected const FIXUP_VAR_BRACED_NAME = 7; 
protected const FIXUP_ENCAPSED = 8; 
protected const FIXUP_NEW = 9; 

protected const MAX_PRECEDENCE = 1000;


protected array $precedenceMap = [




Expr\Clone_::class => [-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 "<?php" . $this->newline . $this->newline;
}

$p = "<?php" . $this->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 = "<?php" . $this->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],

];






}
}
<?php declare(strict_types=1);

namespace PhpParser\PrettyPrinter;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Stmt;
use PhpParser\PrettyPrinterAbstract;

class Standard extends PrettyPrinterAbstract {


protected function pParam(Node\Param $node): string {
return $this->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 . '<?php ';
}

protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node): string {
return '__halt_compiler();' . $node->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
            | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
            | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
            | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
            | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
        )/x';
return preg_replace_callback($regex, function ($matches): string {
assert(strlen($matches[0]) === 1);
$hex = dechex(ord($matches[0]));
return '\\x' . str_pad($hex, 2, '0', \STR_PAD_LEFT);
}, $escaped);
}

protected function containsEndLabel(string $string, string $label, bool $atStart = true): bool {
$start = $atStart ? '(?:^|[\r\n])[ \t]*' : '[\r\n][ \t]*';
return false !== strpos($string, $label)
&& preg_match('/' . $start . $label . '(?:$|[^_A-Za-z0-9\x80-\xff])/', $string);
}


protected function encapsedContainsEndLabel(array $parts, string $label): bool {
foreach ($parts as $i => $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;
}
}
<?php declare(strict_types=1);

namespace PhpParser;





final class Modifiers {
public const PUBLIC = 1;
public const PROTECTED = 2;
public const PRIVATE = 4;
public const STATIC = 8;
public const ABSTRACT = 16;
public const FINAL = 32;
public const READONLY = 64;

public const VISIBILITY_MASK = 1 | 2 | 4;




public static function verifyClassModifier(int $a, int $b): void {
if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
throw new Error('Multiple abstract modifiers are not allowed');
}

if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
throw new Error('Multiple final modifiers are not allowed');
}

if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
throw new Error('Multiple readonly modifiers are not allowed');
}

if ($a & 48 && $b & 48) {
throw new Error('Cannot use the final modifier on an abstract class');
}
}




public static function verifyModifier(int $a, int $b): void {
if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) {
throw new Error('Multiple access type modifiers are not allowed');
}

if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
throw new Error('Multiple abstract modifiers are not allowed');
}

if ($a & Modifiers::STATIC && $b & Modifiers::STATIC) {
throw new Error('Multiple static modifiers are not allowed');
}

if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
throw new Error('Multiple final modifiers are not allowed');
}

if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
throw new Error('Multiple readonly modifiers are not allowed');
}

if ($a & 48 && $b & 48) {
throw new Error('Cannot use the final modifier on an abstract class member');
}
}
}
<?php declare(strict_types=1);

namespace PhpParser;

class NodeTraverser implements NodeTraverserInterface {



public const DONT_TRAVERSE_CHILDREN = NodeVisitor::DONT_TRAVERSE_CHILDREN;




public const STOP_TRAVERSAL = NodeVisitor::STOP_TRAVERSAL;




public const REMOVE_NODE = NodeVisitor::REMOVE_NODE;




public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;


protected array $visitors = [];


protected bool $stopTraversal;






public function __construct(NodeVisitor ...$visitors) {
$this->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()})"
);
}
}
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Include_;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\UseItem;

class NodeDumper {
private bool $dumpComments;
private bool $dumpPositions;
private bool $dumpOtherAttributes;
private ?string $code;
private string $res;
private string $nl;

private const IGNORE_ATTRIBUTES = [
'comments' => 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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\ErrorHandler;

use PhpParser\Error;
use PhpParser\ErrorHandler;






class Collecting implements ErrorHandler {

private array $errors = [];

public function handleError(Error $error): void {
$this->errors[] = $error;
}






public function getErrors(): array {
return $this->errors;
}




public function hasErrors(): bool {
return !empty($this->errors);
}




public function clearErrors(): void {
$this->errors = [];
}
}
<?php declare(strict_types=1);

namespace PhpParser\ErrorHandler;

use PhpParser\Error;
use PhpParser\ErrorHandler;






class Throwing implements ErrorHandler {
public function handleError(Error $error): void {
throw $error;
}
}
<?php declare(strict_types=1);

namespace PhpParser;




class PhpVersion {

public int $id;


private const BUILTIN_TYPE_VERSIONS = [
'array' => 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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Comment;

class Doc extends \PhpParser\Comment {
}
<?php declare(strict_types=1);

namespace PhpParser;

class JsonDecoder {

private array $reflectionClassCache;


public function decode(string $json) {
$value = json_decode($json, true);
if (json_last_error()) {
throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg());
}

return $this->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\"");
}
}
<?php declare(strict_types=1);

namespace PhpParser;

class Comment implements \JsonSerializable {
protected string $text;
protected int $startLine;
protected int $startFilePos;
protected int $startTokenPos;
protected int $endLine;
protected int $endFilePos;
protected int $endTokenPos;









public function __construct(
string $text,
int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1,
int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1
) {
$this->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,
];
}
}
<?php declare(strict_types=1);

namespace PhpParser;

interface NodeVisitor {







public const DONT_TRAVERSE_CHILDREN = 1;







public const STOP_TRAVERSAL = 2;








public const REMOVE_NODE = 3;








public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;






public const REPLACE_WITH_NULL = 5;












public function beforeTraverse(array $nodes);



























public function enterNode(Node $node);






















public function leaveNode(Node $node);












public function afterTraverse(array $nodes);
}
<?php declare(strict_types=1);

namespace PhpParser;

require __DIR__ . '/compatibility_tokens.php';

class Lexer {
















public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
if (null === $errorHandler) {
$errorHandler = new ErrorHandler\Throwing();
}

$scream = ini_set('xdebug.scream', '0');

$tokens = @Token::tokenize($code);
$this->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());
}
}
<?php declare(strict_types=1);

namespace PhpParser;

interface ErrorHandler {





public function handleError(Error $error): void;
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;

use function array_merge;




















class ConstExprEvaluator {

private $fallbackEvaluator;









public function __construct(?callable $fallbackEvaluator = null) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Parser\Php7;
use PhpParser\Parser\Php8;

class ParserFactory {





public function createForVersion(PhpVersion $version): Parser {
if ($version->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());
}
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\ComplexType;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;






final class BuilderHelpers {







public static function normalizeNode($node): Node {
if ($node instanceof Builder) {
return $node->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser;




abstract class NodeVisitorAbstract implements NodeVisitor {
public function beforeTraverse(array $nodes) {
return null;
}

public function enterNode(Node $node) {
return null;
}

public function leaveNode(Node $node) {
return null;
}

public function afterTraverse(array $nodes) {
return null;
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\ErrorHandler;
use PhpParser\NameContext;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitorAbstract;

class NameResolver extends NodeVisitorAbstract {

protected NameContext $nameContext;


protected bool $preserveOriginalNames;


protected bool $replaceNodes;














public function __construct(?ErrorHandler $errorHandler = null, array $options = []) {
$this->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);
}
}
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitor;
use PhpParser\NodeVisitorAbstract;





class FirstFindingVisitor extends NodeVisitorAbstract {

protected $filterCallback;

protected ?Node $foundNode;

public function __construct(callable $filterCallback) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;






class CloningVisitor extends NodeVisitorAbstract {
public function enterNode(Node $origNode) {
$node = clone $origNode;
$node->setAttribute('origNode', $origNode);
return $node;
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Token;

class CommentAnnotatingVisitor extends NodeVisitorAbstract {

private int $pos = 0;

private array $tokens;

private array $commentPositions = [];






public function __construct(array $tokens) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;










final class NodeConnectingVisitor extends NodeVisitorAbstract {



private array $stack = [];




private $previous;

public function beforeTraverse(array $nodes) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

use function array_pop;
use function count;







final class ParentConnectingVisitor extends NodeVisitorAbstract {



private array $stack = [];

public function beforeTraverse(array $nodes) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;





class FindingVisitor extends NodeVisitorAbstract {

protected $filterCallback;

protected array $foundNodes;

public function __construct(callable $filterCallback) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\NodeVisitor\FirstFindingVisitor;

class NodeFinder {








public function find($nodes, callable $filter): array {
if ($nodes === []) {
return [];
}

if (!is_array($nodes)) {
$nodes = [$nodes];
}

$visitor = new FindingVisitor($filter);

$traverser = new NodeTraverser($visitor);
$traverser->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;
});
}
}
<?php declare(strict_types=1);

namespace PhpParser\Parser;

use PhpParser\Error;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;







class Php7 extends \PhpParser\ParserAbstract
{
public const YYERRTOK = 256;
public const T_THROW = 257;
public const T_INCLUDE = 258;
public const T_INCLUDE_ONCE = 259;
public const T_EVAL = 260;
public const T_REQUIRE = 261;
public const T_REQUIRE_ONCE = 262;
public const T_LOGICAL_OR = 263;
public const T_LOGICAL_XOR = 264;
public const T_LOGICAL_AND = 265;
public const T_PRINT = 266;
public const T_YIELD = 267;
public const T_DOUBLE_ARROW = 268;
public const T_YIELD_FROM = 269;
public const T_PLUS_EQUAL = 270;
public const T_MINUS_EQUAL = 271;
public const T_MUL_EQUAL = 272;
public const T_DIV_EQUAL = 273;
public const T_CONCAT_EQUAL = 274;
public const T_MOD_EQUAL = 275;
public const T_AND_EQUAL = 276;
public const T_OR_EQUAL = 277;
public const T_XOR_EQUAL = 278;
public const T_SL_EQUAL = 279;
public const T_SR_EQUAL = 280;
public const T_POW_EQUAL = 281;
public const T_COALESCE_EQUAL = 282;
public const T_COALESCE = 283;
public const T_BOOLEAN_OR = 284;
public const T_BOOLEAN_AND = 285;
public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286;
public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287;
public const T_IS_EQUAL = 288;
public const T_IS_NOT_EQUAL = 289;
public const T_IS_IDENTICAL = 290;
public const T_IS_NOT_IDENTICAL = 291;
public const T_SPACESHIP = 292;
public const T_IS_SMALLER_OR_EQUAL = 293;
public const T_IS_GREATER_OR_EQUAL = 294;
public const T_SL = 295;
public const T_SR = 296;
public const T_INSTANCEOF = 297;
public const T_INC = 298;
public const T_DEC = 299;
public const T_INT_CAST = 300;
public const T_DOUBLE_CAST = 301;
public const T_STRING_CAST = 302;
public const T_ARRAY_CAST = 303;
public const T_OBJECT_CAST = 304;
public const T_BOOL_CAST = 305;
public const T_UNSET_CAST = 306;
public const T_POW = 307;
public const T_NEW = 308;
public const T_CLONE = 309;
public const T_EXIT = 310;
public const T_IF = 311;
public const T_ELSEIF = 312;
public const T_ELSE = 313;
public const T_ENDIF = 314;
public const T_LNUMBER = 315;
public const T_DNUMBER = 316;
public const T_STRING = 317;
public const T_STRING_VARNAME = 318;
public const T_VARIABLE = 319;
public const T_NUM_STRING = 320;
public const T_INLINE_HTML = 321;
public const T_ENCAPSED_AND_WHITESPACE = 322;
public const T_CONSTANT_ENCAPSED_STRING = 323;
public const T_ECHO = 324;
public const T_DO = 325;
public const T_WHILE = 326;
public const T_ENDWHILE = 327;
public const T_FOR = 328;
public const T_ENDFOR = 329;
public const T_FOREACH = 330;
public const T_ENDFOREACH = 331;
public const T_DECLARE = 332;
public const T_ENDDECLARE = 333;
public const T_AS = 334;
public const T_SWITCH = 335;
public const T_MATCH = 336;
public const T_ENDSWITCH = 337;
public const T_CASE = 338;
public const T_DEFAULT = 339;
public const T_BREAK = 340;
public const T_CONTINUE = 341;
public const T_GOTO = 342;
public const T_FUNCTION = 343;
public const T_FN = 344;
public const T_CONST = 345;
public const T_RETURN = 346;
public const T_TRY = 347;
public const T_CATCH = 348;
public const T_FINALLY = 349;
public const T_USE = 350;
public const T_INSTEADOF = 351;
public const T_GLOBAL = 352;
public const T_STATIC = 353;
public const T_ABSTRACT = 354;
public const T_FINAL = 355;
public const T_PRIVATE = 356;
public const T_PROTECTED = 357;
public const T_PUBLIC = 358;
public const T_READONLY = 359;
public const T_VAR = 360;
public const T_UNSET = 361;
public const T_ISSET = 362;
public const T_EMPTY = 363;
public const T_HALT_COMPILER = 364;
public const T_CLASS = 365;
public const T_TRAIT = 366;
public const T_INTERFACE = 367;
public const T_ENUM = 368;
public const T_EXTENDS = 369;
public const T_IMPLEMENTS = 370;
public const T_OBJECT_OPERATOR = 371;
public const T_NULLSAFE_OBJECT_OPERATOR = 372;
public const T_LIST = 373;
public const T_ARRAY = 374;
public const T_CALLABLE = 375;
public const T_CLASS_C = 376;
public const T_TRAIT_C = 377;
public const T_METHOD_C = 378;
public const T_FUNC_C = 379;
public const T_LINE = 380;
public const T_FILE = 381;
public const T_START_HEREDOC = 382;
public const T_END_HEREDOC = 383;
public const T_DOLLAR_OPEN_CURLY_BRACES = 384;
public const T_CURLY_OPEN = 385;
public const T_PAAMAYIM_NEKUDOTAYIM = 386;
public const T_NAMESPACE = 387;
public const T_NS_C = 388;
public const T_DIR = 389;
public const T_NS_SEPARATOR = 390;
public const T_ELLIPSIS = 391;
public const T_NAME_FULLY_QUALIFIED = 392;
public const T_NAME_QUALIFIED = 393;
public const T_NAME_RELATIVE = 394;
public const T_ATTRIBUTE = 395;

protected int $tokenToSymbolMapSize = 396;
protected int $actionTableSize = 1258;
protected int $gotoTableSize = 567;

protected int $invalidSymbol = 168;
protected int $errorSymbol = 1;
protected int $defaultAction = -32766;
protected int $unexpectedTokenRule = 32767;

protected int $YY2TBLSTATE = 435;
protected int $numNonLeafStates = 739;

protected array $symbolToName = array(
"EOF",
"error",
"T_THROW",
"T_INCLUDE",
"T_INCLUDE_ONCE",
"T_EVAL",
"T_REQUIRE",
"T_REQUIRE_ONCE",
"','",
"T_LOGICAL_OR",
"T_LOGICAL_XOR",
"T_LOGICAL_AND",
"T_PRINT",
"T_YIELD",
"T_DOUBLE_ARROW",
"T_YIELD_FROM",
"'='",
"T_PLUS_EQUAL",
"T_MINUS_EQUAL",
"T_MUL_EQUAL",
"T_DIV_EQUAL",
"T_CONCAT_EQUAL",
"T_MOD_EQUAL",
"T_AND_EQUAL",
"T_OR_EQUAL",
"T_XOR_EQUAL",
"T_SL_EQUAL",
"T_SR_EQUAL",
"T_POW_EQUAL",
"T_COALESCE_EQUAL",
"'?'",
"':'",
"T_COALESCE",
"T_BOOLEAN_OR",
"T_BOOLEAN_AND",
"'|'",
"'^'",
"T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG",
"T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG",
"T_IS_EQUAL",
"T_IS_NOT_EQUAL",
"T_IS_IDENTICAL",
"T_IS_NOT_IDENTICAL",
"T_SPACESHIP",
"'<'",
"T_IS_SMALLER_OR_EQUAL",
"'>'",
"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 === "<?=") $self->emitError(new Error('Cannot use "<?=" as an identifier', $self->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,
];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Parser;

use PhpParser\Error;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;







class Php8 extends \PhpParser\ParserAbstract
{
public const YYERRTOK = 256;
public const T_THROW = 257;
public const T_INCLUDE = 258;
public const T_INCLUDE_ONCE = 259;
public const T_EVAL = 260;
public const T_REQUIRE = 261;
public const T_REQUIRE_ONCE = 262;
public const T_LOGICAL_OR = 263;
public const T_LOGICAL_XOR = 264;
public const T_LOGICAL_AND = 265;
public const T_PRINT = 266;
public const T_YIELD = 267;
public const T_DOUBLE_ARROW = 268;
public const T_YIELD_FROM = 269;
public const T_PLUS_EQUAL = 270;
public const T_MINUS_EQUAL = 271;
public const T_MUL_EQUAL = 272;
public const T_DIV_EQUAL = 273;
public const T_CONCAT_EQUAL = 274;
public const T_MOD_EQUAL = 275;
public const T_AND_EQUAL = 276;
public const T_OR_EQUAL = 277;
public const T_XOR_EQUAL = 278;
public const T_SL_EQUAL = 279;
public const T_SR_EQUAL = 280;
public const T_POW_EQUAL = 281;
public const T_COALESCE_EQUAL = 282;
public const T_COALESCE = 283;
public const T_BOOLEAN_OR = 284;
public const T_BOOLEAN_AND = 285;
public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286;
public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287;
public const T_IS_EQUAL = 288;
public const T_IS_NOT_EQUAL = 289;
public const T_IS_IDENTICAL = 290;
public const T_IS_NOT_IDENTICAL = 291;
public const T_SPACESHIP = 292;
public const T_IS_SMALLER_OR_EQUAL = 293;
public const T_IS_GREATER_OR_EQUAL = 294;
public const T_SL = 295;
public const T_SR = 296;
public const T_INSTANCEOF = 297;
public const T_INC = 298;
public const T_DEC = 299;
public const T_INT_CAST = 300;
public const T_DOUBLE_CAST = 301;
public const T_STRING_CAST = 302;
public const T_ARRAY_CAST = 303;
public const T_OBJECT_CAST = 304;
public const T_BOOL_CAST = 305;
public const T_UNSET_CAST = 306;
public const T_POW = 307;
public const T_NEW = 308;
public const T_CLONE = 309;
public const T_EXIT = 310;
public const T_IF = 311;
public const T_ELSEIF = 312;
public const T_ELSE = 313;
public const T_ENDIF = 314;
public const T_LNUMBER = 315;
public const T_DNUMBER = 316;
public const T_STRING = 317;
public const T_STRING_VARNAME = 318;
public const T_VARIABLE = 319;
public const T_NUM_STRING = 320;
public const T_INLINE_HTML = 321;
public const T_ENCAPSED_AND_WHITESPACE = 322;
public const T_CONSTANT_ENCAPSED_STRING = 323;
public const T_ECHO = 324;
public const T_DO = 325;
public const T_WHILE = 326;
public const T_ENDWHILE = 327;
public const T_FOR = 328;
public const T_ENDFOR = 329;
public const T_FOREACH = 330;
public const T_ENDFOREACH = 331;
public const T_DECLARE = 332;
public const T_ENDDECLARE = 333;
public const T_AS = 334;
public const T_SWITCH = 335;
public const T_MATCH = 336;
public const T_ENDSWITCH = 337;
public const T_CASE = 338;
public const T_DEFAULT = 339;
public const T_BREAK = 340;
public const T_CONTINUE = 341;
public const T_GOTO = 342;
public const T_FUNCTION = 343;
public const T_FN = 344;
public const T_CONST = 345;
public const T_RETURN = 346;
public const T_TRY = 347;
public const T_CATCH = 348;
public const T_FINALLY = 349;
public const T_USE = 350;
public const T_INSTEADOF = 351;
public const T_GLOBAL = 352;
public const T_STATIC = 353;
public const T_ABSTRACT = 354;
public const T_FINAL = 355;
public const T_PRIVATE = 356;
public const T_PROTECTED = 357;
public const T_PUBLIC = 358;
public const T_READONLY = 359;
public const T_VAR = 360;
public const T_UNSET = 361;
public const T_ISSET = 362;
public const T_EMPTY = 363;
public const T_HALT_COMPILER = 364;
public const T_CLASS = 365;
public const T_TRAIT = 366;
public const T_INTERFACE = 367;
public const T_ENUM = 368;
public const T_EXTENDS = 369;
public const T_IMPLEMENTS = 370;
public const T_OBJECT_OPERATOR = 371;
public const T_NULLSAFE_OBJECT_OPERATOR = 372;
public const T_LIST = 373;
public const T_ARRAY = 374;
public const T_CALLABLE = 375;
public const T_CLASS_C = 376;
public const T_TRAIT_C = 377;
public const T_METHOD_C = 378;
public const T_FUNC_C = 379;
public const T_LINE = 380;
public const T_FILE = 381;
public const T_START_HEREDOC = 382;
public const T_END_HEREDOC = 383;
public const T_DOLLAR_OPEN_CURLY_BRACES = 384;
public const T_CURLY_OPEN = 385;
public const T_PAAMAYIM_NEKUDOTAYIM = 386;
public const T_NAMESPACE = 387;
public const T_NS_C = 388;
public const T_DIR = 389;
public const T_NS_SEPARATOR = 390;
public const T_ELLIPSIS = 391;
public const T_NAME_FULLY_QUALIFIED = 392;
public const T_NAME_QUALIFIED = 393;
public const T_NAME_RELATIVE = 394;
public const T_ATTRIBUTE = 395;

protected int $tokenToSymbolMapSize = 396;
protected int $actionTableSize = 1257;
protected int $gotoTableSize = 657;

protected int $invalidSymbol = 168;
protected int $errorSymbol = 1;
protected int $defaultAction = -32766;
protected int $unexpectedTokenRule = 32767;

protected int $YY2TBLSTATE = 435;
protected int $numNonLeafStates = 739;

protected array $symbolToName = array(
"EOF",
"error",
"T_THROW",
"T_INCLUDE",
"T_INCLUDE_ONCE",
"T_EVAL",
"T_REQUIRE",
"T_REQUIRE_ONCE",
"','",
"T_LOGICAL_OR",
"T_LOGICAL_XOR",
"T_LOGICAL_AND",
"T_PRINT",
"T_YIELD",
"T_DOUBLE_ARROW",
"T_YIELD_FROM",
"'='",
"T_PLUS_EQUAL",
"T_MINUS_EQUAL",
"T_MUL_EQUAL",
"T_DIV_EQUAL",
"T_CONCAT_EQUAL",
"T_MOD_EQUAL",
"T_AND_EQUAL",
"T_OR_EQUAL",
"T_XOR_EQUAL",
"T_SL_EQUAL",
"T_SR_EQUAL",
"T_POW_EQUAL",
"T_COALESCE_EQUAL",
"'?'",
"':'",
"T_COALESCE",
"T_BOOLEAN_OR",
"T_BOOLEAN_AND",
"'|'",
"'^'",
"T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG",
"T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG",
"T_IS_EQUAL",
"T_IS_NOT_EQUAL",
"T_IS_IDENTICAL",
"T_IS_NOT_IDENTICAL",
"T_SPACESHIP",
"'<'",
"T_IS_SMALLER_OR_EQUAL",
"'>'",
"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 === "<?=") $self->emitError(new Error('Cannot use "<?=" as an identifier', $self->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,
];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;




class DiffElem {
public const TYPE_KEEP = 0;
public const TYPE_REMOVE = 1;
public const TYPE_ADD = 2;
public const TYPE_REPLACE = 3;


public int $type;

public $old;

public $new;






public function __construct(int $type, $old, $new) {
$this->type = $type;
$this->old = $old;
$this->new = $new;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

use PhpParser\Token;






class TokenStream {

private array $tokens;

private array $indentMap;






public function __construct(array $tokens) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

use PhpParser\Node;
use PhpParser\Node\Expr;











class PrintableNewAnonClassNode extends Expr {

public array $attrGroups;

public int $flags;

public array $args;

public ?Node\Name $extends;

public array $implements;

public array $stmts;









public function __construct(
array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements,
array $stmts, array $attributes
) {
parent::__construct($attributes);
$this->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'];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;










class Differ {

private $isEqual;






public function __construct(callable $isEqual) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

if (\PHP_VERSION_ID >= 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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer;

use PhpParser\Error;
use PhpParser\ErrorHandler;
use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;

class Emulative extends Lexer {

private array $patches = [];


private array $emulators = [];

private PhpVersion $targetPhpVersion;

private PhpVersion $hostPhpVersion;




public function __construct(?PhpVersion $phpVersion = null) {
$this->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);
}
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

final class NullsafeTokenEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 0);
}

public function isEmulationNeeded(string $code): bool {
return strpos($code, '?->') !== 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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class ReadonlyTokenEmulator extends KeywordEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 1);
}

public function getKeywordString(): string {
return 'readonly';
}

public function getKeywordToken(): int {
return \T_READONLY;
}

protected function isKeywordContext(array $tokens, int $pos): bool {
if (!parent::isKeywordContext($tokens, $pos)) {
return false;
}

return !(isset($tokens[$pos + 1]) &&
($tokens[$pos + 1]->text === '(' ||
($tokens[$pos + 1]->id === \T_WHITESPACE &&
isset($tokens[$pos + 2]) &&
$tokens[$pos + 2]->text === '(')));
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

class ExplicitOctalEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 1);
}

public function isEmulationNeeded(string $code): bool {
return strpos($code, '0o') !== false || strpos($code, '0O') !== false;
}

public function emulate(string $code, array $tokens): array {
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
if ($token->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;


abstract class TokenEmulator {
abstract public function getPhpVersion(): PhpVersion;

abstract public function isEmulationNeeded(string $code): bool;





abstract public function emulate(string $code, array $tokens): array;





abstract public function reverseEmulate(string $code, array $tokens): array;


public function preprocessCode(string $code, array &$patches): string {
return $code;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

final class AttributeEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 0);
}

public function isEmulationNeeded(string $code): bool {
return strpos($code, '#[') !== 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]->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class EnumTokenEmulator extends KeywordEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 1);
}

public function getKeywordString(): string {
return 'enum';
}

public function getKeywordToken(): int {
return \T_ENUM;
}

protected function isKeywordContext(array $tokens, int $pos): bool {
return parent::isKeywordContext($tokens, $pos)
&& isset($tokens[$pos + 2])
&& $tokens[$pos + 1]->id === \T_WHITESPACE
&& $tokens[$pos + 2]->id === \T_STRING;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;








class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
public function getKeywordString(): string {
return 'readonly';
}

public function getKeywordToken(): int {
return \T_READONLY;
}

public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 2);
}

public function reverseEmulate(string $code, array $tokens): array {

return $tokens;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\Token;

abstract class KeywordEmulator extends TokenEmulator {
abstract public function getKeywordString(): string;
abstract public function getKeywordToken(): int;

public function isEmulationNeeded(string $code): bool {
return strpos(strtolower($code), $this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class MatchTokenEmulator extends KeywordEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 0);
}

public function getKeywordString(): string {
return 'match';
}

public function getKeywordToken(): int {
return \T_MATCH;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;




final class ReverseEmulator extends TokenEmulator {

private TokenEmulator $emulator;

public function __construct(TokenEmulator $emulator) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;

class NameContext {

protected ?Name $namespace;


protected array $aliases = [];


protected array $origAliases = [];


protected ErrorHandler $errorHandler;






public function __construct(ErrorHandler $errorHandler) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser;




class Token extends Internal\TokenPolyfill {

public function getEndPos(): int {
return $this->pos + \strlen($this->text);
}


public function getEndLine(): int {
return $this->line + \substr_count($this->text, "\n");
}
}
<?php declare(strict_types=1);

namespace PhpParser;

class ConstExprEvaluationException extends \Exception {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;

class NullableType extends ComplexType {

public Node $type;







public function __construct(Node $type, array $attributes = []) {
$this->attributes = $attributes;
$this->type = $type;
}

public function getSubNodeNames(): array {
return ['type'];
}

public function getType(): string {
return 'NullableType';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class MatchArm extends NodeAbstract {

public ?array $conds;

public Expr $body;




public function __construct(?array $conds, Node\Expr $body, array $attributes = []) {
$this->conds = $conds;
$this->body = $body;
$this->attributes = $attributes;
}

public function getSubNodeNames(): array {
return ['conds', 'body'];
}

public function getType(): string {
return 'MatchArm';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class Name extends NodeAbstract {

public string $name;


private static array $specialClassNames = [
'self' => 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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class AttributeGroup extends NodeAbstract {

public array $attrs;





public function __construct(array $attrs, array $attributes = []) {
$this->attributes = $attributes;
$this->attrs = $attrs;
}

public function getSubNodeNames(): array {
return ['attrs'];
}

public function getType(): string {
return 'AttributeGroup';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;






abstract class ComplexType extends NodeAbstract {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\NodeAbstract;

class Param extends NodeAbstract {

public ?Node $type;

public bool $byRef;

public bool $variadic;

public Expr $var;

public ?Expr $default;

public int $flags;

public array $attrGroups;













public function __construct(
Expr $var, ?Expr $default = null, ?Node $type = null,
bool $byRef = false, bool $variadic = false,
array $attributes = [],
int $flags = 0,
array $attrGroups = []
) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;

interface FunctionLike extends Node {



public function returnsByRef(): bool;






public function getParams(): array;






public function getReturnType();






public function getStmts(): ?array;






public function getAttrGroups(): array;
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

class IntersectionType extends ComplexType {

public array $types;







public function __construct(array $types, array $attributes = []) {
$this->attributes = $attributes;
$this->types = $types;
}

public function getSubNodeNames(): array {
return ['types'];
}

public function getType(): string {
return 'IntersectionType';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class StaticVar extends NodeAbstract {

public Expr\Variable $var;

public ?Expr $default;








public function __construct(
Expr\Variable $var, ?Node\Expr $default = null, array $attributes = []
) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Do_ extends Node\Stmt {

public array $stmts;

public Node\Expr $cond;








public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['stmts', 'cond'];
}

public function getType(): string {
return 'Stmt_Do';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Return_ extends Node\Stmt {

public ?Node\Expr $expr;







public function __construct(?Node\Expr $expr = null, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Stmt_Return';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\AttributeGroup;

class EnumCase extends Node\Stmt {

public Node\Identifier $name;

public ?Node\Expr $expr;

public array $attrGroups;







public function __construct($name, ?Node\Expr $expr = null, array $attrGroups = [], array $attributes = []) {
parent::__construct($attributes);
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class Block extends Stmt {

public array $stmts;







public function __construct(array $stmts, array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}

public function getType(): string {
return 'Stmt_Block';
}

public function getSubNodeNames(): array {
return ['stmts'];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;


class Nop extends Node\Stmt {
public function getSubNodeNames(): array {
return [];
}

public function getType(): string {
return 'Stmt_Nop';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\DeclareItem;

class Declare_ extends Node\Stmt {

public array $declares;

public ?array $stmts;








public function __construct(array $declares, ?array $stmts = null, array $attributes = []) {
$this->attributes = $attributes;
$this->declares = $declares;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['declares', 'stmts'];
}

public function getType(): string {
return 'Stmt_Declare';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class For_ extends Node\Stmt {

public array $init;

public array $cond;

public array $loop;

public array $stmts;
















public function __construct(array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Global_ extends Node\Stmt {

public array $vars;







public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}

public function getSubNodeNames(): array {
return ['vars'];
}

public function getType(): string {
return 'Stmt_Global';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Foreach_ extends Node\Stmt {

public Node\Expr $expr;

public ?Node\Expr $keyVar;

public bool $byRef;

public Node\Expr $valueVar;

public array $stmts;
















public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Trait_ extends ClassLike {












public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/../DeclareItem.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class Goto_ extends Stmt {

public Identifier $name;







public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}

public function getSubNodeNames(): array {
return ['name'];
}

public function getType(): string {
return 'Stmt_Goto';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Switch_ extends Node\Stmt {

public Node\Expr $cond;

public array $cases;








public function __construct(Node\Expr $cond, array $cases, array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->cases = $cases;
}

public function getSubNodeNames(): array {
return ['cond', 'cases'];
}

public function getType(): string {
return 'Stmt_Switch';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\StaticVar;
use PhpParser\Node\Stmt;

class Static_ extends Stmt {

public array $vars;







public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}

public function getSubNodeNames(): array {
return ['vars'];
}

public function getType(): string {
return 'Stmt_Static';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class TryCatch extends Node\Stmt {

public array $stmts;

public array $catches;

public ?Finally_ $finally;









public function __construct(array $stmts, array $catches, ?Finally_ $finally = null, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class InlineHTML extends Stmt {

public string $value;







public function __construct(string $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}

public function getSubNodeNames(): array {
return ['value'];
}

public function getType(): string {
return 'Stmt_InlineHTML';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;

class ClassConst extends Node\Stmt {

public int $flags;

public array $consts;

public array $attrGroups;

public ?Node $type;










public function __construct(
array $consts,
int $flags = 0,
array $attributes = [],
array $attrGroups = [],
?Node $type = null
) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;

class Class_ extends ClassLike {

public const MODIFIER_PUBLIC = 1;

public const MODIFIER_PROTECTED = 2;

public const MODIFIER_PRIVATE = 4;

public const MODIFIER_STATIC = 8;

public const MODIFIER_ABSTRACT = 16;

public const MODIFIER_FINAL = 32;

public const MODIFIER_READONLY = 64;


public const VISIBILITY_MODIFIER_MASK = 7; 


public int $flags;

public ?Node\Name $extends;

public array $implements;



















public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class HaltCompiler extends Stmt {

public string $remaining;







public function __construct(string $remaining, array $attributes = []) {
$this->attributes = $attributes;
$this->remaining = $remaining;
}

public function getSubNodeNames(): array {
return ['remaining'];
}

public function getType(): string {
return 'Stmt_HaltCompiler';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\FunctionLike;

class ClassMethod extends Node\Stmt implements FunctionLike {

public int $flags;

public bool $byRef;

public Node\Identifier $name;

public array $params;

public ?Node $returnType;

public ?array $stmts;

public array $attrGroups;


private static array $magicNames = [
'__construct' => 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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt\TraitUseAdaptation;

use PhpParser\Node;

class Alias extends Node\Stmt\TraitUseAdaptation {

public ?int $newModifier;

public ?Node\Identifier $newName;










public function __construct(?Node\Name $trait, $method, ?int $newModifier, $newName, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt\TraitUseAdaptation;

use PhpParser\Node;

class Precedence extends Node\Stmt\TraitUseAdaptation {

public array $insteadof;









public function __construct(Node\Name $trait, $method, array $insteadof, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/../StaticVar.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class ElseIf_ extends Node\Stmt {

public Node\Expr $cond;

public array $stmts;








public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['cond', 'stmts'];
}

public function getType(): string {
return 'Stmt_ElseIf';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/../UseItem.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\PropertyItem;

class Property extends Node\Stmt {

public int $flags;

public array $props;

public ?Node $type;

public array $attrGroups;










public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;




class Expression extends Node\Stmt {

public Node\Expr $expr;







public function __construct(Node\Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Stmt_Expression';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/../PropertyItem.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\PropertyItem;

abstract class ClassLike extends Node\Stmt {

public ?Node\Identifier $name;

public array $stmts;

public array $attrGroups;


public ?Node\Name $namespacedName;




public function getTraitUses(): array {
$traitUses = [];
foreach ($this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Namespace_ extends Node\Stmt {

public const KIND_SEMICOLON = 1;
public const KIND_BRACED = 2;


public ?Node\Name $name;

public $stmts;








public function __construct(?Node\Name $name = null, ?array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['name', 'stmts'];
}

public function getType(): string {
return 'Stmt_Namespace';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;

class Use_ extends Stmt {





public const TYPE_UNKNOWN = 0;

public const TYPE_NORMAL = 1;

public const TYPE_FUNCTION = 2;

public const TYPE_CONSTANT = 3;


public int $type;

public array $uses;








public function __construct(array $uses, int $type = self::TYPE_NORMAL, array $attributes = []) {
$this->attributes = $attributes;
$this->type = $type;
$this->uses = $uses;
}

public function getSubNodeNames(): array {
return ['type', 'uses'];
}

public function getType(): string {
return 'Stmt_Use';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Const_ extends Node\Stmt {

public array $consts;







public function __construct(array $consts, array $attributes = []) {
$this->attributes = $attributes;
$this->consts = $consts;
}

public function getSubNodeNames(): array {
return ['consts'];
}

public function getType(): string {
return 'Stmt_Const';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Interface_ extends ClassLike {

public array $extends;















public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;

class GroupUse extends Stmt {



public int $type;

public Name $prefix;

public array $uses;









public function __construct(Name $prefix, array $uses, int $type = Use_::TYPE_NORMAL, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Echo_ extends Node\Stmt {

public array $exprs;







public function __construct(array $exprs, array $attributes = []) {
$this->attributes = $attributes;
$this->exprs = $exprs;
}

public function getSubNodeNames(): array {
return ['exprs'];
}

public function getType(): string {
return 'Stmt_Echo';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Break_ extends Node\Stmt {

public ?Node\Expr $num;







public function __construct(?Node\Expr $num = null, array $attributes = []) {
$this->attributes = $attributes;
$this->num = $num;
}

public function getSubNodeNames(): array {
return ['num'];
}

public function getType(): string {
return 'Stmt_Break';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class While_ extends Node\Stmt {

public Node\Expr $cond;

public array $stmts;








public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['cond', 'stmts'];
}

public function getType(): string {
return 'Stmt_While';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Enum_ extends ClassLike {

public ?Node $scalarType;

public array $implements;















public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\Expr;

class Catch_ extends Node\Stmt {

public array $types;

public ?Expr\Variable $var;

public array $stmts;









public function __construct(
array $types, ?Expr\Variable $var = null, array $stmts = [], array $attributes = []
) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Case_ extends Node\Stmt {

public ?Node\Expr $cond;

public array $stmts;








public function __construct(?Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['cond', 'stmts'];
}

public function getType(): string {
return 'Stmt_Case';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Else_ extends Node\Stmt {

public array $stmts;







public function __construct(array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['stmts'];
}

public function getType(): string {
return 'Stmt_Else';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Unset_ extends Node\Stmt {

public array $vars;







public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}

public function getSubNodeNames(): array {
return ['vars'];
}

public function getType(): string {
return 'Stmt_Unset';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\FunctionLike;

class Function_ extends Node\Stmt implements FunctionLike {

public bool $byRef;

public Node\Identifier $name;

public array $params;

public ?Node $returnType;

public array $stmts;

public array $attrGroups;


public ?Node\Name $namespacedName;



















public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class Label extends Stmt {

public Identifier $name;







public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}

public function getSubNodeNames(): array {
return ['name'];
}

public function getType(): string {
return 'Stmt_Label';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Finally_ extends Node\Stmt {

public array $stmts;







public function __construct(array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}

public function getSubNodeNames(): array {
return ['stmts'];
}

public function getType(): string {
return 'Stmt_Finally';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class TraitUse extends Node\Stmt {

public array $traits;

public array $adaptations;








public function __construct(array $traits, array $adaptations = [], array $attributes = []) {
$this->attributes = $attributes;
$this->traits = $traits;
$this->adaptations = $adaptations;
}

public function getSubNodeNames(): array {
return ['traits', 'adaptations'];
}

public function getType(): string {
return 'Stmt_TraitUse';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

abstract class TraitUseAdaptation extends Node\Stmt {

public ?Node\Name $trait;

public Node\Identifier $method;
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Continue_ extends Node\Stmt {

public ?Node\Expr $num;







public function __construct(?Node\Expr $num = null, array $attributes = []) {
$this->attributes = $attributes;
$this->num = $num;
}

public function getSubNodeNames(): array {
return ['num'];
}

public function getType(): string {
return 'Stmt_Continue';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class If_ extends Node\Stmt {

public Node\Expr $cond;

public array $stmts;

public array $elseifs;

public ?Else_ $else;















public function __construct(Node\Expr $cond, array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class Attribute extends NodeAbstract {

public Name $name;


public array $args;






public function __construct(Name $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
$this->args = $args;
}

public function getSubNodeNames(): array {
return ['name', 'args'];
}

public function getType(): string {
return 'Attribute';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;




class VariadicPlaceholder extends NodeAbstract {





public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}

public function getType(): string {
return 'VariadicPlaceholder';
}

public function getSubNodeNames(): array {
return [];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class Arg extends NodeAbstract {

public ?Identifier $name;

public Expr $value;

public bool $byRef;

public bool $unpack;










public function __construct(
Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [],
?Identifier $name = null
) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class Const_ extends NodeAbstract {

public Identifier $name;

public Expr $value;


public ?Name $namespacedName;








public function __construct($name, Expr $value, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class InterpolatedStringPart extends NodeAbstract {

public string $value;







public function __construct(string $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}

public function getSubNodeNames(): array {
return ['value'];
}

public function getType(): string {
return 'InterpolatedStringPart';
}
}


class_alias(InterpolatedStringPart::class, Scalar\EncapsedStringPart::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

abstract class Stmt extends NodeAbstract {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class PropertyItem extends NodeAbstract {

public VarLikeIdentifier $name;

public ?Expr $default;








public function __construct($name, ?Node\Expr $default = null, array $attributes = []) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class ArrayItem extends NodeAbstract {

public ?Expr $key;

public Expr $value;

public bool $byRef;

public bool $unpack;









public function __construct(Expr $value, ?Expr $key = null, bool $byRef = false, array $attributes = [], bool $unpack = false) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

abstract class Expr extends NodeAbstract {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class ClosureUse extends NodeAbstract {

public Expr\Variable $var;

public bool $byRef;








public function __construct(Expr\Variable $var, bool $byRef = false, array $attributes = []) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;
use PhpParser\Node\Stmt\Use_;

class UseItem extends NodeAbstract {



public int $type;

public Name $name;

public ?Identifier $alias;









public function __construct(Node\Name $name, $alias = null, int $type = Use_::TYPE_UNKNOWN, array $attributes = []) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node;








class VarLikeIdentifier extends Identifier {
public function getType(): string {
return 'VarLikeIdentifier';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Name;

class FullyQualified extends \PhpParser\Node\Name {





public function isUnqualified(): bool {
return false;
}






public function isQualified(): bool {
return false;
}






public function isFullyQualified(): bool {
return true;
}






public function isRelative(): bool {
return false;
}

public function toCodeString(): string {
return '\\' . $this->toString();
}

public function getType(): string {
return 'Name_FullyQualified';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Name;

class Relative extends \PhpParser\Node\Name {





public function isUnqualified(): bool {
return false;
}






public function isQualified(): bool {
return false;
}






public function isFullyQualified(): bool {
return false;
}






public function isRelative(): bool {
return true;
}

public function toCodeString(): string {
return 'namespace\\' . $this->toString();
}

public function getType(): string {
return 'Name_Relative';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/Float_.php';
<?php declare(strict_types=1);

require __DIR__ . '/../InterpolatedStringPart.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\Scalar;

abstract class MagicConst extends Scalar {





public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}

public function getSubNodeNames(): array {
return [];
}






abstract public function getName(): string;
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Error;
use PhpParser\Node\Scalar;

class String_ extends Scalar {

public const KIND_SINGLE_QUOTED = 1;
public const KIND_DOUBLE_QUOTED = 2;
public const KIND_HEREDOC = 3;
public const KIND_NOWDOC = 4;


public string $value;


protected static array $replacements = [
'\\' => '\\',
'$' => '$',
'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';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/Int_.php';
<?php declare(strict_types=1);

require __DIR__ . '/InterpolatedString.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\Scalar;

class Float_ extends Scalar {

public float $value;







public function __construct(float $value, array $attributes = []) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\Expr;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Scalar;

class InterpolatedString extends Scalar {

public array $parts;







public function __construct(array $parts, array $attributes = []) {
$this->attributes = $attributes;
$this->parts = $parts;
}

public function getSubNodeNames(): array {
return ['parts'];
}

public function getType(): string {
return 'Scalar_InterpolatedString';
}
}


class_alias(InterpolatedString::class, Encapsed::class);
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Error;
use PhpParser\Node\Scalar;

class Int_ extends Scalar {

public const KIND_BIN = 2;
public const KIND_OCT = 8;
public const KIND_DEC = 10;
public const KIND_HEX = 16;


public int $value;







public function __construct(int $value, array $attributes = []) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Trait_ extends MagicConst {
public function getName(): string {
return '__TRAIT__';
}

public function getType(): string {
return 'Scalar_MagicConst_Trait';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Class_ extends MagicConst {
public function getName(): string {
return '__CLASS__';
}

public function getType(): string {
return 'Scalar_MagicConst_Class';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Dir extends MagicConst {
public function getName(): string {
return '__DIR__';
}

public function getType(): string {
return 'Scalar_MagicConst_Dir';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Namespace_ extends MagicConst {
public function getName(): string {
return '__NAMESPACE__';
}

public function getType(): string {
return 'Scalar_MagicConst_Namespace';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Method extends MagicConst {
public function getName(): string {
return '__METHOD__';
}

public function getType(): string {
return 'Scalar_MagicConst_Method';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Line extends MagicConst {
public function getName(): string {
return '__LINE__';
}

public function getType(): string {
return 'Scalar_MagicConst_Line';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Function_ extends MagicConst {
public function getName(): string {
return '__FUNCTION__';
}

public function getType(): string {
return 'Scalar_MagicConst_Function';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class File extends MagicConst {
public function getName(): string {
return '__FILE__';
}

public function getType(): string {
return 'Scalar_MagicConst_File';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;




class Identifier extends NodeAbstract {

public string $name;


private static array $specialClassNames = [
'self' => 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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

abstract class Scalar extends Expr {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

class UnionType extends ComplexType {

public array $types;







public function __construct(array $types, array $attributes = []) {
$this->attributes = $attributes;
$this->types = $types;
}

public function getSubNodeNames(): array {
return ['types'];
}

public function getType(): string {
return 'UnionType';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class DeclareItem extends NodeAbstract {

public Identifier $key;

public Expr $value;








public function __construct($key, Node\Expr $value, array $attributes = []) {
$this->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);
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;







class Error extends Expr {





public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}

public function getSubNodeNames(): array {
return [];
}

public function getType(): string {
return 'Expr_Error';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Isset_ extends Expr {

public array $vars;







public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}

public function getSubNodeNames(): array {
return ['vars'];
}

public function getType(): string {
return 'Expr_Isset';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;

class ClassConstFetch extends Expr {

public Node $class;

public Node $name;








public function __construct(Node $class, $name, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PostInc extends Expr {

public Expr $var;







public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}

public function getSubNodeNames(): array {
return ['var'];
}

public function getType(): string {
return 'Expr_PostInc';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Eval_ extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_Eval';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;

class MethodCall extends CallLike {

public Expr $var;

public Node $name;

public array $args;









public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class ErrorSuppress extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_ErrorSuppress';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;

class NullsafeMethodCall extends CallLike {

public Expr $var;

public Node $name;

public array $args;









public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\VarLikeIdentifier;

class StaticPropertyFetch extends Expr {

public Node $class;

public Node $name;








public function __construct(Node $class, $name, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class YieldFrom extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_YieldFrom';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;

class NullsafePropertyFetch extends Expr {

public Expr $var;

public Node $name;








public function __construct(Expr $var, $name, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Exit_ extends Expr {

public const KIND_EXIT = 1;
public const KIND_DIE = 2;


public ?Expr $expr;







public function __construct(?Expr $expr = null, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_Exit';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class ArrayDimFetch extends Expr {

public Expr $var;

public ?Expr $dim;








public function __construct(Expr $var, ?Expr $dim = null, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->dim = $dim;
}

public function getSubNodeNames(): array {
return ['var', 'dim'];
}

public function getType(): string {
return 'Expr_ArrayDimFetch';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class LogicalAnd extends BinaryOp {
public function getOperatorSigil(): string {
return 'and';
}

public function getType(): string {
return 'Expr_BinaryOp_LogicalAnd';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BooleanAnd extends BinaryOp {
public function getOperatorSigil(): string {
return '&&';
}

public function getType(): string {
return 'Expr_BinaryOp_BooleanAnd';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Greater extends BinaryOp {
public function getOperatorSigil(): string {
return '>';
}

public function getType(): string {
return 'Expr_BinaryOp_Greater';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BooleanOr extends BinaryOp {
public function getOperatorSigil(): string {
return '||';
}

public function getType(): string {
return 'Expr_BinaryOp_BooleanOr';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Pow extends BinaryOp {
public function getOperatorSigil(): string {
return '**';
}

public function getType(): string {
return 'Expr_BinaryOp_Pow';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Spaceship extends BinaryOp {
public function getOperatorSigil(): string {
return '<=>';
}

public function getType(): string {
return 'Expr_BinaryOp_Spaceship';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Mod extends BinaryOp {
public function getOperatorSigil(): string {
return '%';
}

public function getType(): string {
return 'Expr_BinaryOp_Mod';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Concat extends BinaryOp {
public function getOperatorSigil(): string {
return '.';
}

public function getType(): string {
return 'Expr_BinaryOp_Concat';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class ShiftLeft extends BinaryOp {
public function getOperatorSigil(): string {
return '<<';
}

public function getType(): string {
return 'Expr_BinaryOp_ShiftLeft';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BitwiseOr extends BinaryOp {
public function getOperatorSigil(): string {
return '|';
}

public function getType(): string {
return 'Expr_BinaryOp_BitwiseOr';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BitwiseXor extends BinaryOp {
public function getOperatorSigil(): string {
return '^';
}

public function getType(): string {
return 'Expr_BinaryOp_BitwiseXor';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class NotEqual extends BinaryOp {
public function getOperatorSigil(): string {
return '!=';
}

public function getType(): string {
return 'Expr_BinaryOp_NotEqual';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Identical extends BinaryOp {
public function getOperatorSigil(): string {
return '===';
}

public function getType(): string {
return 'Expr_BinaryOp_Identical';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Coalesce extends BinaryOp {
public function getOperatorSigil(): string {
return '??';
}

public function getType(): string {
return 'Expr_BinaryOp_Coalesce';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class NotIdentical extends BinaryOp {
public function getOperatorSigil(): string {
return '!==';
}

public function getType(): string {
return 'Expr_BinaryOp_NotIdentical';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Equal extends BinaryOp {
public function getOperatorSigil(): string {
return '==';
}

public function getType(): string {
return 'Expr_BinaryOp_Equal';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class SmallerOrEqual extends BinaryOp {
public function getOperatorSigil(): string {
return '<=';
}

public function getType(): string {
return 'Expr_BinaryOp_SmallerOrEqual';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Smaller extends BinaryOp {
public function getOperatorSigil(): string {
return '<';
}

public function getType(): string {
return 'Expr_BinaryOp_Smaller';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BitwiseAnd extends BinaryOp {
public function getOperatorSigil(): string {
return '&';
}

public function getType(): string {
return 'Expr_BinaryOp_BitwiseAnd';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Mul extends BinaryOp {
public function getOperatorSigil(): string {
return '*';
}

public function getType(): string {
return 'Expr_BinaryOp_Mul';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class ShiftRight extends BinaryOp {
public function getOperatorSigil(): string {
return '>>';
}

public function getType(): string {
return 'Expr_BinaryOp_ShiftRight';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Div extends BinaryOp {
public function getOperatorSigil(): string {
return '/';
}

public function getType(): string {
return 'Expr_BinaryOp_Div';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Plus extends BinaryOp {
public function getOperatorSigil(): string {
return '+';
}

public function getType(): string {
return 'Expr_BinaryOp_Plus';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class LogicalOr extends BinaryOp {
public function getOperatorSigil(): string {
return 'or';
}

public function getType(): string {
return 'Expr_BinaryOp_LogicalOr';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Minus extends BinaryOp {
public function getOperatorSigil(): string {
return '-';
}

public function getType(): string {
return 'Expr_BinaryOp_Minus';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class GreaterOrEqual extends BinaryOp {
public function getOperatorSigil(): string {
return '>=';
}

public function getType(): string {
return 'Expr_BinaryOp_GreaterOrEqual';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class LogicalXor extends BinaryOp {
public function getOperatorSigil(): string {
return 'xor';
}

public function getType(): string {
return 'Expr_BinaryOp_LogicalXor';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class BooleanNot extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_BooleanNot';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Empty_ extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_Empty';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\MatchArm;

class Match_ extends Node\Expr {

public Node\Expr $cond;

public array $arms;






public function __construct(Node\Expr $cond, array $arms = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->arms = $arms;
}

public function getSubNodeNames(): array {
return ['cond', 'arms'];
}

public function getType(): string {
return 'Expr_Match';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class BitwiseNot extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_BitwiseNot';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;
use PhpParser\Node\Name;

class ConstFetch extends Expr {

public Name $name;







public function __construct(Name $name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
}

public function getSubNodeNames(): array {
return ['name'];
}

public function getType(): string {
return 'Expr_ConstFetch';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class UnaryMinus extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_UnaryMinus';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\ClosureUse;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;

class Closure extends Expr implements FunctionLike {

public bool $static;

public bool $byRef;

public array $params;

public array $uses;

public ?Node $returnType;

public array $stmts;

public array $attrGroups;






















public function __construct(array $subNodes = [], array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;

class Instanceof_ extends Expr {

public Expr $expr;

public Node $class;








public function __construct(Expr $expr, Node $class, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
$this->class = $class;
}

public function getSubNodeNames(): array {
return ['expr', 'class'];
}

public function getType(): string {
return 'Expr_Instanceof';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

abstract class BinaryOp extends Expr {

public Expr $left;

public Expr $right;








public function __construct(Expr $left, Expr $right, array $attributes = []) {
$this->attributes = $attributes;
$this->left = $left;
$this->right = $right;
}

public function getSubNodeNames(): array {
return ['left', 'right'];
}







abstract public function getOperatorSigil(): string;
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class AssignRef extends Expr {

public Expr $var;

public Expr $expr;








public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['var', 'expr'];
}

public function getType(): string {
return 'Expr_AssignRef';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

abstract class AssignOp extends Expr {

public Expr $var;

public Expr $expr;








public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['var', 'expr'];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;

class List_ extends Expr {

public const KIND_LIST = 1; 
public const KIND_ARRAY = 2; 


public array $items;







public function __construct(array $items, array $attributes = []) {
$this->attributes = $attributes;
$this->items = $items;
}

public function getSubNodeNames(): array {
return ['items'];
}

public function getType(): string {
return 'Expr_List';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Include_ extends Expr {
public const TYPE_INCLUDE = 1;
public const TYPE_INCLUDE_ONCE = 2;
public const TYPE_REQUIRE = 3;
public const TYPE_REQUIRE_ONCE = 4;


public Expr $expr;

public int $type;








public function __construct(Expr $expr, int $type, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
$this->type = $type;
}

public function getSubNodeNames(): array {
return ['expr', 'type'];
}

public function getType(): string {
return 'Expr_Include';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Assign extends Expr {

public Expr $var;

public Expr $expr;








public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['var', 'expr'];
}

public function getType(): string {
return 'Expr_Assign';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PreInc extends Expr {

public Expr $var;







public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}

public function getSubNodeNames(): array {
return ['var'];
}

public function getType(): string {
return 'Expr_PreInc';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Pow extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Pow';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Mod extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Mod';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Concat extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Concat';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class ShiftLeft extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_ShiftLeft';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class BitwiseOr extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_BitwiseOr';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class BitwiseXor extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_BitwiseXor';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Coalesce extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Coalesce';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class BitwiseAnd extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_BitwiseAnd';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Mul extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Mul';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class ShiftRight extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_ShiftRight';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Div extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Div';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Plus extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Plus';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Minus extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Minus';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;

abstract class CallLike extends Expr {






abstract public function getRawArgs(): array;




public function isFirstClassCallable(): bool {
$rawArgs = $this->getRawArgs();
return count($rawArgs) === 1 && current($rawArgs) instanceof VariadicPlaceholder;
}






public function getArgs(): array {
assert(!$this->isFirstClassCallable());
return $this->getRawArgs();
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Double extends Cast {

public const KIND_DOUBLE = 1; 
public const KIND_FLOAT = 2; 
public const KIND_REAL = 3; 

public function getType(): string {
return 'Expr_Cast_Double';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class String_ extends Cast {
public function getType(): string {
return 'Expr_Cast_String';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Unset_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Unset';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Object_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Object';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Bool_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Bool';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Int_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Int';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Array_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Array';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;

class New_ extends CallLike {

public Node $class;

public array $args;








public function __construct(Node $class, array $args = [], array $attributes = []) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Ternary extends Expr {

public Expr $cond;

public ?Expr $if;

public Expr $else;









public function __construct(Expr $cond, ?Expr $if, Expr $else, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;

class Throw_ extends Node\Expr {

public Node\Expr $expr;







public function __construct(Node\Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_Throw';
}
}
<?php declare(strict_types=1);

require __DIR__ . '/../ArrayItem.php';
<?php declare(strict_types=1);

require __DIR__ . '/../ClosureUse.php';
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Clone_ extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_Clone';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;

class ArrowFunction extends Expr implements FunctionLike {

public bool $static;


public bool $byRef;


public array $params = [];


public ?Node $returnType;


public Expr $expr;

public array $attrGroups;


















public function __construct(array $subNodes, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Yield_ extends Expr {

public ?Expr $key;

public ?Expr $value;








public function __construct(?Expr $value = null, ?Expr $key = null, array $attributes = []) {
$this->attributes = $attributes;
$this->key = $key;
$this->value = $value;
}

public function getSubNodeNames(): array {
return ['key', 'value'];
}

public function getType(): string {
return 'Expr_Yield';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class UnaryPlus extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_UnaryPlus';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;

class PropertyFetch extends Expr {

public Expr $var;

public Node $name;








public function __construct(Expr $var, $name, array $attributes = []) {
$this->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';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;

class StaticCall extends CallLike {

public Node $class;

public Node $name;

public array $args;









public function __construct(Node $class, $name, array $args = [], array $attributes = []) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;

class FuncCall extends CallLike {

public Node $name;

public array $args;








public function __construct(Node $name, array $args = [], array $attributes = []) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;
use PhpParser\Node\InterpolatedStringPart;

class ShellExec extends Expr {

public array $parts;







public function __construct(array $parts, array $attributes = []) {
$this->attributes = $attributes;
$this->parts = $parts;
}

public function getSubNodeNames(): array {
return ['parts'];
}

public function getType(): string {
return 'Expr_ShellExec';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PostDec extends Expr {

public Expr $var;







public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}

public function getSubNodeNames(): array {
return ['var'];
}

public function getType(): string {
return 'Expr_PostDec';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

abstract class Cast extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Variable extends Expr {

public $name;







public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
}

public function getSubNodeNames(): array {
return ['name'];
}

public function getType(): string {
return 'Expr_Variable';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;

class Array_ extends Expr {

public const KIND_LONG = 1; 
public const KIND_SHORT = 2; 


public array $items;







public function __construct(array $items = [], array $attributes = []) {
$this->attributes = $attributes;
$this->items = $items;
}

public function getSubNodeNames(): array {
return ['items'];
}

public function getType(): string {
return 'Expr_Array';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Print_ extends Expr {

public Expr $expr;







public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}

public function getSubNodeNames(): array {
return ['expr'];
}

public function getType(): string {
return 'Expr_Print';
}
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PreDec extends Expr {

public Expr $var;







public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}

public function getSubNodeNames(): array {
return ['var'];
}

public function getType(): string {
return 'Expr_PreDec';
}
}
<?php declare(strict_types=1);

namespace PhpParser;

abstract class NodeAbstract implements Node, \JsonSerializable {

protected array $attributes;






public function __construct(array $attributes = []) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;

abstract class Declaration implements PhpParser\Builder {

protected array $attributes = [];








abstract public function addStmt($stmt);








public function addStmts(array $stmts) {
foreach ($stmts as $stmt) {
$this->addStmt($stmt);
}

return $this;
}








public function setDocComment($docComment) {
$this->attributes['comments'] = [
BuilderHelpers::normalizeDocComment($docComment)
];

return $this;
}
}
<?php

declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class EnumCase implements PhpParser\Builder {

protected $name;

protected ?Node\Expr $value = null;

protected array $attributes = [];


protected array $attributeGroups = [];






public function __construct($name) {
$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
);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Trait_ extends Declaration {
protected string $name;

protected array $uses = [];

protected array $constants = [];

protected array $properties = [];

protected array $methods = [];

protected array $attributeGroups = [];






public function __construct(string $name) {
$this->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
);
}
}
<?php

declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Const_;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class ClassConst implements PhpParser\Builder {
protected int $flags = 0;

protected array $attributes = [];

protected array $constants = [];


protected array $attributeGroups = [];

protected ?Node $type = null;







public function __construct($name, $value) {
$this->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
);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;

class Param implements PhpParser\Builder {
protected string $name;
protected ?Node\Expr $default = null;

protected ?Node $type = null;
protected bool $byRef = false;
protected int $flags = 0;
protected bool $variadic = false;

protected array $attributeGroups = [];






public function __construct(string $name) {
$this->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
);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;

class Class_ extends Declaration {
protected string $name;
protected ?Name $extends = null;

protected array $implements = [];
protected int $flags = 0;

protected array $uses = [];

protected array $constants = [];

protected array $properties = [];

protected array $methods = [];

protected array $attributeGroups = [];






public function __construct(string $name) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\BuilderHelpers;
use PhpParser\Node;

abstract class FunctionLike extends Declaration {
protected bool $returnByRef = false;

protected array $params = [];


protected ?Node $returnType = null;






public function makeReturnByRef() {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\ComplexType;

class Property implements PhpParser\Builder {
protected string $name;

protected int $flags = 0;

protected ?Node\Expr $default = null;

protected array $attributes = [];

protected ?Node $type = null;

protected array $attributeGroups = [];






public function __construct(string $name) {
$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
);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Namespace_ extends Declaration {
private ?Node\Name $name;

private array $stmts = [];






public function __construct($name) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Use_ implements Builder {
protected Node\Name $name;

protected int $type;
protected ?string $alias = null;







public function __construct($name, int $type) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;

class Interface_ extends Declaration {
protected string $name;

protected array $extends = [];

protected array $constants = [];

protected array $methods = [];

protected array $attributeGroups = [];






public function __construct(string $name) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;

class Enum_ extends Declaration {
protected string $name;
protected ?Identifier $scalarType = null;

protected array $implements = [];

protected array $uses = [];

protected array $enumCases = [];

protected array $constants = [];

protected array $methods = [];

protected array $attributeGroups = [];






public function __construct(string $name) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Method extends FunctionLike {
protected string $name;

protected int $flags = 0;


protected ?array $stmts = [];


protected array $attributeGroups = [];






public function __construct(string $name) {
$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 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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Function_ extends FunctionLike {
protected string $name;

protected array $stmts = [];


protected array $attributeGroups = [];






public function __construct(string $name) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class TraitUse implements Builder {

protected array $traits = [];

protected array $adaptations = [];






public function __construct(...$traits) {
foreach ($traits as $trait) {
$this->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);
}
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class TraitUseAdaptation implements Builder {
private const TYPE_UNDEFINED = 0;
private const TYPE_ALIAS = 1;
private const TYPE_PRECEDENCE = 2;

protected int $type;
protected ?Node\Name $trait;
protected Node\Identifier $method;
protected ?int $modifier = null;
protected ?Node\Identifier $alias = null;

protected array $insteadof = [];







public function __construct($trait, $method) {
$this->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');
}
}
}
<?php declare(strict_types=1);

namespace PhpParser;

interface NodeTraverserInterface {





public function addVisitor(NodeVisitor $visitor): void;




public function removeVisitor(NodeVisitor $visitor): void;








public function traverse(array $nodes): array;
}
<?php declare(strict_types=1);

namespace PhpParser;

interface Builder {





public function getNode(): Node;
}
<?php declare(strict_types=1);

namespace PhpParser;

if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
function defineCompatibilityTokens(): void {
$compatTokens = [

'T_NAME_QUALIFIED',
'T_NAME_FULLY_QUALIFIED',
'T_NAME_RELATIVE',
'T_MATCH',
'T_NULLSAFE_OBJECT_OPERATOR',
'T_ATTRIBUTE',

'T_ENUM',
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
'T_READONLY',
];




$usedTokenIds = [];
foreach ($compatTokens as $token) {
if (\defined($token)) {
$tokenId = \constant($token);
if (!\is_int($tokenId)) {
throw new \Error(sprintf(
'Token %s has ID of type %s, should be int. ' .
'You may be using a library with broken token emulation',
$token, \gettype($tokenId)
));
}
$clashingToken = $usedTokenIds[$tokenId] ?? null;
if ($clashingToken !== null) {
throw new \Error(sprintf(
'Token %s has same ID as token %s, ' .
'you may be using a library with broken token emulation',
$token, $clashingToken
));
}
$usedTokenIds[$tokenId] = $token;
}
}



$newTokenId = -1;
foreach ($compatTokens as $token) {
if (!\defined($token)) {
while (isset($usedTokenIds[$newTokenId])) {
$newTokenId--;
}
\define($token, $newTokenId);
$newTokenId--;
}
}
}

defineCompatibilityTokens();
}
<?php declare(strict_types=1);

namespace PhpParser;






use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Cast\Double;
use PhpParser\Node\Identifier;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\TryCatch;
use PhpParser\Node\UseItem;
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;

abstract class ParserAbstract implements Parser {
private const SYMBOL_NONE = -1;


protected Lexer $lexer;

protected PhpVersion $phpVersion;






protected int $tokenToSymbolMapSize;

protected int $actionTableSize;

protected int $gotoTableSize;


protected int $invalidSymbol;

protected int $errorSymbol;

protected int $defaultAction;

protected int $unexpectedTokenRule;

protected int $YY2TBLSTATE;

protected int $numNonLeafStates;


protected array $phpTokenToSymbol;

protected array $dropTokens;

protected array $tokenToSymbol;

protected array $symbolToName;

protected array $productions;




protected array $actionBase;

protected array $action;


protected array $actionCheck;

protected array $actionDefault;

protected array $reduceCallbacks;



protected array $gotoBase;

protected array $goto;


protected array $gotoCheck;

protected array $gotoDefault;



protected array $ruleToNonTerminal;


protected array $ruleToLength;






protected $semValue;

protected array $semStack;

protected array $tokenStartStack;

protected array $tokenEndStack;


protected ErrorHandler $errorHandler;

protected int $errorState;


protected ?\SplObjectStorage $createdArrays;


protected array $tokens;

protected int $tokenPos;




abstract protected function initReduceCallbacks(): void;














public function __construct(Lexer $lexer, ?PhpVersion $phpVersion = null) {
$this->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;
}
}
<?php declare(strict_types=1);

namespace PhpParser;

interface Node {





public function getType(): string;






public function getSubNodeNames(): array;








public function getLine(): int;








public function getStartLine(): int;








public function getEndLine(): int;










public function getStartTokenPos(): int;










public function getEndTokenPos(): int;








public function getStartFilePos(): int;








public function getEndFilePos(): int;








public function getComments(): array;






public function getDocComment(): ?Comment\Doc;








public function setDocComment(Comment\Doc $docComment): void;






public function setAttribute(string $key, $value): void;




public function hasAttribute(string $key): bool;








public function getAttribute(string $key, $default = null);






public function getAttributes(): array;






public function setAttributes(array $attributes): void;
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class VersionConstraintParser {



public function parse(string $value): VersionConstraint {
if (\strpos($value, '|') !== false) {
return $this->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
);
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class VersionNumber {


private $value;

public function __construct(?int $value) {
$this->value = $value;
}

public function isAny(): bool {
return $this->value === null;
}

public function getValue(): ?int {
return $this->value;
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class Version {

private $originalVersionString;


private $major;


private $minor;


private $patch;


private $preReleaseSuffix;


private $buildMetadata;

public function __construct(string $versionString) {
$this->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?
            (?P<Major>0|[1-9]\d*)
            \\.
            (?P<Minor>0|[1-9]\d*)
            (\\.
                (?P<Patch>0|[1-9]\d*)
            )?
            (?:
                -
                (?<PreReleaseSuffix>(?:(dev|beta|b|rc|alpha|a|patch|p|pl)\.?\d*))
            )?
            (?:
                \\+
                (?P<BuildMetadata>[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);
}
}
<?php declare(strict_types = 1);
namespace PharIo\Version;

class VersionConstraintValue {

private $major;


private $minor;


private $patch;


private $label = '';


private $buildMetaData = '';


private $versionString = '';

public function __construct(string $versionString) {
$this->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);
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class AnyVersionConstraint implements VersionConstraint {
public function complies(Version $version): bool {
return true;
}

public function asString(): string {
return '*';
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

interface VersionConstraint {
public function complies(Version $version): bool;

public function asString(): string;
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class OrVersionConstraintGroup extends AbstractVersionConstraint {

private $constraints = [];





public function __construct($originalValue, array $constraints) {
parent::__construct($originalValue);

$this->constraints = $constraints;
}

public function complies(Version $version): bool {
foreach ($this->constraints as $constraint) {
if ($constraint->complies($version)) {
return true;
}
}

return false;
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class GreaterThanOrEqualToVersionConstraint extends AbstractVersionConstraint {

private $minimalVersion;

public function __construct(string $originalValue, Version $minimalVersion) {
parent::__construct($originalValue);

$this->minimalVersion = $minimalVersion;
}

public function complies(Version $version): bool {
return $version->getVersionString() === $this->minimalVersion->getVersionString()
|| $version->isGreaterThan($this->minimalVersion);
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class AndVersionConstraintGroup extends AbstractVersionConstraint {

private $constraints = [];




public function __construct(string $originalValue, array $constraints) {
parent::__construct($originalValue);

$this->constraints = $constraints;
}

public function complies(Version $version): bool {
foreach ($this->constraints as $constraint) {
if (!$constraint->complies($version)) {
return false;
}
}

return true;
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class SpecificMajorVersionConstraint extends AbstractVersionConstraint {

private $major;

public function __construct(string $originalValue, int $major) {
parent::__construct($originalValue);

$this->major = $major;
}

public function complies(Version $version): bool {
return $version->getMajor()->getValue() === $this->major;
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

abstract class AbstractVersionConstraint implements VersionConstraint {

private $originalValue;

public function __construct(string $originalValue) {
$this->originalValue = $originalValue;
}

public function asString(): string {
return $this->originalValue;
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class SpecificMajorAndMinorVersionConstraint extends AbstractVersionConstraint {

private $major;


private $minor;

public function __construct(string $originalValue, int $major, int $minor) {
parent::__construct($originalValue);

$this->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;
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class ExactVersionConstraint extends AbstractVersionConstraint {
public function complies(Version $version): bool {
$other = $version->getVersionString();

if ($version->hasBuildMetaData()) {
$other .= '+' . $version->getBuildMetaData()->asString();
}

return $this->asString() === $other;
}
}
<?php declare(strict_types = 1);
namespace PharIo\Version;

class PreReleaseSuffix {
private const valueScoreMap = [
'dev' => 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]);
}
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

class BuildMetaData {


private $value;

public function __construct(string $value) {
$this->value = $value;
}

public function asString(): string {
return $this->value;
}

public function equals(BuildMetaData $other): bool {
return $this->asString() === $other->asString();
}
}
<?php declare(strict_types = 1);
namespace PharIo\Version;

class NoPreReleaseSuffixException extends \Exception implements Exception {
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

final class UnsupportedVersionConstraintException extends \RuntimeException implements Exception {
}
<?php declare(strict_types = 1);
namespace PharIo\Version;

class NoBuildMetaDataException extends \Exception implements Exception {
}
<?php declare(strict_types = 1);








namespace PharIo\Version;

use Throwable;

interface Exception extends Throwable {
}
<?php declare(strict_types = 1);
namespace PharIo\Version;

class InvalidPreReleaseSuffixException extends \Exception implements Exception {
}
<?php declare(strict_types = 1);
namespace PharIo\Version;

class InvalidVersionException extends \InvalidArgumentException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use function sprintf;

class ManifestLoader {
public static function fromFile(string $filename): Manifest {
try {
return (new ManifestDocumentMapper())->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
);
}
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class AuthorElementCollection extends ElementCollection {
public function current(): AuthorElement {
return new AuthorElement(
$this->getCurrentElement()
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class PhpElement extends ManifestElement {
public function getVersion(): string {
return $this->getAttributeValue('version');
}

public function hasExtElements(): bool {
return $this->hasChild('ext');
}

public function getExtElements(): ExtElementCollection {
return new ExtElementCollection(
$this->getChildrenByName('ext')
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class CopyrightElement extends ManifestElement {
public function getAuthorElements(): AuthorElementCollection {
return new AuthorElementCollection(
$this->getChildrenByName('author')
);
}

public function getLicenseElement(): LicenseElement {
return new LicenseElement(
$this->getChildByName('license')
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class LicenseElement extends ManifestElement {
public function getType(): string {
return $this->getAttributeValue('type');
}

public function getUrl(): string {
return $this->getAttributeValue('url');
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class BundlesElement extends ManifestElement {
public function getComponentElements(): ComponentElementCollection {
return new ComponentElementCollection(
$this->getChildrenByName('component')
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use DOMDocument;
use DOMElement;
use Throwable;
use function count;
use function file_get_contents;
use function is_file;
use function libxml_clear_errors;
use function libxml_get_errors;
use function libxml_use_internal_errors;
use function sprintf;

class ManifestDocument {
public const XMLNS = 'https://phar.io/xml/manifest/1.0';


private $dom;

public static function fromFile(string $filename): ManifestDocument {
if (!is_file($filename)) {
throw new ManifestDocumentException(
sprintf('File "%s" not found', $filename)
);
}

return self::fromString(
file_get_contents($filename)
);
}

public static function fromString(string $xmlString): ManifestDocument {
$prev = libxml_use_internal_errors(true);
libxml_clear_errors();

try {
$dom = new DOMDocument();
$dom->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;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ContainsElement extends ManifestElement {
public function getName(): string {
return $this->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')
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use DOMElement;
use DOMNodeList;
use Iterator;
use ReturnTypeWillChange;
use function count;
use function get_class;
use function sprintf;


abstract class ElementCollection implements Iterator {

private $nodes = [];


private $position;

public function __construct(DOMNodeList $nodeList) {
$this->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;
}
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class AuthorElement extends ManifestElement {
public function getName(): string {
return $this->getAttributeValue('name');
}

public function getEmail(): string {
return $this->getAttributeValue('email');
}

public function hasEMail(): bool {
return $this->hasAttribute('email');
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ExtensionElement extends ManifestElement {
public function getFor(): string {
return $this->getAttributeValue('for');
}

public function getCompatible(): string {
return $this->getAttributeValue('compatible');
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ExtElementCollection extends ElementCollection {
public function current(): ExtElement {
return new ExtElement(
$this->getCurrentElement()
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ComponentElementCollection extends ElementCollection {
public function current(): ComponentElement {
return new ComponentElement(
$this->getCurrentElement()
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class RequiresElement extends ManifestElement {
public function getPHPElement(): PhpElement {
return new PhpElement(
$this->getChildByName('php')
);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ExtElement extends ManifestElement {
public function getName(): string {
return $this->getAttributeValue('name');
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ComponentElement extends ManifestElement {
public function getName(): string {
return $this->getAttributeValue('name');
}

public function getVersion(): string {
return $this->getAttributeValue('version');
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use DOMElement;
use DOMNodeList;
use function sprintf;

class ManifestElement {
public const XMLNS = 'https://phar.io/xml/manifest/1.0';


private $element;

public function __construct(DOMElement $element) {
$this->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;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Countable;
use IteratorAggregate;
use function count;


class AuthorCollection implements Countable, IteratorAggregate {

private $authors = [];

public function add(Author $author): void {
$this->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);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use const FILTER_VALIDATE_EMAIL;
use function filter_var;

class Email {

private $email;

public function __construct(string $email) {
$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;
}
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Countable;
use IteratorAggregate;
use function count;


class BundledComponentCollection implements Countable, IteratorAggregate {

private $bundledComponents = [];

public function add(BundledComponent $bundledComponent): void {
$this->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);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class License {

private $name;


private $url;

public function __construct(string $name, Url $url) {
$this->name = $name;
$this->url = $url;
}

public function getName(): string {
return $this->name;
}

public function getUrl(): Url {
return $this->url;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use const FILTER_VALIDATE_URL;
use function filter_var;

class Url {

private $url;

public function __construct(string $url) {
$this->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;
}
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Countable;
use IteratorAggregate;
use function count;


class RequirementCollection implements Countable, IteratorAggregate {

private $requirements = [];

public function add(Requirement $requirement): void {
$this->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);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class Library extends Type {
public function isLibrary(): bool {
return true;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use function preg_match;
use function sprintf;

class ApplicationName {

private $name;

public function __construct(string $name) {
$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
);
}
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class CopyrightInformation {

private $authors;


private $license;

public function __construct(AuthorCollection $authors, License $license) {
$this->authors = $authors;
$this->license = $license;
}

public function getAuthors(): AuthorCollection {
return $this->authors;
}

public function getLicense(): License {
return $this->license;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\Version;
use PharIo\Version\VersionConstraint;

class Extension extends Type {

private $application;


private $versionConstraint;

public function __construct(ApplicationName $application, VersionConstraint $versionConstraint) {
$this->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);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Iterator;
use function count;


class RequirementCollectionIterator implements Iterator {

private $requirements;


private $position = 0;

public function __construct(RequirementCollection $requirements) {
$this->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++;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Iterator;
use function count;


class BundledComponentCollectionIterator implements Iterator {

private $bundledComponents;


private $position = 0;

public function __construct(BundledComponentCollection $bundledComponents) {
$this->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++;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\Version;

class Manifest {

private $name;


private $version;


private $type;


private $copyrightInformation;


private $requirements;


private $bundledComponents;

public function __construct(ApplicationName $name, Version $version, Type $type, CopyrightInformation $copyrightInformation, RequirementCollection $requirements, BundledComponentCollection $bundledComponents) {
$this->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);
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\Version;

class BundledComponent {

private $name;


private $version;

public function __construct(string $name, Version $version) {
$this->name = $name;
$this->version = $version;
}

public function getName(): string {
return $this->name;
}

public function getVersion(): Version {
return $this->version;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class PhpExtensionRequirement implements Requirement {

private $extension;

public function __construct(string $extension) {
$this->extension = $extension;
}

public function asString(): string {
return $this->extension;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use function sprintf;

class Author {

private $name;


private $email;

public function __construct(string $name, ?Email $email = null) {
$this->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;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\VersionConstraint;

class PhpVersionRequirement implements Requirement {

private $versionConstraint;

public function __construct(VersionConstraint $versionConstraint) {
$this->versionConstraint = $versionConstraint;
}

public function getVersionConstraint(): VersionConstraint {
return $this->versionConstraint;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

interface Requirement {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class Application extends Type {
public function isApplication(): bool {
return true;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Iterator;
use function count;


class AuthorCollectionIterator implements Iterator {

private $authors;


private $position = 0;

public function __construct(AuthorCollection $authors) {
$this->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++;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\VersionConstraint;

abstract class Type {
public static function application(): Application {
return new Application;
}

public static function library(): Library {
return new Library;
}

public static function extension(ApplicationName $application, VersionConstraint $versionConstraint): Extension {
return new Extension($application, $versionConstraint);
}


public function isApplication(): bool {
return false;
}


public function isLibrary(): bool {
return false;
}


public function isExtension(): bool {
return false;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\AnyVersionConstraint;
use PharIo\Version\Version;
use PharIo\Version\VersionConstraint;
use XMLWriter;
use function count;
use function file_put_contents;
use function str_repeat;


class ManifestSerializer {

private $xmlWriter;

public function serializeToFile(Manifest $manifest, string $filename): void {
file_put_contents(
$filename,
$this->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();
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use PharIo\Version\Exception as VersionException;
use PharIo\Version\Version;
use PharIo\Version\VersionConstraintParser;
use Throwable;
use function sprintf;

class ManifestDocumentMapper {
public function map(ManifestDocument $document): Manifest {
try {
$contains = $document->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
);
}
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use RuntimeException;

class ManifestDocumentMapperException extends RuntimeException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use LibXMLError;
use function sprintf;

class ManifestDocumentLoadingException extends \Exception implements Exception {

private $libxmlErrors;






public function __construct(array $libxmlErrors) {
$this->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;
}
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use InvalidArgumentException;

class NoEmailAddressException extends InvalidArgumentException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use RuntimeException;

class ManifestElementException extends RuntimeException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use InvalidArgumentException;

class InvalidEmailException extends InvalidArgumentException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use InvalidArgumentException;

class InvalidUrlException extends InvalidArgumentException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use InvalidArgumentException;

class InvalidApplicationNameException extends InvalidArgumentException implements Exception {
public const InvalidFormat = 2;
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use Throwable;

interface Exception extends Throwable {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use RuntimeException;

class ManifestDocumentException extends RuntimeException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

use InvalidArgumentException;

class ElementCollectionException extends InvalidArgumentException implements Exception {
}
<?php declare(strict_types = 1);









namespace PharIo\Manifest;

class ManifestLoaderException extends \Exception implements Exception {
}
<?php
namespace PharIo\CSFixer;

use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\Token;











class PhpdocSingleLineVarFixer implements FixerInterface {

public function getDefinition(): FixerDefinition {
return new FixerDefinition(
'`@var` annotation must be in single line when is the only content.',
[new CodeSample('<?php
                    /**
                     * @var string
                     */
                ')]
);
}

public function isCandidate(Tokens $tokens): bool {
return $tokens->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;
}

}
<?php









namespace Behat\Gherkin;

use Behat\Gherkin\Exception\LexerException;
use Behat\Gherkin\Exception\NodeException;
use Behat\Gherkin\Exception\ParserException;
use Behat\Gherkin\Node\BackgroundNode;
use Behat\Gherkin\Node\ExampleTableNode;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\OutlineNode;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\ScenarioInterface;
use Behat\Gherkin\Node\ScenarioNode;
use Behat\Gherkin\Node\StepNode;
use Behat\Gherkin\Node\TableNode;










class Parser
{
private $lexer;
private $input;
private $file;
private $tags = array();
private $languageSpecifierLine;

private $passedNodesStack = array();






public function __construct(Lexer $lexer)
{
$this->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
);
}
}
<?php









namespace Behat\Gherkin\Loader;

use Behat\Gherkin\Node\BackgroundNode;
use Behat\Gherkin\Node\ExampleTableNode;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\OutlineNode;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\ScenarioNode;
use Behat\Gherkin\Node\StepNode;
use Behat\Gherkin\Node\TableNode;






class ArrayLoader implements LoaderInterface
{







public function supports($resource)
{
return is_array($resource) && (isset($resource['features']) || isset($resource['feature']));
}








public function load($resource)
{
$features = array();

if (isset($resource['features'])) {
foreach ($resource['features'] as $iterator => $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;
}
}
<?php









namespace Behat\Gherkin\Loader;

use Behat\Gherkin\Node\FeatureNode;
use Symfony\Component\Yaml\Yaml;






class YamlFileLoader extends AbstractFileLoader
{
private $loader;

public function __construct()
{
$this->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);
}
}
<?php









namespace Behat\Gherkin\Loader;






interface FileLoaderInterface extends LoaderInterface
{





public function setBasePath($path);
}
<?php









namespace Behat\Gherkin\Loader;

use Behat\Gherkin\Node\FeatureNode;






interface LoaderInterface
{







public function supports($resource);








public function load($resource);
}
<?php









namespace Behat\Gherkin\Loader;

use Behat\Gherkin\Gherkin;
use Behat\Gherkin\Node\FeatureNode;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;






class DirectoryLoader extends AbstractFileLoader
{
protected $gherkin;






public function __construct(Gherkin $gherkin)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Loader;

use Behat\Gherkin\Cache\CacheInterface;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Parser;






class GherkinFileLoader extends AbstractFileLoader
{
protected $parser;
protected $cache;







public function __construct(Parser $parser, CacheInterface $cache = null)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Loader;






abstract class AbstractFileLoader implements FileLoaderInterface
{
protected $basePath;






public function setBasePath($path)
{
$this->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;
}
}
<?php

namespace Behat\Gherkin\Loader;

use Behat\Gherkin\Node\BackgroundNode;
use Behat\Gherkin\Node\ExampleTableNode;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\OutlineNode;
use Behat\Gherkin\Node\ScenarioInterface;
use Behat\Gherkin\Node\ScenarioNode;
use Behat\Gherkin\Node\StepNode;




class CucumberNDJsonAstLoader implements LoaderInterface
{

public function supports($resource)
{
return is_string($resource);
}

public function load($resource)
{
return array_values(array_filter(array_map(
static function ($line) use ($resource) {
return self::getFeature(json_decode($line, true), $resource);
},
file($resource)
)));
}




private static function getFeature(array $json, $filePath)
{
if (!isset($json['gherkinDocument']['feature'])) {
return null;
}

$featureJson = $json['gherkinDocument']['feature'];

$feature = new FeatureNode(
isset($featureJson['name']) ? $featureJson['name'] : null,
$featureJson['description'] ? trim($featureJson['description']) : null,
self::getTags($featureJson),
self::getBackground($featureJson),
self::getScenarios($featureJson),
$featureJson['keyword'],
$featureJson['language'],
preg_replace('/(?<=\\.feature).*$/', '', $filePath),
$featureJson['location']['line']
);

return $feature;
}




private static function getTags(array $json)
{
return array_map(
static function(array $tag) { return preg_replace('/^@/', '', $tag['name']); },
isset($json['tags']) ? $json['tags'] : []
);
}




private static function getScenarios(array $json)
{

return array_values(
array_map(
static function ($child) {

if ($child['scenario']['examples']) {
return new OutlineNode(
isset($child['scenario']['name']) ? $child['scenario']['name'] : null,
self::getTags($child['scenario']),
self::getSteps(isset($child['scenario']['steps']) ? $child['scenario']['steps'] : []),
self::getTables($child['scenario']['examples']),
$child['scenario']['keyword'],
$child['scenario']['location']['line']
);
}
else {
return new ScenarioNode(
$child['scenario']['name'],
self::getTags($child['scenario']),
self::getSteps(isset($child['scenario']['steps']) ? $child['scenario']['steps'] : []),
$child['scenario']['keyword'],
$child['scenario']['location']['line']
);
}

},
array_filter(
isset($json['children']) ? $json['children'] : [],
static function ($child) {
return isset($child['scenario']);
}
)
)
);
}




private static function getBackground(array $json)
{
$backgrounds = array_values(
array_map(
static function ($child) {
return new BackgroundNode(
$child['background']['name'],
self::getSteps(isset($child['background']['steps']) ? $child['background']['steps'] : []),
$child['background']['keyword'],
$child['background']['location']['line']
);
},
array_filter(
isset($json['children']) ? $json['children'] : [],
static function ($child) {
return isset($child['background']);
}
)
)
);

return count($backgrounds) == 1 ? $backgrounds[0] : null;
}




private static function getSteps(array $json)
{
return array_map(
static function(array $json) {
return new StepNode(
trim($json['keyword']),
$json['text'],
[],
$json['location']['line'],
trim($json['keyword'])
);
},
$json
);
}




private static function getTables(array $json)
{
return array_map(
static function($tableJson) {

$table = [];

$table[$tableJson['tableHeader']['location']['line']] = array_map(
static function($cell) {
return $cell['value'];
},
$tableJson['tableHeader']['cells']
);

foreach ($tableJson['tableBody'] as $bodyRow) {
$table[$bodyRow['location']['line']] = array_map(
static function($cell) {
return $cell['value'];
},
$bodyRow['cells']
);
}

return new ExampleTableNode(
$table,
$tableJson['keyword'],
self::getTags($tableJson)
);
},
$json
);
}
}
<?php









namespace Behat\Gherkin;

use Behat\Gherkin\Exception\LexerException;
use Behat\Gherkin\Keywords\KeywordsInterface;






class Lexer
{
private $language;
private $lines;
private $linesCount;
private $line;
private $trimmedLine;
private $lineNumber;
private $eos;
private $keywords;
private $keywordsCache = array();
private $stepKeywordTypesCache = array();
private $deferredObjects = array();
private $deferredObjectsCount = 0;
private $stashedToken;
private $inPyString = false;
private $pyStringSwallow = 0;
private $featureStarted = false;
private $allowMultilineArguments = false;
private $allowSteps = false;
private $pyStringDelimiter = null;






public function __construct(KeywordsInterface $keywords)
{
$this->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*(?<delimiter>"""|```)/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('/(?<!\\\)\|/u', $line));
$token['columns'] = $columns;

$this->consumeLine();

return $token;
}






protected function scanTags()
{
$line = $this->getTrimmedLine();

if (!isset($line[0]) || '@' !== $line[0]) {
return null;
}

if(preg_match('/^(?<line>.*)\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';
}
}
<?php









namespace Behat\Gherkin\Cache;

use Behat\Gherkin\Node\FeatureNode;







class MemoryCache implements CacheInterface
{
private $features = array();
private $timestamps = array();









public function isFresh($path, $timestamp)
{
if (!isset($this->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();
}
}
<?php









namespace Behat\Gherkin\Cache;

use Behat\Gherkin\Node\FeatureNode;






interface CacheInterface
{








public function isFresh($path, $timestamp);








public function read($path);







public function write($path, FeatureNode $feature);
}
<?php









namespace Behat\Gherkin\Cache;

use Behat\Gherkin\Exception\CacheException;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Gherkin;







class FileCache implements CacheInterface
{
private $path;








public function __construct($path)
{
$this->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';
}
}
<?php









namespace Behat\Gherkin;

use Behat\Gherkin\Filter\FeatureFilterInterface;
use Behat\Gherkin\Filter\LineFilter;
use Behat\Gherkin\Filter\LineRangeFilter;
use Behat\Gherkin\Loader\FileLoaderInterface;
use Behat\Gherkin\Loader\LoaderInterface;






class Gherkin
{
const VERSION = '4.8.0';




protected $loaders = array();



protected $filters = array();






public function addLoader(LoaderInterface $loader)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






class OutlineNode implements ScenarioInterface
{



private $title;



private $tags;



private $steps;



private $tables;



private $keyword;



private $line;



private $examples;











public function __construct(
$title,
array $tags,
array $steps,
$tables,
$keyword,
$line
) {
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






interface ArgumentInterface extends NodeInterface
{
}
<?php









namespace Behat\Gherkin\Node;






class FeatureNode implements KeywordNodeInterface, TaggedNodeInterface
{



private $title;



private $description;



private $tags = array();



private $background;



private $scenarios = array();



private $keyword;



private $language;



private $file;



private $line;














public function __construct(
$title,
$description,
array $tags,
BackgroundNode $background = null,
array $scenarios,
$keyword,
$language,
$file,
$line
) {

if (!empty($file) && !$this->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)
;
}
}
<?php









namespace Behat\Gherkin\Node;






interface NodeInterface
{





public function getNodeType();






public function getLine();
}
<?php









namespace Behat\Gherkin\Node;






class ExampleTableNode extends TableNode
{



private $tags;




private $keyword;








public function __construct(array $table, $keyword, array $tags = array())
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;

use Behat\Gherkin\Exception\NodeException;






class StepNode implements NodeInterface
{



private $keyword;



private $keywordType;



private $text;



private $arguments = array();



private $line;










public function __construct($keyword, $text, array $arguments, $line, $keywordType = null)
{
if (count($arguments) > 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;
}
}
<?php









namespace Behat\Gherkin\Node;






class PyStringNode implements ArgumentInterface
{



private $strings = array();



private $line;







public function __construct(array $strings, $line)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






class ScenarioNode implements ScenarioInterface
{



private $title;



private $tags = array();



private $steps = array();



private $keyword;



private $line;










public function __construct($title, array $tags, array $steps, $keyword, $line)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






interface ScenarioInterface extends ScenarioLikeInterface, TaggedNodeInterface
{
}
<?php









namespace Behat\Gherkin\Node;

use ArrayIterator;
use Behat\Gherkin\Exception\NodeException;
use Iterator;
use IteratorAggregate;
use ReturnTypeWillChange;






class TableNode implements ArgumentInterface, IteratorAggregate
{



private $table;



private $maxLineLength = array();








public function __construct(array $table)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






interface ScenarioLikeInterface extends KeywordNodeInterface, StepContainerInterface
{
}
<?php









namespace Behat\Gherkin\Node;






interface StepContainerInterface extends NodeInterface
{





public function hasSteps();






public function getSteps();
}
<?php









namespace Behat\Gherkin\Node;






class ExampleNode implements ScenarioInterface
{



private $title;



private $tags;



private $outlineSteps;



private $tokens;



private $line;



private $steps;



private $outlineTitle;











public function __construct($title, array $tags, $outlineSteps, array $tokens, $line, $outlineTitle = null)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






interface KeywordNodeInterface extends NodeInterface
{





public function getKeyword();






public function getTitle();
}
<?php









namespace Behat\Gherkin\Node;






class BackgroundNode implements ScenarioLikeInterface
{



private $title;



private $steps = array();



private $keyword;



private $line;









public function __construct($title, array $steps, $keyword, $line)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Node;






interface TaggedNodeInterface extends NodeInterface
{







public function hasTag($tag);






public function hasTags();






public function getTags();
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\ScenarioInterface;






class RoleFilter extends SimpleFilter
{
protected $pattern;






public function __construct($role)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\OutlineNode;
use Behat\Gherkin\Node\ScenarioInterface;






class TagFilter extends ComplexFilter
{
protected $filterString;






public function __construct($filterString)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;






abstract class SimpleFilter implements FilterInterface
{







public function filterFeature(FeatureNode $feature)
{
if ($this->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()
);
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;






interface FeatureFilterInterface
{







public function isFeatureMatch(FeatureNode $feature);








public function filterFeature(FeatureNode $feature);
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\ScenarioInterface;






class NameFilter extends SimpleFilter
{
protected $filterString;






public function __construct($filterString)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\ExampleTableNode;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\OutlineNode;
use Behat\Gherkin\Node\ScenarioInterface;






class LineRangeFilter implements FilterInterface
{
protected $filterMinLine;
protected $filterMaxLine;







public function __construct($filterMinLine, $filterMaxLine)
{
$this->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()
);
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\ScenarioInterface;
use Behat\Gherkin\Node\FeatureNode;






class NarrativeFilter extends SimpleFilter
{



private $regex;






public function __construct($regex)
{
$this->regex = $regex;
}








public function isFeatureMatch(FeatureNode $feature)
{
return 1 === preg_match($this->regex, $feature->getDescription() ?? '');
}








public function isScenarioMatch(ScenarioInterface $scenario)
{
return false;
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;






abstract class ComplexFilter implements ComplexFilterInterface
{







public function filterFeature(FeatureNode $feature)
{
$scenarios = array();
foreach ($feature->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()
);
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\ScenarioInterface;






interface ComplexFilterInterface extends FeatureFilterInterface
{








public function isScenarioMatch(FeatureNode $feature, ScenarioInterface $scenario);
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\ScenarioInterface;






interface FilterInterface extends FeatureFilterInterface
{







public function isScenarioMatch(ScenarioInterface $scenario);
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\ScenarioInterface;






class PathsFilter extends SimpleFilter
{
protected $filterPaths = array();






public function __construct(array $paths)
{
$this->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;
}
}
<?php









namespace Behat\Gherkin\Filter;

use Behat\Gherkin\Node\ExampleTableNode;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Node\OutlineNode;
use Behat\Gherkin\Node\ScenarioInterface;






class LineFilter implements FilterInterface
{
protected $filterLine;






public function __construct($filterLine)
{
$this->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()
);
}
}
<?php









namespace Behat\Gherkin\Keywords;






class KeywordsDumper
{
private $keywords;
private $keywordsDumper;






public function __construct(KeywordsInterface $keywords)
{
$this->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 = <<<GHERKIN
{$keyword}: Internal operations
  In order to stay secret
  As a secret organization
  We need to be able to erase past agents' memory


GHERKIN;


$keywords = explode('|', $this->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 = <<<GHERKIN
  {$keyword}:

GHERKIN;


$dump .= $this->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 = <<<GHERKIN
  {$keyword}: Erasing agent memory

GHERKIN;


$dump .= $this->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 = <<<GHERKIN
  {$keyword}: Erasing other agents' memory

GHERKIN;


$dump .= $this->dumpStep(
$this->keywords->getGivenKeywords(),
'there is agent <agent1>',
$short,
$excludeAsterisk
);


$dump .= $this->dumpStep(
$this->keywords->getAndKeywords(),
'there is agent <agent2>',
$short,
$excludeAsterisk
);


$dump .= $this->dumpStep(
$this->keywords->getWhenKeywords(),
'I erase agent <agent2>\'s memory',
$short,
$excludeAsterisk
);


$dump .= $this->dumpStep(
$this->keywords->getThenKeywords(),
'there should be agent <agent1>',
$short,
$excludeAsterisk
);


$dump .= $this->dumpStep(
$this->keywords->getButKeywords(),
'there should not be agent <agent2>',
$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 .= <<<GHERKIN

    {$keyword}:
      | agent1 | agent2 |
      | D      | M      |

GHERKIN;

return $dump . "\n";
}










protected function dumpStep($keywords, $text, $short = true, $excludeAsterisk = false)
{
$dump = '';

$keywords = explode('|', $keywords);
if ($short) {
$keywords = array_map(
function ($keyword) {
return str_replace('<', '', $keyword);
},
$keywords
);
$keywords = call_user_func($this->keywordsDumper, $keywords, $short);
$dump .= <<<GHERKIN
    {$keywords} {$text}

GHERKIN;
} else {
foreach ($keywords as $keyword) {
if ($excludeAsterisk && '*' === $keyword) {
continue;
}

$indent = ' ';
if (false !== mb_strpos($keyword, '<', 0, 'utf8')) {
$keyword = mb_substr($keyword, 0, -1, 'utf8');
$indent = '';
}
$keyword = call_user_func($this->keywordsDumper, array($keyword), $short);
$dump .= <<<GHERKIN
    {$keyword}{$indent}{$text}

GHERKIN;
}
}

return $dump;
}
}
<?php









namespace Behat\Gherkin\Keywords;






interface KeywordsInterface
{





public function setLanguage($language);






public function getFeatureKeywords();






public function getBackgroundKeywords();






public function getScenarioKeywords();






public function getOutlineKeywords();






public function getExamplesKeywords();






public function getGivenKeywords();






public function getWhenKeywords();






public function getThenKeywords();






public function getAndKeywords();






public function getButKeywords();






public function getStepKeywords();
}
<?php









namespace Behat\Gherkin\Keywords;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;








class CucumberKeywords extends ArrayKeywords
{





public function __construct($yaml)
{

$file = null;
if (strpos($yaml, "\n") === false && is_file($yaml)) {
if (false === is_readable($yaml)) {
throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $yaml));
}

$file = $yaml;
$yaml = file_get_contents($file);
}

try {
$content = Yaml::parse($yaml);
} catch (ParseException $e) {
if ($file) {
$e->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;
}
}
<?php









namespace Behat\Gherkin\Keywords;

































class ArrayKeywords implements KeywordsInterface
{
private $keywords = array();
private $keywordString = array();
private $language;






public function __construct(array $keywords)
{
$this->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];
}
}
<?php









namespace Behat\Gherkin\Keywords;








class CachedArrayKeywords extends ArrayKeywords
{





public function __construct($file)
{
parent::__construct(include($file));
}
}
<?php









namespace Behat\Gherkin\Exception;

use RuntimeException;






class CacheException extends RuntimeException implements Exception
{
}
<?php









namespace Behat\Gherkin\Exception;

use RuntimeException;

class NodeException extends RuntimeException implements Exception
{
}
<?php









namespace Behat\Gherkin\Exception;

interface Exception
{
}
<?php









namespace Behat\Gherkin\Exception;

use RuntimeException;

class ParserException extends RuntimeException implements Exception
{
}
<?php









namespace Behat\Gherkin\Exception;

use RuntimeException;

class LexerException extends RuntimeException implements Exception
{
}
<?php

return __DIR__;<?php










return array (
'en' => 
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' => '當<|*',
),
);<?php








namespace JsonSchema\Uri;

use JsonSchema\Exception\UriResolverException;
use JsonSchema\UriResolverInterface;






class UriResolver implements UriResolverInterface
{







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) && 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('|((?<!\.)\./)*|', '', $path);
$path = preg_replace('|//|', '/', $path);

return $path;
}






public function isValid($uri)
{
$components = $this->parse($uri);

return !empty($components);
}
}
<?php

namespace JsonSchema\Uri\Retrievers;

use JsonSchema\Validator;













class PredefinedArray extends AbstractRetriever
{





private $schemas;







public function __construct(array $schemas, $contentType = Validator::SCHEMA_MEDIA_TYPE)
{
$this->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];
}
}
<?php








namespace JsonSchema\Uri\Retrievers;






interface UriRetrieverInterface
{









public function retrieve($uri);






public function getContentType();
}
<?php






namespace JsonSchema\Uri\Retrievers;







abstract class AbstractRetriever implements UriRetrieverInterface
{





protected $contentType;






public function getContentType()
{
return $this->contentType;
}
}
<?php








namespace JsonSchema\Uri\Retrievers;

use JsonSchema\Exception\RuntimeException;
use JsonSchema\Validator;






class Curl extends AbstractRetriever
{
protected $messageBody;

public function __construct()
{
if (!function_exists('curl_init')) {

throw new RuntimeException('cURL not installed'); 
}
}






public function retrieve($uri)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: ' . Validator::SCHEMA_MEDIA_TYPE));

$response = curl_exec($ch);
if (false === $response) {
throw new \JsonSchema\Exception\ResourceNotFoundException('JSON schema not found');
}

$this->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;
}
}
<?php








namespace JsonSchema\Uri\Retrievers;

use JsonSchema\Exception\ResourceNotFoundException;






class FileGetContents extends AbstractRetriever
{
protected $messageBody;






public function retrieve($uri)
{
$errorMessage = null;
set_error_handler(function ($errno, $errstr) use (&$errorMessage) {
$errorMessage = $errstr;
});
$response = file_get_contents($uri);
restore_error_handler();

if ($errorMessage) {
throw new ResourceNotFoundException($errorMessage);
}

if (false === $response) {
throw new ResourceNotFoundException('JSON schema not found at ' . $uri);
}

if ($response == ''
&& substr($uri, 0, 7) == 'file://' && substr($uri, -1) == '/'
) {
throw new ResourceNotFoundException('JSON schema not found at ' . $uri);
}

$this->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;
}
}
<?php








namespace JsonSchema\Uri;

use JsonSchema\Exception\InvalidSchemaMediaTypeException;
use JsonSchema\Exception\JsonDecodingException;
use JsonSchema\Exception\ResourceNotFoundException;
use JsonSchema\Uri\Retrievers\FileGetContents;
use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
use JsonSchema\UriRetrieverInterface as BaseUriRetrieverInterface;
use JsonSchema\Validator;






class UriRetriever implements BaseUriRetrieverInterface
{



protected $translationMap = array(

'|^https?://json-schema.org/draft-(0[34])/schema#?|' => '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;
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;







abstract class Constraint extends BaseConstraint implements ConstraintInterface
{
protected $inlineSchemaProperty = '$schema';

const CHECK_MODE_NONE = 0x00000000;
const CHECK_MODE_NORMAL = 0x00000001;
const CHECK_MODE_TYPE_CAST = 0x00000002;
const CHECK_MODE_COERCE_TYPES = 0x00000004;
const CHECK_MODE_APPLY_DEFAULTS = 0x00000008;
const CHECK_MODE_EXCEPTIONS = 0x00000010;
const CHECK_MODE_DISABLE_FORMAT = 0x00000020;
const CHECK_MODE_ONLY_REQUIRED_DEFAULTS = 0x00000080;
const CHECK_MODE_VALIDATE_SCHEMA = 0x00000100;









protected function incrementPath(JsonPointer $path = null, $i)
{
$path = $path ?: new JsonPointer('');

if ($i === null || $i === '') {
return $path;
}

$path = $path->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), '.');
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;







class NumberConstraint extends Constraint
{



public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
{

if (isset($schema->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;
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Exception\InvalidArgumentException;
use JsonSchema\SchemaStorage;
use JsonSchema\SchemaStorageInterface;
use JsonSchema\Uri\UriRetriever;
use JsonSchema\UriRetrieverInterface;
use JsonSchema\Validator;




class Factory
{



protected $schemaStorage;




protected $uriRetriever;




private $checkMode = Constraint::CHECK_MODE_NORMAL;




private $typeCheck = array();




protected $errorContext = Validator::ERROR_DOCUMENT_VALIDATION;




protected $constraintMap = array(
'array' => '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;
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;
use JsonSchema\Exception\InvalidArgumentException;
use JsonSchema\Exception\InvalidSchemaException;
use JsonSchema\Exception\RuntimeException;
use JsonSchema\Validator;







class SchemaConstraint extends Constraint
{
const DEFAULT_SCHEMA_SPEC = 'http://json-schema.org/draft-04/schema#';




public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
{
if ($schema !== null) {

$validationSchema = $schema;
} elseif ($this->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);
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;
use JsonSchema\Exception\InvalidArgumentException;
use UnexpectedValueException as StandardUnexpectedValueException;







class TypeConstraint extends Constraint
{



public static $wording = array(
'integer' => '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;
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;







class StringConstraint extends Constraint
{



public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
{

if (isset($schema->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); 
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;







class ObjectConstraint extends Constraint
{



protected $appliedDefaults = array();




public function check(&$element, $schema = null, JsonPointer $path = null, $properties = null,
$additionalProp = null, $patternProperties = null, $appliedDefaults = array())
{
if ($element instanceof UndefinedConstraint) {
return;
}

$this->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));
}
}
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;







class CollectionConstraint extends Constraint
{



public function check(&$value, $schema = null, JsonPointer $path = null, $i = null)
{

if (isset($schema->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);
}
}
}
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Constraints\TypeCheck\LooseTypeCheck;
use JsonSchema\Entity\JsonPointer;
use JsonSchema\Exception\ValidationException;
use JsonSchema\Uri\UriResolver;







#[\AllowDynamicProperties]
class UndefinedConstraint extends Constraint
{



protected $appliedDefaults = array();




public function check(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false)
{
if (is_null($schema) || !is_object($schema)) {
return;
}

$path = $this->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;
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;
use JsonSchema\Exception\InvalidArgumentException;
use JsonSchema\Exception\ValidationException;
use JsonSchema\Validator;





class BaseConstraint
{



protected $errors = array();




protected $errorMask = Validator::ERROR_NONE;




protected $factory;




public function __construct(Factory $factory = null)
{
$this->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);
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;






interface ConstraintInterface
{





public function getErrors();






public function addErrors(array $errors);









public function addError(JsonPointer $path = null, $message, $constraint='', array $more = null);






public function isValid();













public function check(&$value, $schema = null, JsonPointer $path = null, $i = null);
}
<?php

namespace JsonSchema\Constraints\TypeCheck;

class LooseTypeCheck implements TypeCheckInterface
{
public static function isObject($value)
{
return
is_object($value) ||
(is_array($value) && (count($value) == 0 || self::isAssociativeArray($value)));
}

public static function isArray($value)
{
return
is_array($value) &&
(count($value) == 0 || !self::isAssociativeArray($value));
}

public static function propertyGet($value, $property)
{
if (is_object($value)) {
return $value->{$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);
}
}
<?php

namespace JsonSchema\Constraints\TypeCheck;

class StrictTypeCheck implements TypeCheckInterface
{
public static function isObject($value)
{
return is_object($value);
}

public static function isArray($value)
{
return is_array($value);
}

public static function propertyGet($value, $property)
{
return $value->{$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));
}
}
<?php

namespace JsonSchema\Constraints\TypeCheck;

interface TypeCheckInterface
{
public static function isObject($value);

public static function isArray($value);

public static function propertyGet($value, $property);

public static function propertySet(&$value, $property, $data);

public static function propertyExists($value, $property);

public static function propertyCount($value);
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;







class EnumConstraint extends Constraint
{



public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
{

if ($element instanceof UndefinedConstraint && (!isset($schema->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));
}
}
<?php








namespace JsonSchema\Constraints;

use JsonSchema\Entity\JsonPointer;
use JsonSchema\Rfc3339;








class FormatConstraint extends Constraint
{



public function check(&$element, $schema = null, JsonPointer $path = null, $i = null)
{
if (!isset($schema->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);
}
}
<?php

namespace JsonSchema;

interface SchemaStorageInterface
{






public function addSchema($id, $schema = null);








public function getSchema($id);








public function resolveRef($ref);








public function resolveRefSchema($refSchema);
}
<?php

namespace JsonSchema;

class Rfc3339
{
const REGEX = '/^(\d{4}-\d{2}-\d{2}[T ]{1}\d{2}:\d{2}:\d{2})(\.\d+)?(Z|([+-]\d{2}):?(\d{2}))$/';








public static function createFromString($string)
{
if (!preg_match(self::REGEX, strtoupper($string), $matches)) {
return null;
}

$dateAndTime = $matches[1];
$microseconds = $matches[2] ?: '.000000';
$timeZone = 'Z' !== $matches[3] ? $matches[4] . ':' . $matches[5] : '+00:00';
$dateFormat = strpos($dateAndTime, 'T') === false ? 'Y-m-d H:i:s.uP' : 'Y-m-d\TH:i:s.uP';
$dateTime = \DateTime::createFromFormat($dateFormat, $dateAndTime . $microseconds . $timeZone, new \DateTimeZone('UTC'));

return $dateTime ?: null;
}
}
<?php








namespace JsonSchema;




interface UriRetrieverInterface
{








public function retrieve($uri, $baseUri = null);
}
<?php

namespace JsonSchema;

use JsonSchema\Constraints\BaseConstraint;
use JsonSchema\Entity\JsonPointer;
use JsonSchema\Exception\UnresolvableJsonPointerException;
use JsonSchema\Uri\UriResolver;
use JsonSchema\Uri\UriRetriever;

class SchemaStorage implements SchemaStorageInterface
{
const INTERNAL_PROVIDED_SCHEMA_URI = 'internal://provided-schema/';

protected $uriRetriever;
protected $uriResolver;
protected $schemas = array();

public function __construct(
UriRetrieverInterface $uriRetriever = null,
UriResolverInterface $uriResolver = null
) {
$this->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;
}
}
<?php








namespace JsonSchema;

use JsonSchema\Constraints\BaseConstraint;
use JsonSchema\Constraints\Constraint;









class Validator extends BaseConstraint
{
const SCHEMA_MEDIA_TYPE = 'application/schema+json';

const ERROR_NONE = 0x00000000;
const ERROR_ALL = 0xFFFFFFFF;
const ERROR_DOCUMENT_VALIDATION = 0x00000001;
const ERROR_SCHEMA_VALIDATION = 0x00000002;








public function validate(&$value, $schema = null, $checkMode = null)
{

if (is_array($schema)) {
$schema = self::arrayToObjectRecursive($schema);
}


$initialCheckMode = $this->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);
}
}
<?php








namespace JsonSchema\Entity;

use JsonSchema\Exception\InvalidArgumentException;






class JsonPointer
{

private $filename;


private $propertyPaths = array();




private $fromDefault = false;






public function __construct($value)
{
if (!is_string($value)) {
throw new InvalidArgumentException('Ref value must be a string');
}

$splitRef = explode('#', $value, 2);
$this->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;
}
}
<?php








namespace JsonSchema;




interface UriResolverInterface
{








public function resolve($uri, $baseUri = null);
}
<?php








namespace JsonSchema\Iterator;






class ObjectIterator implements \Iterator, \Countable
{

private $object;


private $position = 0;


private $data = array();


private $initialized = false;




public function __construct($object)
{
$this->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;
}
}
<?php








namespace JsonSchema\Exception;




class InvalidConfigException extends RuntimeException
{
}
<?php

namespace JsonSchema\Exception;

interface ExceptionInterface
{
}
<?php








namespace JsonSchema\Exception;




class ResourceNotFoundException extends RuntimeException
{
}
<?php








namespace JsonSchema\Exception;




class JsonDecodingException extends RuntimeException
{
public function __construct($code = JSON_ERROR_NONE, \Exception $previous = null)
{
switch ($code) {
case JSON_ERROR_DEPTH:
$message = 'The maximum stack depth has been exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$message = 'Invalid or malformed JSON';
break;
case JSON_ERROR_CTRL_CHAR:
$message = 'Control character error, possibly incorrectly encoded';
break;
case JSON_ERROR_UTF8:
$message = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
case JSON_ERROR_SYNTAX:
$message = 'JSON syntax is malformed';
break;
default:
$message = 'Syntax error';
}
parent::__construct($message, $code, $previous);
}
}
<?php








namespace JsonSchema\Exception;




class InvalidSchemaException extends RuntimeException
{
}
<?php








namespace JsonSchema\Exception;




class InvalidSchemaMediaTypeException extends RuntimeException
{
}
<?php








namespace JsonSchema\Exception;






class UnresolvableJsonPointerException extends InvalidArgumentException
{
}
<?php








namespace JsonSchema\Exception;




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








namespace JsonSchema\Exception;




class UriResolverException extends RuntimeException
{
}
<?php








namespace JsonSchema\Exception;




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








namespace JsonSchema\Exception;

class ValidationException extends RuntimeException
{
}
<?php








namespace JsonSchema\Exception;




class InvalidSourceUriException extends InvalidArgumentException
{
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class NamespaceUriException extends Exception {
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class TokenCollection implements \ArrayAccess, \Iterator, \Countable {


private $tokens = [];


private $pos;

public function addToken(Token $token): void {
$this->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]);
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

use DOMDocument;

class XMLSerializer {


private $writer;


private $previousToken;


private $xmlns;






public function __construct(?NamespaceUri $xmlns = null) {
if ($xmlns === null) {
$xmlns = new NamespaceUri('https://github.com/theseer/tokenizer');
}
$this->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();
}
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class NamespaceUri {


private $value;

public function __construct(string $value) {
$this->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)
);
}
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class Tokenizer {






private $map = [
'(' => '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;
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class Exception extends \Exception {
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class TokenCollectionException extends Exception {
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;

class Token {


private $line;


private $name;


private $value;




public function __construct(int $line, string $name, string $value) {
$this->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;
}
}
<?php

namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;








interface RequestExceptionInterface extends ClientExceptionInterface
{







public function getRequest(): RequestInterface;
}
<?php

namespace Psr\Http\Client;




interface ClientExceptionInterface extends \Throwable
{
}
<?php

namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface ClientInterface
{









public function sendRequest(RequestInterface $request): ResponseInterface;
}
<?php

namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;








interface NetworkExceptionInterface extends ClientExceptionInterface
{







public function getRequest(): RequestInterface;
}
<?php
declare(strict_types=1);

namespace Psr\EventDispatcher;








interface StoppableEventInterface
{










public function isPropagationStopped() : bool;
}
<?php
declare(strict_types=1);

namespace Psr\EventDispatcher;




interface EventDispatcherInterface
{









public function dispatch(object $event);
}
<?php
declare(strict_types=1);

namespace Psr\EventDispatcher;




interface ListenerProviderInterface
{







public function getListenersForEvent(object $event) : iterable;
}
<?php

namespace Psr\Http\Message;

interface UriFactoryInterface
{









public function createUri(string $uri = ''): UriInterface;
}
<?php

namespace Psr\Http\Message;

interface RequestFactoryInterface
{










public function createRequest(string $method, $uri): RequestInterface;
}
<?php

namespace Psr\Http\Message;

interface StreamFactoryInterface
{









public function createStream(string $content = ''): StreamInterface;
















public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;










public function createStreamFromResource($resource): StreamInterface;
}
<?php

namespace Psr\Http\Message;

interface ResponseFactoryInterface
{










public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
}
<?php

namespace Psr\Http\Message;

interface UploadedFileFactoryInterface
{




















public function createUploadedFile(
StreamInterface $stream,
int $size = null,
int $error = \UPLOAD_ERR_OK,
string $clientFilename = null,
string $clientMediaType = null
): UploadedFileInterface;
}
<?php

namespace Psr\Http\Message;

interface ServerRequestFactoryInterface
{
















public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface;
}
<?php

namespace Psr\Log;




trait LoggerAwareTrait
{





protected ?LoggerInterface $logger = null;






public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
}
<?php

namespace Psr\Log;









class NullLogger extends AbstractLogger
{











public function log($level, string|\Stringable $message, array $context = []): void
{

}
}
<?php

namespace Psr\Log;








abstract class AbstractLogger implements LoggerInterface
{
use LoggerTrait;
}
<?php

namespace Psr\Log;

class InvalidArgumentException extends \InvalidArgumentException
{
}
<?php

namespace Psr\Log;




interface LoggerAwareInterface
{







public function setLogger(LoggerInterface $logger): void;
}
<?php

namespace Psr\Log;




class LogLevel
{
const EMERGENCY = 'emergency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
}
<?php

namespace Psr\Log;









trait LoggerTrait
{








public function emergency(string|\Stringable $message, array $context = []): void
{
$this->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;
}
<?php

namespace Psr\Log;
















interface LoggerInterface
{








public function emergency(string|\Stringable $message, array $context = []): void;












public function alert(string|\Stringable $message, array $context = []): void;











public function critical(string|\Stringable $message, array $context = []): void;










public function error(string|\Stringable $message, array $context = []): void;












public function warning(string|\Stringable $message, array $context = []): void;









public function notice(string|\Stringable $message, array $context = []): void;











public function info(string|\Stringable $message, array $context = []): void;









public function debug(string|\Stringable $message, array $context = []): void;












public function log($level, string|\Stringable $message, array $context = []): void;
}
<?php

namespace Psr\Container;




interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}
<?php

namespace Psr\Container;

use Throwable;




interface ContainerExceptionInterface extends Throwable
{
}
<?php

declare(strict_types=1);

namespace Psr\Container;




interface ContainerInterface
{










public function get(string $id);












public function has(string $id): bool;
}
<?php

namespace Psr\Http\Message;













interface MessageInterface
{







public function getProtocolVersion(): string;














public function withProtocolVersion(string $version): MessageInterface;


























public function getHeaders(): array;









public function hasHeader(string $name): bool;















public function getHeader(string $name): array;




















public function getHeaderLine(string $name): string;
















public function withHeader(string $name, $value): MessageInterface;

















public function withAddedHeader(string $name, $value): MessageInterface;













public function withoutHeader(string $name): MessageInterface;






public function getBody(): StreamInterface;














public function withBody(StreamInterface $body): MessageInterface;
}
<?php

namespace Psr\Http\Message;




















interface RequestInterface extends MessageInterface
{
















public function getRequestTarget(): string;


















public function withRequestTarget(string $requestTarget): RequestInterface;







public function getMethod(): string;
















public function withMethod(string $method): RequestInterface;










public function getUri(): UriInterface;































public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface;
}
<?php

namespace Psr\Http\Message;
















interface ResponseInterface extends MessageInterface
{








public function getStatusCode(): int;





















public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface;














public function getReasonPhrase(): string;
}
<?php

namespace Psr\Http\Message;








interface StreamInterface
{














public function __toString(): string;






public function close(): void;








public function detach();






public function getSize(): ?int;







public function tell(): int;






public function eof(): bool;






public function isSeekable(): bool;













public function seek(int $offset, int $whence = SEEK_SET): void;











public function rewind(): void;






public function isWritable(): bool;








public function write(string $string): int;






public function isReadable(): bool;











public function read(int $length): string;








public function getContents(): string;













public function getMetadata(?string $key = null);
}
<?php

namespace Psr\Http\Message;









interface UploadedFileInterface
{
















public function getStream(): StreamInterface;

































public function moveTo(string $targetPath): void;










public function getSize(): ?int;















public function getError(): int;














public function getClientFilename(): ?string;














public function getClientMediaType(): ?string;
}
<?php

namespace Psr\Http\Message;





















interface UriInterface
{














public function getScheme(): string;



















public function getAuthority(): string;
















public function getUserInfo(): string;












public function getHost(): string;
















public function getPort(): ?int;


























public function getPath(): string;





















public function getQuery(): string;

















public function getFragment(): string;
















public function withScheme(string $scheme): UriInterface;















public function withUserInfo(string $user, ?string $password = null): UriInterface;













public function withHost(string $host): UriInterface;


















public function withPort(?int $port): UriInterface;























public function withPath(string $path): UriInterface;
















public function withQuery(string $query): UriInterface;















public function withFragment(string $fragment): UriInterface;
























public function __toString(): string;
}
<?php

namespace Psr\Http\Message;







































interface ServerRequestInterface extends RequestInterface
{









public function getServerParams(): array;











public function getCookieParams(): array;


















public function withCookieParams(array $cookies): ServerRequestInterface;













public function getQueryParams(): array;























public function withQueryParams(array $query): ServerRequestInterface;













public function getUploadedFiles(): array;












public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface;
















public function getParsedBody();





























public function withParsedBody($data): ServerRequestInterface;












public function getAttributes(): array;
















public function getAttribute(string $name, $default = null);
















public function withAttribute(string $name, $value): ServerRequestInterface;















public function withoutAttribute(string $name): ServerRequestInterface;
}
<?php



$issues = array();

if (!(PHP_VERSION_ID >= 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
);
}
<?php



class ComposerAutoloaderInit6df945c87d8c91a0cdc33b5e2833ee2d
{
private static $loader;

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




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

require __DIR__ . '/platform_check.php';

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

require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d::getInitializer($loader));

$loader->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;
}
}
<?php











namespace Composer;

use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;










class InstalledVersions
{




private static $installed;




private static $canGetVendors;





private static $installedByVendor = array();







public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}

if (1 === \count($packages)) {
return $packages[0];
}

return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}








public static function getInstalledPackagesByType($type)
{
$packagesByType = array();

foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $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;
}
}
<?php



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

return array(
'phpseclib\\' => 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'),
);
<?php return array(
'root' => 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',
),
),
),
);
<?php



namespace Composer\Autoload;

class ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d
{
public static $files = array (
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __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);
}
}
<?php



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

return array(
'Attribute' => $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',
);
<?php











namespace Composer\Autoload;





























class ClassLoader
{

private static $includeFile;


private $vendorDir;





private $prefixLengthsPsr4 = array();



private $prefixDirsPsr4 = array();



private $fallbackDirsPsr4 = array();









private $prefixesPsr0 = array();



private $fallbackDirsPsr0 = array();


private $useIncludePath = false;




private $classMap = array();


private $classMapAuthoritative = false;




private $missingClasses = array();


private $apcuPrefix;




private static $registeredLoaders = array();




public function __construct($vendorDir = null)
{
$this->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);
}
}
<?php



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

return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $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',
);
<?php



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

return array(
'Behat\\Gherkin' => array($vendorDir . '/behat/gherkin/src'),
);
<?php

namespace GuzzleHttp;

use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;




trait ClientTrait
{













abstract public function request(string $method, $uri, array $options = []): ResponseInterface;













public function get($uri, array $options = []): ResponseInterface
{
return $this->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);
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;







class PrepareBodyMiddleware
{



private $nextHandler;




public function __construct(callable $nextHandler)
{
$this->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';
}
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\EachPromise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Promise\PromisorInterface;
use Psr\Http\Message\RequestInterface;














class Pool implements PromisorInterface
{



private $each;











public function __construct(ClientInterface $client, $requests, array $config = [])
{
if (!isset($config['concurrency'])) {
$config['concurrency'] = 25;
}

if (isset($config['options'])) {
$opts = $config['options'];
unset($config['options']);
} else {
$opts = [];
}

$iterable = P\Create::iterFor($requests);
$requests = static function () use ($iterable, $client, $opts) {
foreach ($iterable as $key => $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;
};
}
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;







class HandlerStack
{



private $handler;




private $stack = [];




private $cached;
















public static function create(callable $handler = null): self
{
$stack = new self($handler ?: Utils::chooseHandler());
$stack->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).')';
}
}
<?php

namespace GuzzleHttp;












function describe_type($input): string
{
return Utils::describeType($input);
}









function headers_from_lines(iterable $lines): array
{
return Utils::headersFromLines($lines);
}










function debug_resource($value = null)
{
return Utils::debugResource($value);
}












function choose_handler(): callable
{
return Utils::chooseHandler();
}






function default_user_agent(): string
{
return Utils::defaultUserAgent();
}
















function default_ca_bundle(): string
{
return Utils::defaultCaBundle();
}







function normalize_header_keys(array $headers): array
{
return Utils::normalizeHeaderKeys($headers);
}






















function is_host_in_noproxy(string $host, array $noProxyArray): bool
{
return Utils::isHostInNoProxy($host, $noProxyArray);
}

















function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
{
return Utils::jsonDecode($json, $assoc, $depth, $options);
}













function json_encode($value, int $options = 0, int $depth = 512): string
{
return Utils::jsonEncode($value, $options, $depth);
}
<?php


if (!\function_exists('GuzzleHttp\describe_type')) {
require __DIR__.'/functions.php';
}
<?php

namespace GuzzleHttp;

use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;






























class MessageFormatter implements MessageFormatterInterface
{







public const CLF = '{hostname} {req_header_User-Agent} - [{date_common_log}] "{method} {target} HTTP/{version}" {code} {res_header_Content-Length}';
public const DEBUG = ">>>>>>>>\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);
}
}
<?php

namespace GuzzleHttp;






final class RequestOptions
{





















public const ALLOW_REDIRECTS = 'allow_redirects';








public const AUTH = 'auth';





public const BODY = 'body';








public const CERT = 'cert';








public const COOKIES = 'cookies';






public const CONNECT_TIMEOUT = 'connect_timeout';











public const CRYPTO_METHOD = 'crypto_method';






public const DEBUG = 'debug';






public const DECODE_CONTENT = 'decode_content';




public const DELAY = 'delay';

















public const EXPECT = 'expect';







public const FORM_PARAMS = 'form_params';





public const HEADERS = 'headers';







public const HTTP_ERRORS = 'http_errors';







public const IDN_CONVERSION = 'idn_conversion';






public const JSON = 'json';










public const MULTIPART = 'multipart';






public const ON_HEADERS = 'on_headers';










public const ON_STATS = 'on_stats';








public const PROGRESS = 'progress';






public const PROXY = 'proxy';







public const QUERY = 'query';






public const SINK = 'sink';







public const SYNCHRONOUS = 'synchronous';







public const SSL_KEY = 'ssl_key';





public const STREAM = 'stream';









public const VERIFY = 'verify';





public const TIMEOUT = 'timeout';





public const READ_TIMEOUT = 'read_timeout';




public const VERSION = 'version';




public const FORCE_IP_RESOLVE = 'force_ip_resolve';
}
<?php

namespace GuzzleHttp;

use Psr\Http\Message\MessageInterface;

final class BodySummarizer implements BodySummarizerInterface
{



private $truncateAt;

public function __construct(int $truncateAt = null)
{
$this->truncateAt = $truncateAt;
}




public function summarize(MessageInterface $message): ?string
{
return $this->truncateAt === null
? \GuzzleHttp\Psr7\Message::bodySummary($message)
: \GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt);
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Exception\InvalidArgumentException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\Handler\Proxy;
use GuzzleHttp\Handler\StreamHandler;
use Psr\Http\Message\UriInterface;

final class Utils
{








public static function describeType($input): string
{
switch (\gettype($input)) {
case 'object':
return 'object('.\get_class($input).')';
case 'array':
return 'array('.\count($input).')';
default:
\ob_start();
\var_dump($input);


$varDumpContent = \ob_get_clean();

return \str_replace('double(', 'float(', \rtrim($varDumpContent));
}
}







public static function headersFromLines(iterable $lines): array
{
$headers = [];

foreach ($lines as $line) {
$parts = \explode(':', $line, 2);
$headers[\trim($parts[0])][] = isset($parts[1]) ? \trim($parts[1]) : null;
}

return $headers;
}








public static function debugResource($value = null)
{
if (\is_resource($value)) {
return $value;
}
if (\defined('STDOUT')) {
return \STDOUT;
}

return \GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w');
}










public static function chooseHandler(): callable
{
$handler = null;

if (\defined('CURLOPT_CUSTOMREQUEST')) {
if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
$handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
} elseif (\function_exists('curl_exec')) {
$handler = new CurlHandler();
} elseif (\function_exists('curl_multi_exec')) {
$handler = new CurlMultiHandler();
}
}

if (\ini_get('allow_url_fopen')) {
$handler = $handler
? Proxy::wrapStreaming($handler, new StreamHandler())
: new StreamHandler();
} elseif (!$handler) {
throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.');
}

return $handler;
}




public static function defaultUserAgent(): string
{
return sprintf('GuzzleHttp/%d', ClientInterface::MAJOR_VERSION);
}
















public static function defaultCaBundle(): string
{
static $cached = null;
static $cafiles = [

'/etc/pki/tls/certs/ca-bundle.crt',

'/etc/ssl/certs/ca-certificates.crt',

'/usr/local/share/certs/ca-root-nss.crt',

'/var/lib/ca-certificates/ca-bundle.pem',

'/usr/local/etc/openssl/cert.pem',

'/etc/ca-certificates.crt',

'C:\\windows\\system32\\curl-ca-bundle.crt',
'C:\\windows\\curl-ca-bundle.crt',
];

if ($cached) {
return $cached;
}

if ($ca = \ini_get('openssl.cafile')) {
return $cached = $ca;
}

if ($ca = \ini_get('curl.cainfo')) {
return $cached = $ca;
}

foreach ($cafiles as $filename) {
if (\file_exists($filename)) {
return $cached = $filename;
}
}

throw new \RuntimeException(
<<< EOT
No system CA bundle could be found in any of the the common system locations.
PHP versions earlier than 5.6 are not properly configured to use the system's
CA bundle by default. In order to verify peer certificates, you will need to
supply the path on disk to a certificate bundle to the 'verify' request
option: https://docs.guzzlephp.org/en/latest/request-options.html#verify. If
you do not need a specific certificate bundle, then Mozilla provides a commonly
used CA bundle which can be downloaded here (provided by the maintainer of
cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available
on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path
to the file, allowing you to omit the 'verify' request option. See
https://curl.haxx.se/docs/sslcerts.html for more information.
EOT
);
}





public static function normalizeHeaderKeys(array $headers): array
{
$result = [];
foreach (\array_keys($headers) as $key) {
$result[\strtolower($key)] = $key;
}

return $result;
}




















public static function isHostInNoProxy(string $host, array $noProxyArray): bool
{
if (\strlen($host) === 0) {
throw new InvalidArgumentException('Empty host provided');
}


[$host] = \explode(':', $host, 2);

foreach ($noProxyArray as $area) {

if ($area === '*') {
return true;
}

if (empty($area)) {

continue;
}

if ($area === $host) {

return true;
}


$area = '.'.\ltrim($area, '.');
if (\substr($host, -\strlen($area)) === $area) {
return true;
}
}

return false;
}
















public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
{
$data = \json_decode($json, $assoc, $depth, $options);
if (\JSON_ERROR_NONE !== \json_last_error()) {
throw new InvalidArgumentException('json_decode error: '.\json_last_error_msg());
}

return $data;
}












public static function jsonEncode($value, int $options = 0, int $depth = 512): string
{
$json = \json_encode($value, $options, $depth);
if (\JSON_ERROR_NONE !== \json_last_error()) {
throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
}


return $json;
}









public static function currentTime(): float
{
return (float) \function_exists('hrtime') ? \hrtime(true) / 1e9 : \microtime(true);
}






public static function idnUriConvert(UriInterface $uri, int $options = 0): UriInterface
{
if ($uri->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');
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;







class RetryMiddleware
{



private $nextHandler;




private $decider;




private $delay;











public function __construct(callable $decider, callable $nextHandler, callable $delay = null)
{
$this->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);
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;




interface ClientInterface
{



public const MAJOR_VERSION = 7;










public function send(RequestInterface $request, array $options = []): ResponseInterface;








public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface;














public function request(string $method, $uri, array $options = []): ResponseInterface;













public function requestAsync(string $method, $uri, array $options = []): PromiseInterface;














public function getConfig(string $option = null);
}
<?php

namespace GuzzleHttp;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;





final class TransferStats
{



private $request;




private $response;




private $transferTime;




private $handlerStats;




private $handlerErrorData;








public function __construct(
RequestInterface $request,
ResponseInterface $response = null,
float $transferTime = null,
$handlerErrorData = null,
array $handlerStats = []
) {
$this->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;
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Cookie\CookieJarInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;




final class Middleware
{








public static function cookies(): callable
{
return static function (callable $handler): callable {
return static function ($request, array $options) use ($handler) {
if (empty($options['cookies'])) {
return $handler($request, $options);
} elseif (!($options['cookies'] instanceof CookieJarInterface)) {
throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface');
}
$cookieJar = $options['cookies'];
$request = $cookieJar->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);
};
};
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\TooManyRedirectsException;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;









class RedirectMiddleware
{
public const HISTORY_HEADER = 'X-Guzzle-Redirect-History';

public const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';




public static $defaultSettings = [
'max' => 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;
}
}
<?php

namespace GuzzleHttp;

use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\InvalidArgumentException;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;




class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
{
use ClientTrait;




private $config;
































public function __construct(array $config = [])
{
if (!isset($config['handler'])) {
$config['handler'] = HandlerStack::create();
} elseif (!\is_callable($config['handler'])) {
throw new InvalidArgumentException('handler must be a callable');
}


if (isset($config['base_uri'])) {
$config['base_uri'] = Psr7\Utils::uriFor($config['base_uri']);
}

$this->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.');
}
}
<?php

namespace GuzzleHttp\Cookie;




class SetCookie
{



private static $defaults = [
'Name' => 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;
}
}
<?php

namespace GuzzleHttp\Cookie;




class SessionCookieJar extends CookieJar
{



private $sessionKey;




private $storeSessionCookies;









public function __construct(string $sessionKey, bool $storeSessionCookies = false)
{
parent::__construct();
$this->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');
}
}
}
<?php

namespace GuzzleHttp\Cookie;

use GuzzleHttp\Utils;




class FileCookieJar extends CookieJar
{



private $filename;




private $storeSessionCookies;










public function __construct(string $cookieFile, bool $storeSessionCookies = false)
{
parent::__construct();
$this->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}");
}
}
}
<?php

namespace GuzzleHttp\Cookie;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;













interface CookieJarInterface extends \Countable, \IteratorAggregate
{










public function withCookieHeader(RequestInterface $request): RequestInterface;







public function extractCookies(RequestInterface $request, ResponseInterface $response): void;








public function setCookie(SetCookie $cookie): bool;















public function clear(string $domain = null, string $path = null, string $name = null): void;








public function clearSessionCookies(): void;




public function toArray(): array;
}
<?php

namespace GuzzleHttp\Cookie;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;




class CookieJar implements CookieJarInterface
{



private $cookies = [];




private $strictMode;








public function __construct(bool $strictMode = false, array $cookieArray = [])
{
$this->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()
);
}
}
}
<?php

namespace GuzzleHttp\Exception;

class TooManyRedirectsException extends RequestException
{
}
<?php

namespace GuzzleHttp\Exception;

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

namespace GuzzleHttp\Exception;

use GuzzleHttp\BodySummarizer;
use GuzzleHttp\BodySummarizerInterface;
use Psr\Http\Client\RequestExceptionInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;




class RequestException extends TransferException implements RequestExceptionInterface
{



private $request;




private $response;




private $handlerContext;

public function __construct(
string $message,
RequestInterface $request,
ResponseInterface $response = null,
\Throwable $previous = null,
array $handlerContext = []
) {

$code = $response ? $response->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;
}
}
<?php

namespace GuzzleHttp\Exception;

use Psr\Http\Client\NetworkExceptionInterface;
use Psr\Http\Message\RequestInterface;






class ConnectException extends TransferException implements NetworkExceptionInterface
{



private $request;




private $handlerContext;

public function __construct(
string $message,
RequestInterface $request,
\Throwable $previous = null,
array $handlerContext = []
) {
parent::__construct($message, 0, $previous);
$this->request = $request;
$this->handlerContext = $handlerContext;
}




public function getRequest(): RequestInterface
{
return $this->request;
}









public function getHandlerContext(): array
{
return $this->handlerContext;
}
}
<?php

namespace GuzzleHttp\Exception;




class ClientException extends BadResponseException
{
}
<?php

namespace GuzzleHttp\Exception;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;




class BadResponseException extends RequestException
{
public function __construct(
string $message,
RequestInterface $request,
ResponseInterface $response,
\Throwable $previous = null,
array $handlerContext = []
) {
parent::__construct($message, $request, $response, $previous, $handlerContext);
}




public function hasResponse(): bool
{
return true;
}




public function getResponse(): ResponseInterface
{

return parent::getResponse();
}
}
<?php

namespace GuzzleHttp\Exception;

use Psr\Http\Client\ClientExceptionInterface;

interface GuzzleException extends ClientExceptionInterface
{
}
<?php

namespace GuzzleHttp\Exception;

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

namespace GuzzleHttp\Exception;




class ServerException extends BadResponseException
{
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Utils;




final class HeaderProcessor
{









public static function parseHeaders(array $headers): array
{
if ($headers === []) {
throw new \RuntimeException('Expected a non-empty array of header data');
}

$parts = \explode(' ', \array_shift($headers), 3);
$version = \explode('/', $parts[0])[1] ?? null;

if ($version === null) {
throw new \RuntimeException('HTTP version missing from header data');
}

$status = $parts[1] ?? null;

if ($status === null) {
throw new \RuntimeException('HTTP status code missing from header data');
}

return [$version, (int) $status, $parts[2] ?? null, Utils::headersFromLines($headers)];
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Utils;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;






final class EasyHandle
{



public $handle;




public $sink;




public $headers = [];




public $response;




public $request;




public $options = [];




public $errno = 0;




public $onHeadersException;




public $createResponseException;







public function createResponse(): void
{
[$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($this->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);
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;










class CurlHandler
{



private $factory;








public function __construct(array $options = [])
{
$this->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);
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\TransferStats;
use GuzzleHttp\Utils;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;






class StreamHandler
{



private $lastHeaders = [];







public function __invoke(RequestInterface $request, array $options): PromiseInterface
{

if (isset($options['delay'])) {
\usleep($options['delay'] * 1000);
}

$startTime = isset($options['on_stats']) ? Utils::currentTime() : null;

try {

$request = $request->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);
}
};
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Utils;
use Psr\Http\Message\RequestInterface;










class CurlMultiHandler
{



private $factory;




private $selectTimeout;




private $active = 0;






private $handles = [];






private $delays = [];




private $options = [];


private $_mh;










public function __construct(array $options = [])
{
$this->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;
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\TransferStats;
use GuzzleHttp\Utils;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;






class MockHandler implements \Countable
{



private $queue = [];




private $lastRequest;




private $lastOptions = [];




private $onFulfilled;




private $onRejected;









public static function createWithMiddleware(array $queue = null, callable $onFulfilled = null, callable $onRejected = null): HandlerStack
{
return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
}










public function __construct(array $queue = null, callable $onFulfilled = null, callable $onRejected = null)
{
$this->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);
}
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\LazyOpenStream;
use GuzzleHttp\TransferStats;
use GuzzleHttp\Utils;
use Psr\Http\Message\RequestInterface;






class CurlFactory implements CurlFactoryInterface
{
public const CURL_VERSION_STR = 'curl_version';




public const LOW_CURL_VERSION_NUMBER = '7.21.2';




private $handles = [];




private $maxHandles;




public function __construct(int $maxHandles)
{
$this->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]);
}
}
}
<?php

namespace GuzzleHttp\Handler;

use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\RequestInterface;






class Proxy
{









public static function wrapSync(callable $default, callable $sync): callable
{
return static function (RequestInterface $request, array $options) use ($default, $sync): PromiseInterface {
return empty($options[RequestOptions::SYNCHRONOUS]) ? $default($request, $options) : $sync($request, $options);
};
}














public static function wrapStreaming(callable $default, callable $streaming): callable
{
return static function (RequestInterface $request, array $options) use ($default, $streaming): PromiseInterface {
return empty($options['stream']) ? $default($request, $options) : $streaming($request, $options);
};
}
}
<?php

namespace GuzzleHttp\Handler;

use Psr\Http\Message\RequestInterface;

interface CurlFactoryInterface
{








public function create(RequestInterface $request, array $options): EasyHandle;






public function release(EasyHandle $easy): void;
}
<?php

namespace GuzzleHttp;

use Psr\Http\Message\MessageInterface;

interface BodySummarizerInterface
{



public function summarize(MessageInterface $message): ?string;
}
<?php

namespace GuzzleHttp;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface MessageFormatterInterface
{







public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string;
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;




class CancellationException extends RejectionException
{
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;

interface TaskQueueInterface
{



public function isEmpty(): bool;





public function add(callable $task): void;




public function run(): void;
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;








class Promise implements PromiseInterface
{
private $state = self::PENDING;
private $result;
private $cancelFn;
private $waitFn;
private $waitList;
private $handlers = [];





public function __construct(
callable $waitFn = null,
callable $cancelFn = null
) {
$this->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);
}
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;









class FulfilledPromise implements PromiseInterface
{
private $value;




public function __construct($value)
{
if (is_object($value) && method_exists($value, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a FulfilledPromise with a promise.'
);
}

$this->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
{

}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;




interface PromisorInterface
{



public function promise(): PromiseInterface;
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;






class RejectionException extends \RuntimeException
{

private $reason;





public function __construct($reason, string $description = null)
{
$this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;










interface PromiseInterface
{
public const PENDING = 'pending';
public const FULFILLED = 'fulfilled';
public const REJECTED = 'rejected';








public function then(
callable $onFulfilled = null,
callable $onRejected = null
): PromiseInterface;









public function otherwise(callable $onRejected): PromiseInterface;







public function getState(): string;








public function resolve($value): void;








public function reject($reason): void;






public function cancel(): void;














public function wait(bool $unwrap = true);
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;

final class Utils
{















public static function queue(TaskQueueInterface $assign = null): TaskQueueInterface
{
static $queue;

if ($assign) {
$queue = $assign;
} elseif (!$queue) {
$queue = new TaskQueue();
}

return $queue;
}







public static function task(callable $task): PromiseInterface
{
$queue = self::queue();
$promise = new Promise([$queue, 'run']);
$queue->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;
});
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;







class EachPromise implements PromisorInterface
{
private $pending = [];

private $nextPendingIndex = 0;


private $iterable;


private $concurrency;


private $onFulfilled;


private $onRejected;


private $aggregate;


private $mutex;






















public function __construct($iterable, array $config = [])
{
$this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;

use Generator;
use Throwable;





































final class Coroutine implements PromiseInterface
{



private $currentPromise;




private $generator;




private $result;

public function __construct(callable $generatorFn)
{
$this->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);
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;









class RejectedPromise implements PromiseInterface
{
private $reason;




public function __construct($reason)
{
if (is_object($reason) && method_exists($reason, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a RejectedPromise with a promise.'
);
}

$this->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
{

}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;

final class Each
{















public static function of(
$iterable,
callable $onFulfilled = null,
callable $onRejected = null
): PromiseInterface {
return (new EachPromise($iterable, [
'fulfilled' => $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);
}
);
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;












class TaskQueue implements TaskQueueInterface
{
private $enableShutdown = true;
private $queue = [];

public function __construct(bool $withShutdown = true)
{
if ($withShutdown) {
register_shutdown_function(function (): void {
if ($this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;

final class Create
{





public static function promiseFor($value): PromiseInterface
{
if ($value instanceof PromiseInterface) {
return $value;
}


if (is_object($value) && method_exists($value, 'then')) {
$wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
$promise = new Promise($wfn, $cfn);
$value->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]);
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;




class AggregateException extends RejectionException
{
public function __construct(string $msg, array $reasons)
{
parent::__construct(
$reasons,
sprintf('%s; %d rejected promises', $msg, count($reasons))
);
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Promise;

final class Is
{



public static function pending(PromiseInterface $promise): bool
{
return $promise->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;






final class StreamWrapper
{

public $context;


private $stream;


private $mode;










public static function getResource(StreamInterface $stream)
{
self::register();

if ($stream->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,
];
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;





final class CachingStream implements StreamInterface
{
use StreamDecoratorTrait;


private $remoteStream;


private $skipReadBytes = 0;




private $stream;







public function __construct(
StreamInterface $stream,
StreamInterface $target = null
) {
$this->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();
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\UriInterface;








final class UriNormalizer
{



public const PRESERVING_NORMALIZATIONS =
self::CAPITALIZE_PERCENT_ENCODING |
self::DECODE_UNRESERVED_CHARACTERS |
self::CONVERT_EMPTY_PATH |
self::REMOVE_DEFAULT_HOST |
self::REMOVE_DEFAULT_PORT |
self::REMOVE_DOT_SEGMENTS;






public const CAPITALIZE_PERCENT_ENCODING = 1;










public const DECODE_UNRESERVED_CHARACTERS = 2;






public const CONVERT_EMPTY_PATH = 4;












public const REMOVE_DEFAULT_HOST = 8;






public const REMOVE_DEFAULT_PORT = 16;









public const REMOVE_DOT_SEGMENTS = 32;










public const REMOVE_DUPLICATE_SLASHES = 64;












public const SORT_QUERY_PARAMETERS = 128;

















public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface
{
if ($flags & self::CAPITALIZE_PERCENT_ENCODING) {
$uri = self::capitalizePercentEncoding($uri);
}

if ($flags & self::DECODE_UNRESERVED_CHARACTERS) {
$uri = self::decodeUnreservedCharacters($uri);
}

if ($flags & self::CONVERT_EMPTY_PATH && $uri->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()
{

}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;




final class NoSeekStream implements StreamInterface
{
use StreamDecoratorTrait;


private $stream;

public function seek($offset, $whence = SEEK_SET): void
{
throw new \RuntimeException('Cannot seek a NoSeekStream');
}

public function isSeekable(): bool
{
return false;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\StreamInterface;




trait MessageTrait
{

private $headers = [];


private $headerNames = [];


private $protocol = '1.1';


private $stream;

public function getProtocolVersion(): string
{
return $this->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)
);
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;











final class PumpStream implements StreamInterface
{

private $source;


private $size;


private $tellPos = 0;


private $metadata;


private $buffer;











public function __construct(callable $source, array $options = [])
{
$this->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);
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;




class Stream implements StreamInterface
{




private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';


private $stream;

private $size;

private $seekable;

private $readable;

private $writable;

private $uri;

private $customMetadata;















public function __construct($stream, array $options = [])
{
if (!is_resource($stream)) {
throw new \InvalidArgumentException('Stream must be a resource');
}

if (isset($options['size'])) {
$this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;







final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
{
public function createUploadedFile(
StreamInterface $stream,
int $size = null,
int $error = \UPLOAD_ERR_OK,
string $clientFilename = null,
string $clientMediaType = null
): UploadedFileInterface {
if ($size === null) {
$size = $stream->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);
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\UriInterface;








final class UriResolver
{





public static function removeDotSegments(string $path): string
{
if ($path === '' || $path === '/') {
return $path;
}

$results = [];
$segments = explode('/', $path);
foreach ($segments as $segment) {
if ($segment === '..') {
array_pop($results);
} elseif ($segment !== '.') {
$results[] = $segment;
}
}

$newPath = implode('/', $results);

if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {

$newPath = '/'.$newPath;
} elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {


$newPath .= '/';
}

return $newPath;
}






public static function resolve(UriInterface $base, UriInterface $rel): UriInterface
{
if ((string) $rel === '') {

return $base;
}

if ($rel->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()
{

}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

final class Header
{








public static function parse($header): array
{
static $trimmed = "\"'  \n\t\r";
$params = $matches = [];

foreach ((array) $header as $value) {
foreach (self::splitList($value) as $val) {
$part = [];
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) {
if (preg_match_all('/<[^>]+>|[^=]+/', $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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use InvalidArgumentException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;




class Request implements RequestInterface
{
use MessageTrait;


private $method;


private $requestTarget;


private $uri;








public function __construct(
string $method,
$uri,
array $headers = [],
$body = null,
string $version = '1.1'
) {
$this->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.');
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use GuzzleHttp\Psr7\Exception\MalformedUriException;
use Psr\Http\Message\UriInterface;








class Uri implements UriInterface, \JsonSerializable
{






private const HTTP_DEFAULT_HOST = 'localhost';

private const DEFAULT_PORTS = [
'http' => 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');
}
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;




class Response implements ResponseInterface
{
use MessageTrait;


private const PHRASES = [
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-status',
208 => 'Already Reported',
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.');
}
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

final class MimeType
{
private const MIME_TYPES = [
'1km' => '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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

final class Message
{





public static function toString(MessageInterface $message): string
{
if ($message instanceof RequestInterface) {
$msg = trim($message->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
);
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;




final class Rfc7230
{









public const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

final class Utils
{





public static function caselessRemove(array $keys, array $data): array
{
$result = [];

foreach ($keys as &$key) {
$key = strtolower((string) $key);
}

foreach ($data as $k => $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');
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;





final class DroppingStream implements StreamInterface
{
use StreamDecoratorTrait;


private $maxLength;


private $stream;





public function __construct(StreamInterface $stream, int $maxLength)
{
$this->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));
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

final class Query
{











public static function parse(string $str, $urlEncoding = true): array
{
$result = [];

if ($str === '') {
return $result;
}

if ($urlEncoding === true) {
$decoder = function ($value) {
return rawurldecode(str_replace('+', ' ', (string) $value));
};
} elseif ($urlEncoding === PHP_QUERY_RFC3986) {
$decoder = 'rawurldecode';
} elseif ($urlEncoding === PHP_QUERY_RFC1738) {
$decoder = 'urldecode';
} else {
$decoder = function ($str) {
return $str;
};
}

foreach (explode('&', $str) as $kvp) {
$parts = explode('=', $kvp, 2);
$key = $decoder($parts[0]);
$value = isset($parts[1]) ? $decoder($parts[1]) : null;
if (!array_key_exists($key, $result)) {
$result[$key] = $value;
} else {
if (!is_array($result[$key])) {
$result[$key] = [$result[$key]];
}
$result[$key][] = $value;
}
}

return $result;
}













public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string
{
if (!$params) {
return '';
}

if ($encoding === false) {
$encoder = function (string $str): string {
return $str;
};
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}

$qs = '';
foreach ($params as $k => $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) : '';
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\UriInterface;






final class UriComparator
{




public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool
{
if (\strcasecmp($original->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()
{

}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;





final class MultipartStream implements StreamInterface
{
use StreamDecoratorTrait;


private $boundary;


private $stream;













public function __construct(array $elements = [], string $boundary = null)
{
$this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;




final class LimitStream implements StreamInterface
{
use StreamDecoratorTrait;


private $offset;


private $limit;


private $stream;








public function __construct(
StreamInterface $stream,
int $limit = -1,
int $offset = 0
) {
$this->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 '';
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;












final class InflateStream implements StreamInterface
{
use StreamDecoratorTrait;


private $stream;

public function __construct(StreamInterface $stream)
{
$resource = StreamWrapper::getResource($stream);




stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);
$this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use InvalidArgumentException;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriInterface;















class ServerRequest extends Request implements ServerRequestInterface
{



private $attributes = [];




private $cookieParams = [];




private $parsedBody;




private $queryParams = [];




private $serverParams;




private $uploadedFiles = [];









public function __construct(
string $method,
$uri,
array $headers = [],
$body = null,
string $version = '1.1',
array $serverParams = []
) {
$this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;






trait StreamDecoratorTrait
{



public function __construct(StreamInterface $stream)
{
$this->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');
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;





final class LazyOpenStream implements StreamInterface
{
use StreamDecoratorTrait;


private $filename;


private $mode;




private $stream;





public function __construct(string $filename, string $mode)
{
$this->filename = $filename;
$this->mode = $mode;



unset($this->stream);
}




protected function createStream(): StreamInterface
{
return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;







#[\AllowDynamicProperties]
final class FnStream implements StreamInterface
{
private const SLOTS = [
'__toString', 'close', 'detach', 'rewind',
'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
'isReadable', 'read', 'getContents', 'getMetadata',
];


private $methods;




public function __construct(array $methods)
{
$this->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);
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;






final class AppendStream implements StreamInterface
{

private $streams = [];


private $seekable = true;


private $current = 0;


private $pos = 0;





public function __construct(array $streams = [])
{
foreach ($streams as $stream) {
$this->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 : [];
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use InvalidArgumentException;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
use RuntimeException;

class UploadedFile implements UploadedFileInterface
{
private const ERRORS = [
UPLOAD_ERR_OK,
UPLOAD_ERR_INI_SIZE,
UPLOAD_ERR_FORM_SIZE,
UPLOAD_ERR_PARTIAL,
UPLOAD_ERR_NO_FILE,
UPLOAD_ERR_NO_TMP_DIR,
UPLOAD_ERR_CANT_WRITE,
UPLOAD_ERR_EXTENSION,
];




private $clientFilename;




private $clientMediaType;




private $error;




private $file;




private $moved = false;




private $size;




private $stream;




public function __construct(
$streamOrFile,
?int $size,
int $errorStatus,
string $clientFilename = null,
string $clientMediaType = null
) {
$this->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;
}
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7\Exception;

use InvalidArgumentException;




class MalformedUriException extends InvalidArgumentException
{
}
<?php

declare(strict_types=1);

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;









final class BufferStream implements StreamInterface
{

private $hwm;


private $buffer = '';








public function __construct(int $hwm = 16384)
{
$this->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 : [];
}
}
<?php

namespace PhpAmqpLib\Connection;

use PhpAmqpLib\Wire\IO\StreamIO;

class AMQPStreamConnection extends AbstractConnection
{




















public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$insist = false,
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
$connection_timeout = 3.0,
$read_write_timeout = 3.0,
$context = null,
$keepalive = false,
$heartbeat = 0,
$channel_rpc_timeout = 0.0,
$ssl_protocol = null,
?AMQPConnectionConfig $config = null
) {
if (func_num_args() === 17 || ($ssl_protocol !== null && $ssl_protocol instanceof AMQPConnectionConfig === false)) {
trigger_error(
'$ssl_protocol parameter is deprecated, use stream_context_set_option($context, \'ssl\', \'crypto_method\', $ssl_protocol) instead (see https://www.php.net/manual/en/function.stream-socket-enable-crypto.php for possible values)',
E_USER_DEPRECATED
);
} elseif ($ssl_protocol instanceof AMQPConnectionConfig) {
$config = $ssl_protocol;
}

if ($channel_rpc_timeout > $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
);
}
}
<?php

namespace PhpAmqpLib\Connection;

use LogicException;




class AMQPConnectionFactory
{
public static function create(AMQPConnectionConfig $config): AbstractConnection
{
if ($config->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;
});
}
}
<?php

namespace PhpAmqpLib\Connection;

use InvalidArgumentException;
use PhpAmqpLib\Wire;




final class AMQPConnectionConfig
{
public const AUTH_PLAIN = 'PLAIN';
public const AUTH_AMQPPLAIN = 'AMQPLAIN';
public const AUTH_EXTERNAL = 'EXTERNAL';
public const IO_TYPE_STREAM = 'stream';
public const IO_TYPE_SOCKET = 'socket';


private $ioType = self::IO_TYPE_STREAM;


private $isLazy = false;


private $host = '127.0.0.1';


private $port = 5672;


private $user = 'guest';


private $password = 'guest';


private $vhost = '/';


private $insist = false;


private $loginMethod = self::AUTH_AMQPPLAIN;


private $loginResponse;


private $locale = 'en_US';


private $connectionTimeout = 3.0;


private $readTimeout = 3.0;


private $writeTimeout = 3.0;


private $channelRPCTimeout = 0.0;


private $heartbeat = 0;


private $keepalive = false;


private $isSecure = false;





private $networkProtocol = 'tcp';


private $streamContext;


private $sendBufferSize = 0;


private $dispatchSignals = true;


private $amqpProtocol = Wire\Constants091::VERSION;





private $protocolStrictFields = false;


private $sslCaCert;




private $sslCaPath;


private $sslCert;


private $sslKey;


private $sslVerify;


private $sslVerifyName;


private $sslPassPhrase;


private $sslCiphers;


private $sslSecurityLevel;


private $sslCryptoMethod;


private $connectionName = '';





private $debugPackets = false;

public function getIoType(): string
{
return $this->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;
}
}
<?php

namespace PhpAmqpLib\Connection;




class AMQPLazySocketConnection extends AMQPSocketConnection
{



public function connectOnConstruct(): bool
{
return false;
}








public static function create_connection($hosts, $options = array())
{
if (count($hosts) > 1) {
throw new \RuntimeException('Lazy connection does not support multiple hosts');
}

return parent::create_connection($hosts, $options);
}
}
<?php

namespace PhpAmqpLib\Connection;




class AMQPLazyConnection extends AMQPStreamConnection
{



public function connectOnConstruct(): bool
{
return false;
}








public static function create_connection($hosts, $options = array())
{
if (count($hosts) > 1) {
throw new \RuntimeException('Lazy connection does not support multiple hosts');
}

return parent::create_connection($hosts, $options);
}
}
<?php

namespace PhpAmqpLib\Connection\Heartbeat;

use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPRuntimeException;




abstract class AbstractSignalHeartbeatSender
{



protected $connection;




protected $wasActive = false;





public function __construct(AbstractConnection $connection)
{
if (!$this->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();
}
}
}
<?php

namespace PhpAmqpLib\Connection\Heartbeat;

use PhpAmqpLib\Exception\AMQPRuntimeException;







final class PCNTLHeartbeatSender extends AbstractSignalHeartbeatSender
{
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);
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);
}
}
<?php
namespace PhpAmqpLib\Connection\Heartbeat;

use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPRuntimeException;









final class SIGHeartbeatSender extends AbstractSignalHeartbeatSender
{



private $signal;




private $childPid;






public function __construct(AbstractConnection $connection, int $signal = SIGUSR1)
{
parent::__construct($connection);
$this->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;
}
}
}
<?php

namespace PhpAmqpLib\Connection;




class AMQPLazySSLConnection extends AMQPSSLConnection
{



public function connectOnConstruct(): bool
{
return false;
}








public static function create_connection($hosts, $options = array())
{
if (count($hosts) > 1) {
throw new \RuntimeException('Lazy connection does not support multiple hosts');
}

return parent::create_connection($hosts, $options);
}
}
<?php

namespace PhpAmqpLib\Connection;

class AMQPSSLConnection extends AMQPStreamConnection
{











public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$ssl_options = array(),
$options = array(),
?AMQPConnectionConfig $config = null
) {
if (empty($ssl_options)) {
trigger_error('Using non-TLS instances of AMQPSSLConnection is deprecated and will be removed in version 4 of php-amqplib', E_USER_DEPRECATED);
$ssl_context = null;
} else {
$ssl_context = $this->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;
}
}
<?php

namespace PhpAmqpLib\Connection;

use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Channel\Frame;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPHeartbeatMissedException;
use PhpAmqpLib\Exception\AMQPInvalidFrameException;
use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPNoDataException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPSocketException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\Assert;
use PhpAmqpLib\Package;
use PhpAmqpLib\Wire;
use PhpAmqpLib\Wire\AMQPReader;
use PhpAmqpLib\Wire\AMQPTable;
use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\IO\AbstractIO;

abstract class AbstractConnection extends AbstractChannel
{




public static $LIBRARY_PROPERTIES = array(
'product' => 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.");
}
}
}
<?php

namespace PhpAmqpLib\Connection;

use PhpAmqpLib\Wire\IO\SocketIO;

class AMQPSocketConnection extends AbstractConnection
{


















public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$insist = false,
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
$read_timeout = 3,
$keepalive = false,
$write_timeout = 3,
$heartbeat = 0,
$channel_rpc_timeout = 0.0,
?AMQPConnectionConfig $config = null
) {
if ($channel_rpc_timeout > $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
);
}
}
<?php

namespace PhpAmqpLib\Message;

use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Exception\AMQPEmptyDeliveryTagException;
use PhpAmqpLib\Wire\AMQPReader;
use PhpAmqpLib\Wire\AMQPWriter;




class AMQPMessage
{
const DELIVERY_MODE_NON_PERSISTENT = 1;
const DELIVERY_MODE_PERSISTENT = 2;


public $body;


public $body_size;


public $is_truncated = false;


public $content_encoding;


private $deliveryTag;


private $consumerTag;


private $redelivered;


private $exchange;


private $routingKey;


private $messageCount;


private $channel;


private $responded = false;






public $delivery_info = array();


protected $properties = array();


protected $serialized_properties;


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





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;
}
}
<?php

namespace PhpAmqpLib\Helper;

use PhpAmqpLib\Wire\Constants;

class DebugHelper
{



protected $debug;




protected $debug_output;




protected $constants;




public function __construct(Constants $constants)
{
$this->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);
}
}
<?php



namespace PhpAmqpLib\Helper\Protocol;

use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\AMQPReader;

class Protocol091
{








public function connectionStart(
$version_major = 0,
$version_minor = 9,
$server_properties = [],
$mechanisms = 'PLAIN',
$locales = 'en_US'
) {
$writer = new AMQPWriter();
$writer->write_octet($version_major);
$writer->write_octet($version_minor);
$writer->write_table(empty($server_properties) ? array() : $server_properties);
$writer->write_longstr($mechanisms);
$writer->write_longstr($locales);
return array(10, 10, $writer);
}





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;
}
}
<?php



namespace PhpAmqpLib\Helper\Protocol;

use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\AMQPReader;

class Protocol080
{








public function connectionStart(
$version_major = 0,
$version_minor = 8,
$server_properties = [],
$mechanisms = 'PLAIN',
$locales = 'en_US'
) {
$writer = new AMQPWriter();
$writer->write_octet($version_major);
$writer->write_octet($version_minor);
$writer->write_table(empty($server_properties) ? array() : $server_properties);
$writer->write_longstr($mechanisms);
$writer->write_longstr($locales);
return array(10, 10, $writer);
}





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;
}
}
<?php



namespace PhpAmqpLib\Helper\Protocol;

class Wait091
{



protected $wait = array(
'connection.start' => '10,10',
'connection.start_ok' => '10,11',
'connection.secure' => '10,20',
'connection.secure_ok' => '10,21',
'connection.tune' => '10,30',
'connection.tune_ok' => '10,31',
'connection.open' => '10,40',
'connection.open_ok' => '10,41',
'connection.close' => '10,50',
'connection.close_ok' => '10,51',
'connection.blocked' => '10,60',
'connection.unblocked' => '10,61',
'channel.open' => '20,10',
'channel.open_ok' => '20,11',
'channel.flow' => '20,20',
'channel.flow_ok' => '20,21',
'channel.close' => '20,40',
'channel.close_ok' => '20,41',
'access.request' => '30,10',
'access.request_ok' => '30,11',
'exchange.declare' => '40,10',
'exchange.declare_ok' => '40,11',
'exchange.delete' => '40,20',
'exchange.delete_ok' => '40,21',
'exchange.bind' => '40,30',
'exchange.bind_ok' => '40,31',
'exchange.unbind' => '40,40',
'exchange.unbind_ok' => '40,51',
'queue.declare' => '50,10',
'queue.declare_ok' => '50,11',
'queue.bind' => '50,20',
'queue.bind_ok' => '50,21',
'queue.purge' => '50,30',
'queue.purge_ok' => '50,31',
'queue.delete' => '50,40',
'queue.delete_ok' => '50,41',
'queue.unbind' => '50,50',
'queue.unbind_ok' => '50,51',
'basic.qos' => '60,10',
'basic.qos_ok' => '60,11',
'basic.consume' => '60,20',
'basic.consume_ok' => '60,21',
'basic.cancel' => '60,30',
'basic.cancel_ok' => '60,31',
'basic.publish' => '60,40',
'basic.return' => '60,50',
'basic.deliver' => '60,60',
'basic.get' => '60,70',
'basic.get_ok' => '60,71',
'basic.get_empty' => '60,72',
'basic.ack' => '60,80',
'basic.reject' => '60,90',
'basic.recover_async' => '60,100',
'basic.recover' => '60,110',
'basic.recover_ok' => '60,111',
'basic.nack' => '60,120',
'tx.select' => '90,10',
'tx.select_ok' => '90,11',
'tx.commit' => '90,20',
'tx.commit_ok' => '90,21',
'tx.rollback' => '90,30',
'tx.rollback_ok' => '90,31',
'confirm.select' => '85,10',
'confirm.select_ok' => '85,11',
);





public function get_wait($method)
{
return $this->wait[$method];
}
}
<?php



namespace PhpAmqpLib\Helper\Protocol;

class MethodMap080
{



protected $method_map = array(
'10,10' => 'connection_start',
'10,11' => 'connection_start_ok',
'10,20' => 'connection_secure',
'10,21' => 'connection_secure_ok',
'10,30' => 'connection_tune',
'10,31' => 'connection_tune_ok',
'10,40' => 'connection_open',
'10,41' => 'connection_open_ok',
'10,50' => 'connection_redirect',
'10,60' => 'connection_close',
'10,61' => 'connection_close_ok',
'20,10' => 'channel_open',
'20,11' => 'channel_open_ok',
'20,20' => 'channel_flow',
'20,21' => 'channel_flow_ok',
'20,30' => 'channel_alert',
'20,40' => 'channel_close',
'20,41' => 'channel_close_ok',
'30,10' => 'access_request',
'30,11' => 'access_request_ok',
'40,10' => 'exchange_declare',
'40,11' => 'exchange_declare_ok',
'40,20' => 'exchange_delete',
'40,21' => 'exchange_delete_ok',
'50,10' => 'queue_declare',
'50,11' => 'queue_declare_ok',
'50,20' => 'queue_bind',
'50,21' => 'queue_bind_ok',
'50,30' => 'queue_purge',
'50,31' => 'queue_purge_ok',
'50,40' => 'queue_delete',
'50,41' => 'queue_delete_ok',
'50,50' => 'queue_unbind',
'50,51' => 'queue_unbind_ok',
'60,10' => 'basic_qos',
'60,11' => 'basic_qos_ok',
'60,20' => 'basic_consume',
'60,21' => 'basic_consume_ok',
'60,30' => 'basic_cancel',
'60,31' => 'basic_cancel_ok',
'60,40' => 'basic_publish',
'60,50' => 'basic_return',
'60,60' => 'basic_deliver',
'60,70' => 'basic_get',
'60,71' => 'basic_get_ok',
'60,72' => 'basic_get_empty',
'60,80' => 'basic_ack',
'60,90' => 'basic_reject',
'60,100' => 'basic_recover_async',
'60,110' => 'basic_recover',
'60,111' => 'basic_recover_ok',
'70,10' => 'file_qos',
'70,11' => 'file_qos_ok',
'70,20' => 'file_consume',
'70,21' => 'file_consume_ok',
'70,30' => 'file_cancel',
'70,31' => 'file_cancel_ok',
'70,40' => 'file_open',
'70,41' => 'file_open_ok',
'70,50' => 'file_stage',
'70,60' => 'file_publish',
'70,70' => 'file_return',
'70,80' => 'file_deliver',
'70,90' => 'file_ack',
'70,100' => 'file_reject',
'80,10' => 'stream_qos',
'80,11' => 'stream_qos_ok',
'80,20' => 'stream_consume',
'80,21' => 'stream_consume_ok',
'80,30' => 'stream_cancel',
'80,31' => 'stream_cancel_ok',
'80,40' => 'stream_publish',
'80,50' => 'stream_return',
'80,60' => 'stream_deliver',
'90,10' => 'tx_select',
'90,11' => 'tx_select_ok',
'90,20' => 'tx_commit',
'90,21' => 'tx_commit_ok',
'90,30' => 'tx_rollback',
'90,31' => 'tx_rollback_ok',
'100,10' => 'dtx_select',
'100,11' => 'dtx_select_ok',
'100,20' => 'dtx_start',
'100,21' => 'dtx_start_ok',
'110,10' => 'tunnel_request',
'120,10' => 'test_integer',
'120,11' => 'test_integer_ok',
'120,20' => 'test_string',
'120,21' => 'test_string_ok',
'120,30' => 'test_table',
'120,31' => 'test_table_ok',
'120,40' => 'test_content',
'120,41' => 'test_content_ok',
);





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);
}
}
<?php



namespace PhpAmqpLib\Helper\Protocol;

class Wait080
{



protected $wait = array(
'connection.start' => '10,10',
'connection.start_ok' => '10,11',
'connection.secure' => '10,20',
'connection.secure_ok' => '10,21',
'connection.tune' => '10,30',
'connection.tune_ok' => '10,31',
'connection.open' => '10,40',
'connection.open_ok' => '10,41',
'connection.redirect' => '10,50',
'connection.close' => '10,60',
'connection.close_ok' => '10,61',
'channel.open' => '20,10',
'channel.open_ok' => '20,11',
'channel.flow' => '20,20',
'channel.flow_ok' => '20,21',
'channel.alert' => '20,30',
'channel.close' => '20,40',
'channel.close_ok' => '20,41',
'access.request' => '30,10',
'access.request_ok' => '30,11',
'exchange.declare' => '40,10',
'exchange.declare_ok' => '40,11',
'exchange.delete' => '40,20',
'exchange.delete_ok' => '40,21',
'queue.declare' => '50,10',
'queue.declare_ok' => '50,11',
'queue.bind' => '50,20',
'queue.bind_ok' => '50,21',
'queue.purge' => '50,30',
'queue.purge_ok' => '50,31',
'queue.delete' => '50,40',
'queue.delete_ok' => '50,41',
'queue.unbind' => '50,50',
'queue.unbind_ok' => '50,51',
'basic.qos' => '60,10',
'basic.qos_ok' => '60,11',
'basic.consume' => '60,20',
'basic.consume_ok' => '60,21',
'basic.cancel' => '60,30',
'basic.cancel_ok' => '60,31',
'basic.publish' => '60,40',
'basic.return' => '60,50',
'basic.deliver' => '60,60',
'basic.get' => '60,70',
'basic.get_ok' => '60,71',
'basic.get_empty' => '60,72',
'basic.ack' => '60,80',
'basic.reject' => '60,90',
'basic.recover_async' => '60,100',
'basic.recover' => '60,110',
'basic.recover_ok' => '60,111',
'file.qos' => '70,10',
'file.qos_ok' => '70,11',
'file.consume' => '70,20',
'file.consume_ok' => '70,21',
'file.cancel' => '70,30',
'file.cancel_ok' => '70,31',
'file.open' => '70,40',
'file.open_ok' => '70,41',
'file.stage' => '70,50',
'file.publish' => '70,60',
'file.return' => '70,70',
'file.deliver' => '70,80',
'file.ack' => '70,90',
'file.reject' => '70,100',
'stream.qos' => '80,10',
'stream.qos_ok' => '80,11',
'stream.consume' => '80,20',
'stream.consume_ok' => '80,21',
'stream.cancel' => '80,30',
'stream.cancel_ok' => '80,31',
'stream.publish' => '80,40',
'stream.return' => '80,50',
'stream.deliver' => '80,60',
'tx.select' => '90,10',
'tx.select_ok' => '90,11',
'tx.commit' => '90,20',
'tx.commit_ok' => '90,21',
'tx.rollback' => '90,30',
'tx.rollback_ok' => '90,31',
'dtx.select' => '100,10',
'dtx.select_ok' => '100,11',
'dtx.start' => '100,20',
'dtx.start_ok' => '100,21',
'tunnel.request' => '110,10',
'test.integer' => '120,10',
'test.integer_ok' => '120,11',
'test.string' => '120,20',
'test.string_ok' => '120,21',
'test.table' => '120,30',
'test.table_ok' => '120,31',
'test.content' => '120,40',
'test.content_ok' => '120,41',
);





public function get_wait($method)
{
return $this->wait[$method];
}
}
<?php



namespace PhpAmqpLib\Helper\Protocol;

class MethodMap091
{



protected $method_map = array(
'10,10' => 'connection_start',
'10,11' => 'connection_start_ok',
'10,20' => 'connection_secure',
'10,21' => 'connection_secure_ok',
'10,30' => 'connection_tune',
'10,31' => 'connection_tune_ok',
'10,40' => 'connection_open',
'10,41' => 'connection_open_ok',
'10,50' => 'connection_close',
'10,51' => 'connection_close_ok',
'10,60' => 'connection_blocked',
'10,61' => 'connection_unblocked',
'20,10' => 'channel_open',
'20,11' => 'channel_open_ok',
'20,20' => 'channel_flow',
'20,21' => 'channel_flow_ok',
'20,40' => 'channel_close',
'20,41' => 'channel_close_ok',
'30,10' => 'access_request',
'30,11' => 'access_request_ok',
'40,10' => 'exchange_declare',
'40,11' => 'exchange_declare_ok',
'40,20' => 'exchange_delete',
'40,21' => 'exchange_delete_ok',
'40,30' => 'exchange_bind',
'40,31' => 'exchange_bind_ok',
'40,40' => 'exchange_unbind',
'40,51' => 'exchange_unbind_ok',
'50,10' => 'queue_declare',
'50,11' => 'queue_declare_ok',
'50,20' => 'queue_bind',
'50,21' => 'queue_bind_ok',
'50,30' => 'queue_purge',
'50,31' => 'queue_purge_ok',
'50,40' => 'queue_delete',
'50,41' => 'queue_delete_ok',
'50,50' => 'queue_unbind',
'50,51' => 'queue_unbind_ok',
'60,10' => 'basic_qos',
'60,11' => 'basic_qos_ok',
'60,20' => 'basic_consume',
'60,21' => 'basic_consume_ok',
'60,30' => 'basic_cancel_from_server',
'60,31' => 'basic_cancel_ok',
'60,40' => 'basic_publish',
'60,50' => 'basic_return',
'60,60' => 'basic_deliver',
'60,70' => 'basic_get',
'60,71' => 'basic_get_ok',
'60,72' => 'basic_get_empty',
'60,80' => 'basic_ack_from_server',
'60,90' => 'basic_reject',
'60,100' => 'basic_recover_async',
'60,110' => 'basic_recover',
'60,111' => 'basic_recover_ok',
'60,120' => 'basic_nack_from_server',
'90,10' => 'tx_select',
'90,11' => 'tx_select_ok',
'90,20' => 'tx_commit',
'90,21' => 'tx_commit_ok',
'90,30' => 'tx_rollback',
'90,31' => 'tx_rollback_ok',
'85,10' => 'confirm_select',
'85,11' => 'confirm_select_ok',
);





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);
}
}
<?php

namespace PhpAmqpLib\Helper;

class MiscHelper
{




public static function methodSig($a)
{
if (is_string($a)) {
return $a;
}

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









public static function splitSecondsMicroseconds($number)
{
return array((int)floor($number), (int)(fmod($number, 1) * 1000000));
}



















public static function hexdump($data, $htmloutput = true, $uppercase = false, $return = false)
{

$hexi = '';
$ascii = '';
$dump = $htmloutput ? '<pre>' : '';
$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 ? '</pre>' : '';
$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);
}
}
<?php

namespace PhpAmqpLib\Helper;

use InvalidArgumentException;

class Assert
{




public static function isCallable($argument)
{
if (!is_callable($argument)) {
throw new InvalidArgumentException(sprintf(
'Given argument "%s" should be callable. %s type was given.',
$argument,
gettype($argument)
));
}
}
}
<?php

namespace PhpAmqpLib\Helper;














final class SocketConstants
{



private $constants;


private static $instance;

public function __construct()
{
$constants = get_defined_constants(true);
if (isset($constants['sockets'])) {
$this->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;
}
}
<?php

namespace PhpAmqpLib\Helper;

if (class_exists('phpseclib\Math\BigInteger')) {
class BigInteger extends \phpseclib\Math\BigInteger
{
}
} elseif (class_exists('phpseclib3\Math\BigInteger')) {
class BigInteger extends \phpseclib3\Math\BigInteger
{
}
} else {
throw new \RuntimeException('Cannot find supported phpseclib/phpseclib library');
}
<?php

namespace PhpAmqpLib\Wire;

abstract class Constants
{
const VERSION = '';
const AMQP_HEADER = '';




protected static $FRAME_TYPES = array();




protected static $CONTENT_METHODS = array();




protected static $CLOSE_METHODS = array();




public static $GLOBAL_METHOD_NAMES = array();




public function getHeader()
{
return static::AMQP_HEADER;
}





public function isFrameType($type)
{
return array_key_exists($type, static::$FRAME_TYPES);
}





public function getFrameType($type)
{
return static::$FRAME_TYPES[$type];
}





public function isContentMethod($method)
{
return in_array($method, static::$CONTENT_METHODS, false);
}





public function isCloseMethod($method)
{
return in_array($method, static::$CLOSE_METHODS, false);
}
}
<?php



namespace PhpAmqpLib\Wire;

final class Constants091 extends Constants
{
const VERSION = '0.9.1';
const AMQP_HEADER = "AMQP\x00\x00\x09\x01";




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




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',
);
}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Helper\BigInteger;












class AMQPDecimal
{

protected $n;


protected $e;






public function __construct($n, $e)
{
if ($e < 0) {
throw new AMQPOutOfBoundsException('Decimal exponent value must be unsigned!');
}

$this->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;
}
}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPDataReadException;
use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPNoDataException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\IO\AbstractIO;
use RuntimeException;

class AMQPIOReader extends AMQPReader
{

private $io;


protected $timeout;

public function __construct(AbstractIO $io, $timeout = 0)
{
$this->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
));

}
}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPDataReadException;

class AMQPBufferReader extends AMQPReader
{



private $buffer;




private $length;

public function __construct(string $buffer)
{
$this->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;
}

}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception;

class AMQPTable extends AMQPAbstractCollection
{




final public function getType()
{
return self::T_TABLE;
}






public function set($key, $val, $type = null)
{







if (!($len = strlen($key)) || ($len > 128)) {
throw new Exception\AMQPInvalidArgumentException(
'Table key must be non-empty string up to 128 chars in length'
);
}
$this->setValue($val, $type, $key);
}
}
<?php

namespace PhpAmqpLib\Wire;

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




abstract class AMQPAbstractCollection implements \Iterator, \ArrayAccess
{

const PROTOCOL_RBT = 'rabbit'; 


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

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

const T_BOOL = 12;

const T_STRING_SHORT = 13;
const T_STRING_LONG = 14;

const T_ARRAY = 15;
const T_TABLE = 16;

const T_BYTES = 17;

const T_FLOAT = 18;
const T_DOUBLE = 19;




private static $protocol;







private static $types_080 = array(
self::T_INT_LONG => 'I',
self::T_DECIMAL => 'D',
self::T_TIMESTAMP => 'T',
self::T_STRING_LONG => 'S',
self::T_TABLE => 'F'
);




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;
}
}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
use PhpAmqpLib\Exception\AMQPOutOfRangeException;
use PhpAmqpLib\Helper\BigInteger;

class AMQPWriter extends AMQPByteStream
{

protected $out = '';


protected $bits = array();


protected $bitcount = 0;

private function flushbits()
{
if (!empty($this->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
));
}
}
}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Helper\BigInteger;

abstract class AMQPByteStream
{
public const BIT = 1;
public const OCTET = 1;
public const SHORTSTR = 1;
public const SHORT = 2;
public const LONG = 4;
public const SIGNED_LONG = 4;
public const READ_PHP_INT = 4; 
public const LONGLONG = 8;
public const TIMESTAMP = 8;


protected const PLATFORM_64BIT = PHP_INT_SIZE === 8;


protected static $bigIntegers = array();




protected static $isLittleEndian;







protected function correctEndianness($bytes)
{
return self::isLittleEndian() ? $this->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;
}
}
<?php

namespace PhpAmqpLib\Wire\IO;

use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPDataReadException;
use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Helper\SocketConstants;

class StreamIO extends AbstractIO
{

protected $context;


private $sock;











public function __construct(
$host,
$port,
$connection_timeout,
$read_write_timeout,
$context = null,
$keepalive = false,
$heartbeat = 0,
$ssl_protocol = null
) {
if (func_num_args() === 8) {
trigger_error(
'$ssl_protocol parameter is deprecated, use stream_context_set_option($context, \'ssl\', \'crypto_method\', $ssl_protocol) instead (see https://www.php.net/manual/en/function.stream-socket-enable-crypto.php for possible values)',
E_USER_DEPRECATED
);
}









$this->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');
}
}
}
<?php

namespace PhpAmqpLib\Wire\IO;

use PhpAmqpLib\Connection\AMQPConnectionConfig;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPSocketException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Helper\SocketConstants;

class SocketIO extends AbstractIO
{

private $sock;










public function __construct(
$host,
$port,
$read_timeout = 3,
$keepalive = false,
$write_timeout = null,
$heartbeat = 0,
?AMQPConnectionConfig $config = null
) {
$this->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;
}
}
<?php

namespace PhpAmqpLib\Wire\IO;

use PhpAmqpLib\Connection\AMQPConnectionConfig;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPHeartbeatMissedException;
use PhpAmqpLib\Exception\AMQPIOWaitException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Wire\AMQPWriter;

abstract class AbstractIO
{
const BUFFER_SIZE = 8192;


protected $config;


protected $host;


protected $port;


protected $connection_timeout;


protected $read_timeout;


protected $write_timeout;


protected $heartbeat;


protected $initial_heartbeat;


protected $keepalive;


protected $last_read;


protected $last_write;


protected $last_error;


protected $canDispatchPcntlSignal = false;










abstract public function read($len);








abstract public function write($data);




abstract public function close();









public function select(?int $sec, int $usec = 0)
{
$this->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);
}
}
<?php



namespace PhpAmqpLib\Wire;

final class Constants080 extends Constants
{
const VERSION = '8.0';
const AMQP_HEADER = "AMQP\x01\x01\x08\x00";




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




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',
);
}
<?php

namespace PhpAmqpLib\Wire;

class AMQPArray extends AMQPAbstractCollection
{




public function __construct(array $data = null)
{
parent::__construct(empty($data) ? null : array_values($data));
}




final public function getType()
{
return self::T_ARRAY;
}






public function push($val, $type = null)
{
$this->setValue($val, $type);

return $this;
}
}
<?php

namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Channel\Frame;
use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Helper\BigInteger;

abstract class AMQPReader extends AMQPByteStream
{

protected $offset = 0;


protected $bitcount = 0;


protected $bits = 0;




abstract public function close(): void;

abstract protected function rawread(int $n): string;





public function read($n)
{
$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;
}
}
<?php

namespace PhpAmqpLib\Exchange;

final class AMQPExchangeType
{
const DIRECT = 'direct';
const FANOUT = 'fanout';
const TOPIC = 'topic';
const HEADERS = 'headers';
}
<?php

namespace PhpAmqpLib\Channel;

use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPChannelClosedException;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPInvalidFrameException;
use PhpAmqpLib\Exception\AMQPNoDataException;
use PhpAmqpLib\Exception\AMQPNotImplementedException;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Exception\AMQPOutOfRangeException;
use PhpAmqpLib\Helper\DebugHelper;
use PhpAmqpLib\Helper\Protocol\MethodMap080;
use PhpAmqpLib\Helper\Protocol\MethodMap091;
use PhpAmqpLib\Helper\Protocol\Protocol080;
use PhpAmqpLib\Helper\Protocol\Protocol091;
use PhpAmqpLib\Helper\Protocol\Wait080;
use PhpAmqpLib\Helper\Protocol\Wait091;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire;
use PhpAmqpLib\Wire\AMQPReader;

abstract class AbstractChannel
{



const PROTOCOL_080 = Wire\Constants080::VERSION;




const PROTOCOL_091 = Wire\Constants091::VERSION;





protected $frame_queue;





protected $method_queue = array();


protected $auto_decode = false;


protected $constants;


protected $debug;


protected $connection;


protected $protocolVersion;


protected $maxBodySize;


protected $protocolWriter;


protected $waitHelper;


protected $methodMap;


protected $channel_id;


protected $msg_property_reader;


protected $dispatch_reader;






public function __construct(AbstractConnection $connection, $channel_id)
{
$this->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);
}
}
}
<?php

namespace PhpAmqpLib\Channel;
use PhpAmqpLib\Wire\AMQPReader;





final class Frame
{
public const FRAME_HEADER_SIZE = AMQPReader::OCTET + AMQPReader::SHORT + AMQPReader::LONG;
public const END = 0xCE;

public const TYPE_METHOD = 1;
public const TYPE_HEADER = 2;
public const TYPE_BODY = 3;
public const TYPE_HEARTBEAT = 8;


private $type;


private $channel;


private $size;


private $payload;

public function __construct(int $type, int $channel, int $size, ?string $payload = null)
{
$this->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;
}
}
<?php

namespace PhpAmqpLib\Channel;

use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPBasicCancelException;
use PhpAmqpLib\Exception\AMQPChannelClosedException;
use PhpAmqpLib\Exception\AMQPConnectionBlockedException;
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
use PhpAmqpLib\Exception\AMQPNoDataException;
use PhpAmqpLib\Exception\AMQPProtocolChannelException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\Assert;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire;
use PhpAmqpLib\Wire\AMQPReader;
use PhpAmqpLib\Wire\AMQPTable;
use PhpAmqpLib\Wire\AMQPWriter;

class AMQPChannel extends AbstractChannel
{




public $callbacks = array();


protected $is_open = false;


protected $default_ticket = 0;


protected $active = true;


protected $stopConsume = false;


protected $alerts = array();


protected $auto_decode;











protected $basic_return_callback;


protected $batch_messages = array();







private $published_messages = array();


private $next_delivery_tag = 0;


private $ack_handler;


private $nack_handler;









private $publish_cache = array();






private $publish_cache_max_size = 100;





protected $channel_rpc_timeout;











public function __construct($connection, $channel_id = null, $auto_decode = true, $channel_rpc_timeout = 0)
{
if ($channel_id == null) {
$channel_id = $connection->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;
}
}
<?php

namespace PhpAmqpLib\Channel;

final class Method
{

private $class;


private $method;


private $arguments;

public function __construct(int $class, int $method, string $arguments)
{
$this->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;
}
}
<?php

namespace PhpAmqpLib\Exception;




class AMQPConnectionClosedException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPIOWaitException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

interface AMQPExceptionInterface
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPInvalidArgumentException extends \RuntimeException implements AMQPExceptionInterface
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPIOException extends \Exception implements AMQPExceptionInterface
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPSocketException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPNotImplementedException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPProtocolException extends \Exception implements AMQPExceptionInterface
{

public $amqp_reply_code;


public $amqp_reply_text;


public $amqp_method_sig;


public $args;






public function __construct($reply_code, $reply_text, $method_sig)
{
parent::__construct($reply_text, $reply_code);

$this->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);
}
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPLogicException extends \LogicException implements AMQPExceptionInterface
{

}
<?php

namespace PhpAmqpLib\Exception;

class AMQPOutOfBoundsException extends \OutOfBoundsException implements AMQPExceptionInterface
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPOutOfRangeException extends \OutOfRangeException implements AMQPExceptionInterface
{

}
<?php

namespace PhpAmqpLib\Exception;

class AMQPRuntimeException extends \RuntimeException implements AMQPExceptionInterface
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPDataReadException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPChannelClosedException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPConnectionBlockedException extends AMQPRuntimeException
{
public function __construct($message = '', $code = 0, $previous = null)
{
if (empty($message)) {
$message = 'Connection is blocked due to low resources';
}
parent::__construct($message, $code, $previous);
}
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPInvalidFrameException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPProtocolChannelException extends AMQPProtocolException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPTimeoutException extends \RuntimeException implements AMQPExceptionInterface
{



private $timeout;

public function __construct($message = '', $timeout = 0, $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->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;
}
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPEmptyDeliveryTagException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPHeartbeatMissedException extends AMQPConnectionClosedException
{
}
<?php

namespace PhpAmqpLib\Exception;




class AMQPNoDataException extends AMQPRuntimeException
{
}
<?php

namespace PhpAmqpLib\Exception;

class AMQPBasicCancelException extends \Exception implements AMQPExceptionInterface
{




public $consumerTag;




public function __construct($consumerTag)
{
parent::__construct('Channel was canceled');
$this->consumerTag = $consumerTag;
}




public function getConsumerTag()
{
return $this->consumerTag;
}
}
<?php

namespace PhpAmqpLib;

final class Package
{
public const NAME = 'AMQPLib';
public const VERSION = '3.6.0';
}
<?php

namespace Doctrine\Instantiator;

use ArrayIterator;
use Doctrine\Instantiator\Exception\ExceptionInterface;
use Doctrine\Instantiator\Exception\InvalidArgumentException;
use Doctrine\Instantiator\Exception\UnexpectedValueException;
use Exception;
use ReflectionClass;
use ReflectionException;
use Serializable;

use function class_exists;
use function enum_exists;
use function is_subclass_of;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function strlen;
use function unserialize;

use const PHP_VERSION_ID;

final class Instantiator implements InstantiatorInterface
{







public const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C';


public const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O';






private static $cachedInstantiators = [];






private static $cachedCloneables = [];












public function instantiate($className)
{
if (isset(self::$cachedCloneables[$className])) {

$cachedCloneable = self::$cachedCloneables[$className];

return clone $cachedCloneable;
}

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

return $factory();
}

return $this->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);
}
}
<?php

namespace Doctrine\Instantiator;

use Doctrine\Instantiator\Exception\ExceptionInterface;




interface InstantiatorInterface
{











public function instantiate($className);
}
<?php

namespace Doctrine\Instantiator\Exception;

use Throwable;




interface ExceptionInterface extends Throwable
{
}
<?php

namespace Doctrine\Instantiator\Exception;

use InvalidArgumentException as BaseInvalidArgumentException;
use ReflectionClass;

use function interface_exists;
use function sprintf;
use function trait_exists;




class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface
{
public static function fromNonExistingClass(string $className): self
{
if (interface_exists($className)) {
return new self(sprintf('The provided type "%s" is an interface, and cannot be instantiated', $className));
}

if (trait_exists($className)) {
return new self(sprintf('The provided type "%s" is a trait, and cannot be instantiated', $className));
}

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






public static function fromAbstractClass(ReflectionClass $reflectionClass): self
{
return new self(sprintf(
'The provided class "%s" is abstract, and cannot be instantiated',
$reflectionClass->getName()
));
}

public static function fromEnum(string $className): self
{
return new self(sprintf(
'The provided class "%s" is an enum, and cannot be instantiated',
$className
));
}
}
<?php

namespace Doctrine\Instantiator\Exception;

use Exception;
use ReflectionClass;
use UnexpectedValueException as BaseUnexpectedValueException;

use function sprintf;




class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface
{





public static function fromSerializationTriggeredException(
ReflectionClass $reflectionClass,
Exception $exception
): self {
return new self(
sprintf(
'An exception was raised while trying to instantiate an instance of "%s" via un-serialization',
$reflectionClass->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)
);
}
}
<?php

namespace DeepCopy\TypeFilter\Spl;

use Closure;
use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\TypeFilter;
use SplDoublyLinkedList;




class SplDoublyLinkedListFilter implements TypeFilter
{
private $copier;

public function __construct(DeepCopy $copier)
{
$this->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);
}
}
<?php

namespace DeepCopy\TypeFilter\Spl;




class SplDoublyLinkedList extends SplDoublyLinkedListFilter
{
}
<?php
namespace DeepCopy\TypeFilter\Spl;

use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\TypeFilter;





final class ArrayObjectFilter implements TypeFilter
{



private $copier;

public function __construct(DeepCopy $copier)
{
$this->copier = $copier;
}




public function apply($arrayObject)
{
$clone = clone $arrayObject;
foreach ($arrayObject->getArrayCopy() as $k => $v) {
$clone->offsetSet($k, $this->copier->copy($v));
}

return $clone;
}
}

<?php

namespace DeepCopy\TypeFilter;

interface TypeFilter
{





public function apply($element);
}
<?php

namespace DeepCopy\TypeFilter;




class ReplaceFilter implements TypeFilter
{



protected $callback;




public function __construct(callable $callable)
{
$this->callback = $callable;
}




public function apply($element)
{
return call_user_func($this->callback, $element);
}
}
<?php

namespace DeepCopy\TypeFilter;




class ShallowCopyFilter implements TypeFilter
{



public function apply($element)
{
return clone $element;
}
}
<?php

namespace DeepCopy\TypeFilter\Date;

use DateInterval;
use DeepCopy\TypeFilter\TypeFilter;






class DateIntervalFilter implements TypeFilter
{








public function apply($element)
{
$copy = new DateInterval('P0D');

foreach ($element as $propertyName => $propertyValue) {
$copy->{$propertyName} = $propertyValue;
}

return $copy;
}
}
<?php

namespace DeepCopy;

use function function_exists;

if (false === function_exists('DeepCopy\deep_copy')) {








function deep_copy($value, $useCloneMethod = false)
{
return (new DeepCopy($useCloneMethod))->copy($value);
}
}
<?php

namespace DeepCopy\Reflection;

use DeepCopy\Exception\PropertyException;
use ReflectionClass;
use ReflectionException;
use ReflectionObject;
use ReflectionProperty;

class ReflectionHelper
{












public static function getProperties(ReflectionClass $ref)
{
$props = $ref->getProperties();
$propsArr = array();

foreach ($props as $prop) {
$propertyName = $prop->getName();
$propsArr[$propertyName] = $prop;
}

if ($parentClass = $ref->getParentClass()) {
$parentPropsArr = self::getProperties($parentClass);
foreach ($propsArr as $key => $property) {
$parentPropsArr[$key] = $property;
}

return $parentPropsArr;
}

return $propsArr;
}












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
)
);
}
}
<?php

namespace DeepCopy\Matcher;

use DeepCopy\Reflection\ReflectionHelper;
use ReflectionException;









class PropertyTypeMatcher implements Matcher
{



private $propertyType;




public function __construct($propertyType)
{
$this->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;
}
}
<?php

namespace DeepCopy\Matcher;




class PropertyMatcher implements Matcher
{



private $class;




private $property;





public function __construct($class, $property)
{
$this->class = $class;
$this->property = $property;
}






public function matches($object, $property)
{
return ($object instanceof $this->class) && $property == $this->property;
}
}
<?php

namespace DeepCopy\Matcher;




class PropertyNameMatcher implements Matcher
{



private $property;




public function __construct($property)
{
$this->property = $property;
}






public function matches($object, $property)
{
return $property == $this->property;
}
}
<?php

namespace DeepCopy\Matcher\Doctrine;

use DeepCopy\Matcher\Matcher;
use Doctrine\Persistence\Proxy;




class DoctrineProxyMatcher implements Matcher
{





public function matches($object, $property)
{
return $object instanceof Proxy;
}
}
<?php

namespace DeepCopy\Matcher;

interface Matcher
{






public function matches($object, $property);
}
<?php

namespace DeepCopy;

use ArrayObject;
use DateInterval;
use DateTimeInterface;
use DateTimeZone;
use DeepCopy\Exception\CloneException;
use DeepCopy\Filter\ChainableFilter;
use DeepCopy\Filter\Filter;
use DeepCopy\Matcher\Matcher;
use DeepCopy\Reflection\ReflectionHelper;
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
use DeepCopy\TypeFilter\Spl\ArrayObjectFilter;
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
use DeepCopy\TypeFilter\TypeFilter;
use DeepCopy\TypeMatcher\TypeMatcher;
use ReflectionObject;
use ReflectionProperty;
use SplDoublyLinkedList;




class DeepCopy
{



private $hashMap = [];






private $filters = [];






private $typeFilters = [];




private $skipUncloneable = false;




private $useCloneMethod;





public function __construct($useCloneMethod = false)
{
$this->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;
}
}
<?php

namespace DeepCopy\TypeMatcher;

class TypeMatcher
{



private $type;




public function __construct($type)
{
$this->type = $type;
}






public function matches($element)
{
return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type;
}
}
<?php

namespace DeepCopy\Filter;

use DeepCopy\Reflection\ReflectionHelper;




class SetNullFilter implements Filter
{





public function apply($object, $property, $objectCopier)
{
$reflectionProperty = ReflectionHelper::getProperty($object, $property);

$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, null);
}
}
<?php

namespace DeepCopy\Filter;

use DeepCopy\Reflection\ReflectionHelper;




class ReplaceFilter implements Filter
{



protected $callback;




public function __construct(callable $callable)
{
$this->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);
}
}
<?php

namespace DeepCopy\Filter;




class ChainableFilter implements Filter
{



protected $filter;

public function __construct(Filter $filter)
{
$this->filter = $filter;
}

public function apply($object, $property, $objectCopier)
{
$this->filter->apply($object, $property, $objectCopier);
}
}
<?php

namespace DeepCopy\Filter;




interface Filter
{







public function apply($object, $property, $objectCopier);
}
<?php

namespace DeepCopy\Filter;

class KeepFilter implements Filter
{





public function apply($object, $property, $objectCopier)
{

}
}
<?php

namespace DeepCopy\Filter\Doctrine;

use DeepCopy\Filter\Filter;
use DeepCopy\Reflection\ReflectionHelper;
use Doctrine\Common\Collections\ArrayCollection;




class DoctrineEmptyCollectionFilter implements Filter
{







public function apply($object, $property, $objectCopier)
{
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
$reflectionProperty->setAccessible(true);

$reflectionProperty->setValue($object, new ArrayCollection());
}
} <?php

namespace DeepCopy\Filter\Doctrine;

use DeepCopy\Filter\Filter;
use DeepCopy\Reflection\ReflectionHelper;




class DoctrineCollectionFilter implements Filter
{





public function apply($object, $property, $objectCopier)
{
$reflectionProperty = ReflectionHelper::getProperty($object, $property);

$reflectionProperty->setAccessible(true);
$oldCollection = $reflectionProperty->getValue($object);

$newCollection = $oldCollection->map(
function ($item) use ($objectCopier) {
return $objectCopier($item);
}
);

$reflectionProperty->setValue($object, $newCollection);
}
}
<?php

namespace DeepCopy\Filter\Doctrine;

use DeepCopy\Filter\Filter;




class DoctrineProxyFilter implements Filter
{






public function apply($object, $property, $objectCopier)
{
$object->__load();
}
}
<?php

namespace DeepCopy\Exception;

use UnexpectedValueException;

class CloneException extends UnexpectedValueException
{
} <?php

namespace DeepCopy\Exception;

use ReflectionException;

class PropertyException extends ReflectionException
{
}
<?php











namespace Predis\Protocol;

use Predis\Command\CommandInterface;




interface RequestSerializerInterface
{







public function serialize(CommandInterface $command);
}
<?php











namespace Predis\Protocol;

use Predis\CommunicationException;





class ProtocolException extends CommunicationException
{
}
<?php











namespace Predis\Protocol;

use Predis\Command\CommandInterface;
use Predis\Connection\CompositeConnectionInterface;





interface ProtocolProcessorInterface
{






public function write(CompositeConnectionInterface $connection, CommandInterface $command);








public function read(CompositeConnectionInterface $connection);
}
<?php











namespace Predis\Protocol\Text;

use Predis\Command\CommandInterface;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolProcessorInterface;
use Predis\Protocol\RequestSerializerInterface;
use Predis\Protocol\ResponseReaderInterface;







class CompositeProtocolProcessor implements ProtocolProcessorInterface
{



protected $serializer;




protected $reader;





public function __construct(
RequestSerializerInterface $serializer = null,
ResponseReaderInterface $reader = null
) {
$this->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;
}
}
<?php











namespace Predis\Protocol\Text;

use Predis\Command\CommandInterface;
use Predis\Protocol\RequestSerializerInterface;






class RequestSerializer implements RequestSerializerInterface
{



public function serialize(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($argument);
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
}

return $buffer;
}
}
<?php











namespace Predis\Protocol\Text;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ResponseReaderInterface;






class ResponseReader implements ResponseReaderInterface
{
protected $handlers;

public function __construct()
{
$this->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()}]")
);
}
}
<?php











namespace Predis\Protocol\Text;

use Predis\Command\CommandInterface;
use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ProtocolProcessorInterface;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;
use Predis\Response\Status as StatusResponse;






class ProtocolProcessor implements ProtocolProcessorInterface
{
protected $mbiterable;
protected $serializer;

public function __construct()
{
$this->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;
}
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;







class BulkResponse implements ResponseHandlerInterface
{



public function handle(CompositeConnectionInterface $connection, $payload)
{
$length = (int) $payload;

if ("$length" !== $payload) {
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$payload' as a valid length for a bulk response [{$connection->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;
}
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;










class StreamableMultiBulkResponse implements ResponseHandlerInterface
{



public function handle(CompositeConnectionInterface $connection, $payload)
{
$length = (int) $payload;

if ("$length" != $payload) {
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$payload' as a valid length for a multi-bulk response [{$connection->getParameters()}]"
));
}

return new MultiBulkIterator($connection, $length);
}
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\Connection\CompositeConnectionInterface;
use Predis\Response\Error;







class ErrorResponse implements ResponseHandlerInterface
{



public function handle(CompositeConnectionInterface $connection, $payload)
{
return new Error($payload);
}
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;







class MultiBulkResponse implements ResponseHandlerInterface
{



public function handle(CompositeConnectionInterface $connection, $payload)
{
$length = (int) $payload;

if ("$length" !== $payload) {
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$payload' as a valid length of a multi-bulk response [{$connection->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;
}
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\Connection\CompositeConnectionInterface;
use Predis\Response\Status;








class StatusResponse implements ResponseHandlerInterface
{



public function handle(CompositeConnectionInterface $connection, $payload)
{
return Status::get($payload);
}
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\Connection\CompositeConnectionInterface;




interface ResponseHandlerInterface
{









public function handle(CompositeConnectionInterface $connection, $payload);
}
<?php











namespace Predis\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;







class IntegerResponse implements ResponseHandlerInterface
{



public function handle(CompositeConnectionInterface $connection, $payload)
{
if (is_numeric($payload)) {
$integer = (int) $payload;

return $integer == $payload ? $integer : $payload;
}

if ($payload !== 'nil') {
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$payload' as a valid numeric response [{$connection->getParameters()}]"
));
}

return;
}
}
<?php











namespace Predis\Protocol;

use Predis\Connection\CompositeConnectionInterface;





interface ResponseReaderInterface
{







public function read(CompositeConnectionInterface $connection);
}
<?php











namespace Predis\Connection;

trait RelayMethods
{






public function onFlushed(?callable $callback)
{
return $this->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);
}
}
<?php











namespace Predis\Connection;

use Predis\Command\CommandInterface;




interface NodeConnectionInterface extends ConnectionInterface
{





public function __toString();






public function getResource();






public function getParameters();







public function addConnectCommand(CommandInterface $command);






public function read();
}
<?php











namespace Predis\Connection;

use Predis\Command\CommandInterface;





interface AggregateConnectionInterface extends ConnectionInterface
{





public function add(NodeConnectionInterface $connection);








public function remove(NodeConnectionInterface $connection);








public function getConnectionByCommand(CommandInterface $command);








public function getConnectionById($connectionID);
}
<?php











namespace Predis\Connection;

use InvalidArgumentException;
use Predis\Client;
use Predis\Command\RawCommand;
use ReflectionClass;
use UnexpectedValueException;




class Factory implements FactoryInterface
{
private $defaults = [];

protected $schemes = [
'tcp' => '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])
);
}
}
}
<?php











namespace Predis\Connection;

























interface ParametersInterface
{







public function __isset($parameter);








public function __get($parameter);






public function __toString();






public function toArray();
}
<?php











namespace Predis\Connection;




interface FactoryInterface
{






public function define($scheme, $initializer);






public function undefine($scheme);








public function create($parameters);
}
<?php











namespace Predis\Connection;

use InvalidArgumentException;






class Parameters implements ParametersInterface
{
protected static $defaults = [
'scheme' => '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'];
}
}
<?php











namespace Predis\Connection;

use Predis\CommunicationException;




class ConnectionException extends CommunicationException
{
}
<?php











namespace Predis\Connection;

use Predis\Command\CommandInterface;





interface ConnectionInterface
{



public function connect();




public function disconnect();






public function isConnected();






public function writeRequest(CommandInterface $command);








public function readResponse(CommandInterface $command);









public function executeCommand(CommandInterface $command);
}
<?php











namespace Predis\Connection;

use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\Status as StatusResponse;
















class StreamConnection extends AbstractConnection
{





public function __destruct()
{
if (isset($this->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);
}
}
<?php











namespace Predis\Connection;

use InvalidArgumentException;
use Predis\ClientException;
use Predis\Command\CommandInterface;
use Predis\NotSupportedException;
use Predis\Response\ServerException;
use Relay\Exception as RelayException;
use Relay\Relay;






























class RelayConnection extends StreamConnection
{
use RelayMethods;






protected $client;






public $atypicalCommands = [
'AUTH',
'SELECT',

'TYPE',

'MULTI',
'EXEC',
'DISCARD',

'WATCH',
'UNWATCH',

'SUBSCRIBE',
'UNSUBSCRIBE',
'PSUBSCRIBE',
'PUNSUBSCRIBE',
'SSUBSCRIBE',
'SUNSUBSCRIBE',
];




public function __construct(ParametersInterface $parameters)
{
$this->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();
}
}
<?php











namespace Predis\Connection;

use Closure;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\NotSupportedException;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\Status as StatusResponse;






























class PhpiredisStreamConnection extends StreamConnection
{
private $reader;




public function __construct(ParametersInterface $parameters)
{
$this->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();
}
}
<?php











namespace Predis\Connection;

use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Protocol\ProtocolProcessorInterface;
use Predis\Protocol\Text\ProtocolProcessor as TextProtocolProcessor;





class CompositeStreamConnection extends StreamConnection implements CompositeConnectionInterface
{
protected $protocol;





public function __construct(
ParametersInterface $parameters,
ProtocolProcessorInterface $protocol = null
) {
$this->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']);
}
}
<?php











namespace Predis\Connection;

use Closure;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\NotSupportedException;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\Status as StatusResponse;



























class PhpiredisSocketConnection extends AbstractConnection
{
private $reader;




public function __construct(ParametersInterface $parameters)
{
$this->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();
}
}
<?php











namespace Predis\Connection\Replication;

use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\CommunicationException;
use Predis\Connection\ConnectionException;
use Predis\Connection\FactoryInterface as ConnectionFactoryInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Replication\ReplicationStrategy;
use Predis\Replication\RoleException;
use Predis\Response\Error;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;





class SentinelReplication implements ReplicationInterface
{



protected $master;




protected $slaves = [];




protected $pool = [];




protected $current;




protected $service;




protected $connectionFactory;




protected $strategy;




protected $sentinels = [];




protected $sentinelIndex = 0;




protected $sentinelConnection;




protected $sentinelTimeout = 0.100;










protected $retryLimit = 20;







protected $retryWait = 1000;






protected $updateSentinels = false;







public function __construct(
$service,
array $sentinels,
ConnectionFactoryInterface $connectionFactory,
ReplicationStrategy $strategy = null
) {
$this->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',
];
}
}
<?php











namespace Predis\Connection\Replication;

use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\NodeConnectionInterface;




interface ReplicationInterface extends AggregateConnectionInterface
{



public function switchToMaster();




public function switchToSlave();






public function getCurrent();






public function getMaster();






public function getSlaves();
}
<?php











namespace Predis\Connection\Replication;

use InvalidArgumentException;
use Predis\ClientException;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Connection\ConnectionException;
use Predis\Connection\FactoryInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\Replication\MissingMasterException;
use Predis\Replication\ReplicationStrategy;
use Predis\Response\ErrorInterface as ResponseErrorInterface;





class MasterSlaveReplication implements ReplicationInterface
{



protected $strategy;




protected $master;




protected $slaves = [];




protected $pool = [];




protected $aliases = [];




protected $current;




protected $autoDiscovery = false;




protected $connectionFactory;




public function __construct(ReplicationStrategy $strategy = null)
{
$this->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<host>.*),port=(?P<port>\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'];
}
}
<?php











namespace Predis\Connection;





interface CompositeConnectionInterface extends NodeConnectionInterface
{



public function getProtocol();






public function writeBuffer($buffer);








public function readBuffer($length);






public function readLine();
}
<?php











namespace Predis\Connection;

use Closure;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\NotSupportedException;
use Predis\Protocol\ProtocolException;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\Status as StatusResponse;


























class WebdisConnection implements NodeConnectionInterface
{
private $parameters;
private $resource;
private $reader;






public function __construct(ParametersInterface $parameters)
{
$this->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();
}
}
<?php











namespace Predis\Connection\Cluster;

use ArrayIterator;
use Countable;
use IteratorAggregate;
use OutOfBoundsException;
use Predis\ClientException;
use Predis\Cluster\RedisStrategy as RedisClusterStrategy;
use Predis\Cluster\SlotMap;
use Predis\Cluster\StrategyInterface;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Connection\ConnectionException;
use Predis\Connection\FactoryInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\NotSupportedException;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
use ReturnTypeWillChange;
use Throwable;
use Traversable;





















class RedisCluster implements ClusterInterface, IteratorAggregate, Countable
{
private $useClusterSlots = true;
private $pool = [];
private $slots = [];
private $slotmap;
private $strategy;
private $connections;
private $retryLimit = 5;
private $retryInterval = 10;





public function __construct(
FactoryInterface $connections,
StrategyInterface $strategy = null
) {
$this->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;
}
}
<?php











namespace Predis\Connection\Cluster;

use ArrayIterator;
use Countable;
use IteratorAggregate;
use Predis\Cluster\PredisStrategy;
use Predis\Cluster\StrategyInterface;
use Predis\Command\CommandInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\NotSupportedException;
use ReturnTypeWillChange;
use Traversable;





class PredisCluster implements ClusterInterface, IteratorAggregate, Countable
{



private $pool = [];




private $aliases = [];




private $strategy;




private $distributor;




public function __construct(StrategyInterface $strategy = null)
{
$this->strategy = $strategy ?: new PredisStrategy();
$this->distributor = $this->strategy->getDistributor();
}




public function isConnected()
{
foreach ($this->pool as $connection) {
if ($connection->isConnected()) {
return true;
}
}

return false;
}




public function connect()
{
foreach ($this->pool as $connection) {
$connection->connect();
}
}




public function disconnect()
{
foreach ($this->pool as $connection) {
$connection->disconnect();
}
}




public function add(NodeConnectionInterface $connection)
{
$parameters = $connection->getParameters();

$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);
}
}
<?php











namespace Predis\Connection\Cluster;

use Predis\Connection\AggregateConnectionInterface;





interface ClusterInterface extends AggregateConnectionInterface
{
}
<?php











namespace Predis\Connection;

use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\CommunicationException;
use Predis\Protocol\ProtocolException;





abstract class AbstractConnection implements NodeConnectionInterface
{
private $resource;
private $cachedId;

protected $parameters;




protected $initCommands = [];




public function __construct(ParametersInterface $parameters)
{
$this->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'];
}
}
<?php











namespace Predis;

class ClientConfiguration
{



private static $config = [
'modules' => [
['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'];
}
}
<?php











namespace Predis;





class NotSupportedException extends PredisException
{
}
<?php











namespace Predis;








class Autoloader
{
private $directory;
private $prefix;
private $prefixLength;




public function __construct($baseDirectory = __DIR__)
{
$this->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;
}
}
}
}
<?php











namespace Predis;

use Exception;




abstract class PredisException extends Exception
{
}
<?php











namespace Predis\Collection\Iterator;

use Predis\ClientInterface;







class Keyspace extends CursorBasedIterator
{



public function __construct(ClientInterface $client, $match = null, $count = null)
{
$this->requiredCommand($client, 'SCAN');

parent::__construct($client, $match, $count);
}




protected function executeCommand()
{
return $this->client->scan($this->cursor, $this->getScanOptions());
}
}
<?php











namespace Predis\Collection\Iterator;

use InvalidArgumentException;
use Iterator;
use Predis\ClientInterface;
use Predis\NotSupportedException;
use ReturnTypeWillChange;













class ListKey implements Iterator
{
protected $client;
protected $count;
protected $key;

protected $valid;
protected $fetchmore;
protected $elements;
protected $position;
protected $current;








public function __construct(ClientInterface $client, $key, $count = 10)
{
$this->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;
}
}
<?php











namespace Predis\Collection\Iterator;

use Predis\ClientInterface;







class SortedSetKey extends CursorBasedIterator
{
protected $key;




public function __construct(ClientInterface $client, $key, $match = null, $count = null)
{
$this->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]);
}
}
<?php











namespace Predis\Collection\Iterator;

use Predis\ClientInterface;







class HashKey extends CursorBasedIterator
{
protected $key;




public function __construct(ClientInterface $client, $key, $match = null, $count = null)
{
$this->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]);
}
}
<?php











namespace Predis\Collection\Iterator;

use Predis\ClientInterface;







class SetKey extends CursorBasedIterator
{
protected $key;




public function __construct(ClientInterface $client, $key, $match = null, $count = null)
{
$this->requiredCommand($client, 'SSCAN');

parent::__construct($client, $match, $count);

$this->key = $key;
}




protected function executeCommand()
{
return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions());
}
}
<?php











namespace Predis\Collection\Iterator;

use Iterator;
use Predis\ClientInterface;
use Predis\NotSupportedException;
use ReturnTypeWillChange;












abstract class CursorBasedIterator implements Iterator
{
protected $client;
protected $match;
protected $count;

protected $valid;
protected $fetchmore;
protected $elements;
protected $cursor;
protected $position;
protected $current;






public function __construct(ClientInterface $client, $match = null, $count = null)
{
$this->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;
}
}
<?php











namespace Predis\Response;





class Error implements ErrorInterface
{
private $message;




public function __construct($message)
{
$this->message = $message;
}




public function getMessage()
{
return $this->message;
}




public function getErrorType()
{
[$errorType] = explode(' ', $this->getMessage(), 2);

return $errorType;
}






public function __toString()
{
return $this->getMessage();
}
}
<?php











namespace Predis\Response;




interface ResponseInterface
{
}
<?php











namespace Predis\Response;




class Status implements ResponseInterface
{
private static $OK;
private static $QUEUED;

private $payload;




public function __construct($payload)
{
$this->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);
}
}
}
<?php











namespace Predis\Response;





interface ErrorInterface extends ResponseInterface
{





public function getMessage();






public function getErrorType();
}
<?php











namespace Predis\Response;

use Predis\PredisException;




class ServerException extends PredisException implements ErrorInterface
{





public function getErrorType()
{
[$errorType] = explode(' ', $this->getMessage(), 2);

return $errorType;
}






public function toErrorResponse()
{
return new Error($this->getMessage());
}
}
<?php











namespace Predis\Response\Iterator;

use Predis\Connection\NodeConnectionInterface;




class MultiBulk extends MultiBulkIterator
{
private $connection;





public function __construct(NodeConnectionInterface $connection, $size)
{
$this->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();
}
}
<?php











namespace Predis\Response\Iterator;

use InvalidArgumentException;
use OuterIterator;
use ReturnTypeWillChange;
use UnexpectedValueException;








class MultiBulkTuple extends MultiBulk implements OuterIterator
{
private $iterator;




public function __construct(MultiBulk $iterator)
{
$this->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];
}
}
<?php











namespace Predis\Response\Iterator;

use Countable;
use Iterator;
use Predis\Response\ResponseInterface;
use ReturnTypeWillChange;











abstract class MultiBulkIterator implements Iterator, Countable, ResponseInterface
{
protected $current;
protected $position;
protected $size;




#[ReturnTypeWillChange]
public function rewind()
{

}




#[ReturnTypeWillChange]
public function current()
{
return $this->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();
}
<?php











namespace Predis\Session;

use Predis\ClientInterface;
use ReturnTypeWillChange;
use SessionHandlerInterface;









class Handler implements SessionHandlerInterface
{
protected $client;
protected $ttl;





public function __construct(ClientInterface $client, array $options = [])
{
$this->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;
}
}
<?php











namespace Predis\Transaction;




class MultiExecState
{
public const INITIALIZED = 1; 
public const INSIDEBLOCK = 2; 
public const DISCARDED = 4; 
public const CAS = 8; 
public const WATCH = 16; 

private $flags;

public function __construct()
{
$this->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);
}
}
<?php











namespace Predis\Transaction;

use Exception;
use InvalidArgumentException;
use Predis\ClientContextInterface;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Command\CommandInterface;
use Predis\CommunicationException;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\Connection\RelayConnection;
use Predis\NotSupportedException;
use Predis\Protocol\ProtocolException;
use Predis\Response\Error;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
use Predis\Response\Status as StatusResponse;
use Relay\Exception as RelayException;
use Relay\Relay;
use SplQueue;






class MultiExec implements ClientContextInterface
{
private $state;

protected $client;
protected $commands;
protected $exceptions = true;
protected $attempts = 0;
protected $watchKeys = [];
protected $modeCAS = false;





public function __construct(ClientInterface $client, array $options = null)
{
$this->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
));
}
}
<?php











namespace Predis\Transaction;

use Predis\PredisException;




class AbortedMultiExecException extends PredisException
{
private $transaction;






public function __construct(MultiExec $transaction, $message, $code = 0)
{
parent::__construct($message, is_null($code) ? 0 : $code);

$this->transaction = $transaction;
}






public function getTransaction()
{
return $this->transaction;
}
}
<?php











namespace Predis;

use Exception;
use Predis\Connection\NodeConnectionInterface;




abstract class CommunicationException extends PredisException
{
private $connection;







public function __construct(
NodeConnectionInterface $connection,
$message = '',
$code = 0,
Exception $innerException = null
) {
parent::__construct(
is_null($message) ? '' : $message,
is_null($code) ? 0 : $code,
$innerException
);

$this->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;
}
}
<?php











namespace Predis;




class ClientException extends PredisException
{
}
<?php











namespace Predis;

use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Search\AggregateArguments;
use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
use Predis\Command\Argument\Search\ExplainArguments;
use Predis\Command\Argument\Search\ProfileArguments;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Argument\Search\SearchArguments;
use Predis\Command\Argument\Search\SugAddArguments;
use Predis\Command\Argument\Search\SugGetArguments;
use Predis\Command\Argument\Search\SynUpdateArguments;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\Argument\TimeSeries\AddArguments;
use Predis\Command\Argument\TimeSeries\AlterArguments as TSAlterArguments;
use Predis\Command\Argument\TimeSeries\CreateArguments as TSCreateArguments;
use Predis\Command\Argument\TimeSeries\DecrByArguments;
use Predis\Command\Argument\TimeSeries\GetArguments;
use Predis\Command\Argument\TimeSeries\IncrByArguments;
use Predis\Command\Argument\TimeSeries\InfoArguments;
use Predis\Command\Argument\TimeSeries\MGetArguments;
use Predis\Command\Argument\TimeSeries\MRangeArguments;
use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Command\Redis\Container\ACL;
use Predis\Command\Redis\Container\CLUSTER;
use Predis\Command\Redis\Container\FunctionContainer;
use Predis\Command\Redis\Container\Json\JSONDEBUG;
use Predis\Command\Redis\Container\Search\FTCONFIG;
use Predis\Command\Redis\Container\Search\FTCURSOR;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;






























































































































































































































































































































interface ClientInterface
{





public function getCommandFactory();






public function getOptions();




public function connect();




public function disconnect();






public function getConnection();









public function createCommand($method, $arguments = []);








public function executeCommand(CommandInterface $command);










public function __call($method, $arguments);
}
<?php











namespace Predis\PubSub;

use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Command\Command;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\NotSupportedException;




class Consumer extends AbstractConsumer
{
protected $client;
protected $options;





public function __construct(ClientInterface $client, array $options = null)
{
$this->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."
);
}
}
}
<?php











namespace Predis\PubSub;

use InvalidArgumentException;





class DispatcherLoop
{
private $pubsub;

protected $callbacks;
protected $defaultCallback;
protected $subscriptionCallback;




public function __construct(Consumer $pubsub)
{
$this->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 '';
}
}
<?php











namespace Predis\PubSub;

use Iterator;
use ReturnTypeWillChange;




abstract class AbstractConsumer implements Iterator
{
public const SUBSCRIBE = 'subscribe';
public const UNSUBSCRIBE = 'unsubscribe';
public const PSUBSCRIBE = 'psubscribe';
public const PUNSUBSCRIBE = 'punsubscribe';
public const MESSAGE = 'message';
public const PMESSAGE = 'pmessage';
public const PONG = 'pong';

public const STATUS_VALID = 1; 
public const STATUS_SUBSCRIBED = 2; 
public const STATUS_PSUBSCRIBED = 4; 

protected $position;
protected $statusFlags = self::STATUS_VALID;




public function __destruct()
{
$this->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();
}
<?php











namespace Predis\PubSub;

use Predis\NotSupportedException;




class RelayConsumer extends Consumer
{






public function subscribe($channel) 
{
$channels = func_get_args();
$callback = array_pop($channels);

$this->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()
{

}
}
<?php











namespace Predis\Monitor;

use Iterator;
use Predis\ClientInterface;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\NotSupportedException;
use ReturnTypeWillChange;




class Consumer implements Iterator
{
private $client;
private $valid;
private $position;




public function __construct(ClientInterface $client)
{
$this->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,
];
}
}
<?php











namespace Predis\Command\Processor;

use ArrayAccess;
use ArrayIterator;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use ReturnTypeWillChange;
use Traversable;




class ProcessorChain implements ArrayAccess, ProcessorInterface
{
private $processors = [];




public function __construct($processors = [])
{
foreach ($processors as $processor) {
$this->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);
}
}
<?php











namespace Predis\Command\Processor;

use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;





class KeyPrefixProcessor implements ProcessorInterface
{
private $prefix;
private $commands;




public function __construct($prefix)
{
$this->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);
}
}
}
<?php











namespace Predis\Command\Processor;

use Predis\Command\CommandInterface;




interface ProcessorInterface
{





public function process(CommandInterface $command);
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PSUBSCRIBE extends RedisCommand
{



public function getId()
{
return 'PSUBSCRIBE';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;




class ZREVRANGEBYLEX extends ZRANGEBYLEX
{



public function getId()
{
return 'ZREVRANGEBYLEX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Redis\AbstractCommand\BZPOPBase;











class BZPOPMIN extends BZPOPBase
{
public function getId(): string
{
return 'BZPOPMIN';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class MSET extends RedisCommand
{



public function getId()
{
return 'MSET';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 1 && is_array($arguments[0])) {
$flattenedKVs = [];
$args = $arguments[0];

foreach ($args as $k => $v) {
$flattenedKVs[] = $k;
$flattenedKVs[] = $v;
}

$arguments = $flattenedKVs;
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class DECRBY extends RedisCommand
{



public function getId()
{
return 'DECRBY';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class CLUSTER extends RedisCommand
{
public function getId()
{
return 'CLUSTER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class EXEC extends RedisCommand
{



public function getId()
{
return 'EXEC';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BRPOPLPUSH extends RedisCommand
{



public function getId()
{
return 'BRPOPLPUSH';
}
}
<?php











namespace Predis\Command\Redis;






class BZMPOP extends ZMPOP
{
protected static $keysArgumentPositionOffset = 1;
protected static $countArgumentPositionOffset = 3;
protected static $modifierArgumentPositionOffset = 2;

public function getId()
{
return 'BZMPOP';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class XDEL extends RedisCommand
{



public function getId()
{
return 'XDEL';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;







class PEXPIRETIME extends RedisCommand
{
public function getId()
{
return 'PEXPIRETIME';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZREVRANK extends RedisCommand
{



public function getId()
{
return 'ZREVRANK';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class MIGRATE extends RedisCommand
{



public function getId()
{
return 'MIGRATE';
}




public function setArguments(array $arguments)
{
if (is_array(end($arguments))) {
foreach (array_pop($arguments) as $modifier => $value) {
$modifier = strtoupper($modifier);

if ($modifier === 'COPY' && $value == true) {
$arguments[] = $modifier;
}

if ($modifier === 'REPLACE' && $value == true) {
$arguments[] = $modifier;
}
}
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RPOPLPUSH extends RedisCommand
{



public function getId()
{
return 'RPOPLPUSH';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SUBSCRIBE extends RedisCommand
{



public function getId()
{
return 'SUBSCRIBE';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;







class BFSCANDUMP extends RedisCommand
{
public function getId()
{
return 'BF.SCANDUMP';
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;







class BFMADD extends RedisCommand
{
public function getId()
{
return 'BF.MADD';
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BloomFilters\Expansion;










class BFRESERVE extends RedisCommand
{
use Expansion {
Expansion::setArguments as setExpansion;
}

protected static $expansionArgumentPositionOffset = 3;

public function getId()
{
return 'BF.RESERVE';
}

public function setArguments(array $arguments)
{
if (array_key_exists(4, $arguments) && $arguments[4]) {
$arguments[4] = 'NONSCALING';
}

$this->setExpansion($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;






class BFMEXISTS extends RedisCommand
{
public function getId()
{
return 'BF.MEXISTS';
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;
use UnexpectedValueException;






class BFINFO extends RedisCommand
{



private $modifierEnum = [
'capacity' => '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;
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;







class BFADD extends RedisCommand
{
public function getId()
{
return 'BF.ADD';
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;






class BFEXISTS extends RedisCommand
{
public function getId()
{
return 'BF.EXISTS';
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BloomFilters\Capacity;
use Predis\Command\Traits\BloomFilters\Error;
use Predis\Command\Traits\BloomFilters\Expansion;
use Predis\Command\Traits\BloomFilters\Items;
use Predis\Command\Traits\BloomFilters\NoCreate;

class BFINSERT extends RedisCommand
{
use Capacity {
Capacity::setArguments as setCapacity;
}
use Error {
Error::setArguments as setErrorRate;
}
use Expansion {
Expansion::setArguments as setExpansion;
}
use Items {
Items::setArguments as setItems;
}
use NoCreate {
NoCreate::setArguments as setNoCreate;
}

protected static $capacityArgumentPositionOffset = 1;
protected static $errorArgumentPositionOffset = 2;
protected static $expansionArgumentPositionOffset = 3;
protected static $noCreateArgumentPositionOffset = 4;
protected static $itemsArgumentPositionOffset = 6;

public function getId()
{
return 'BF.INSERT';
}

public function setArguments(array $arguments)
{
$this->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();
}
}
<?php











namespace Predis\Command\Redis\BloomFilter;

use Predis\Command\Command as RedisCommand;






class BFLOADCHUNK extends RedisCommand
{
public function getId()
{
return 'BF.LOADCHUNK';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class DBSIZE extends RedisCommand
{



public function getId()
{
return 'DBSIZE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HGET extends RedisCommand
{



public function getId()
{
return 'HGET';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as BaseCommand;




class COMMAND extends BaseCommand
{



public function getId()
{
return 'COMMAND';
}




public function parseResponse($data)
{



return $data;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SCARD extends RedisCommand
{



public function getId()
{
return 'SCARD';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SPOP extends RedisCommand
{



public function getId()
{
return 'SPOP';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZCARD extends RedisCommand
{



public function getId()
{
return 'ZCARD';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class DEL extends RedisCommand
{



public function getId()
{
return 'DEL';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SENTINEL extends RedisCommand
{



public function getId()
{
return 'SENTINEL';
}




public function parseResponse($data)
{
$argument = $this->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;
}
}
<?php











namespace Predis\Command\Redis;









class GEORADIUSBYMEMBER extends GEORADIUS
{



public function getId()
{
return 'GEORADIUSBYMEMBER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LPUSHX extends RedisCommand
{



public function getId()
{
return 'LPUSHX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SRANDMEMBER extends RedisCommand
{



public function getId()
{
return 'SRANDMEMBER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SCRIPT extends RedisCommand
{



public function getId()
{
return 'SCRIPT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BGSAVE extends RedisCommand
{



public function getId()
{
return 'BGSAVE';
}




public function parseResponse($data)
{
return $data === 'Background saving started' ? true : $data;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GETSET extends RedisCommand
{



public function getId()
{
return 'GETSET';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class DECR extends RedisCommand
{



public function getId()
{
return 'DECR';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RPOP extends RedisCommand
{



public function getId()
{
return 'RPOP';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RESTORE extends RedisCommand
{



public function getId()
{
return 'RESTORE';
}
}
<?php











namespace Predis\Command\Redis;




class ZREVRANGE extends ZRANGE
{



public function getId()
{
return 'ZREVRANGE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RANDOMKEY extends RedisCommand
{



public function getId()
{
return 'RANDOMKEY';
}




public function parseResponse($data)
{
return $data !== '' ? $data : null;
}
}
<?php











namespace Predis\Command\Redis;




class ZREVRANGEBYSCORE extends ZRANGEBYSCORE
{



public function getId()
{
return 'ZREVRANGEBYSCORE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PEXPIRE extends RedisCommand
{



public function getId()
{
return 'PEXPIRE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class AUTH extends RedisCommand
{



public function getId()
{
return 'AUTH';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SHUTDOWN extends RedisCommand
{



public function getId()
{
return 'SHUTDOWN';
}




public function setArguments(array $arguments)
{
if (empty($arguments)) {
parent::setArguments($arguments);

return;
}

$processedArguments = [];

if (array_key_exists(0, $arguments) && null !== $arguments[0]) {
$processedArguments[] = ($arguments[0]) ? 'SAVE' : 'NOSAVE';
}

if (array_key_exists(1, $arguments) && false !== $arguments[1]) {
$processedArguments[] = 'NOW';
}

if (array_key_exists(2, $arguments) && false !== $arguments[2]) {
$processedArguments[] = 'FORCE';
}

if (array_key_exists(3, $arguments) && false !== $arguments[3]) {
$processedArguments[] = 'ABORT';
}

parent::setArguments($processedArguments);
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Command as RedisCommand;






class FTCREATE extends RedisCommand
{
public function getId()
{
return 'FT.CREATE';
}

public function setArguments(array $arguments)
{
[$index, $schema] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->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
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTALIASADD extends RedisCommand
{
public function getId()
{
return 'FT.ALIASADD';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTINFO extends RedisCommand
{
public function getId()
{
return 'FT.INFO';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTTAGVALS extends RedisCommand
{
public function getId()
{
return 'FT.TAGVALS';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Command as RedisCommand;

class FTALTER extends RedisCommand
{
public function getId()
{
return 'FT.ALTER';
}

public function setArguments(array $arguments)
{
[$index, $schema] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->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
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSYNUPDATE extends RedisCommand
{
public function getId()
{
return 'FT.SYNUPDATE';
}

public function setArguments(array $arguments)
{
[$index, $synonymGroupId] = $arguments;
$commandArguments = [];

if (!empty($arguments[2])) {
$commandArguments = $arguments[2]->toArray();
}

$terms = array_slice($arguments, 3);

parent::setArguments(array_merge(
[$index, $synonymGroupId],
$commandArguments,
$terms
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;







class FTALIASUPDATE extends RedisCommand
{
public function getId()
{
return 'FT.ALIASUPDATE';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTEXPLAIN extends RedisCommand
{
public function getId()
{
return 'FT.EXPLAIN';
}

public function setArguments(array $arguments)
{
[$index, $query] = $arguments;
$commandArguments = [];

if (!empty($arguments[2])) {
$commandArguments = $arguments[2]->toArray();
}

parent::setArguments(array_merge(
[$index, $query],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSUGADD extends RedisCommand
{
public function getId()
{
return 'FT.SUGADD';
}

public function setArguments(array $arguments)
{
[$key, $string, $score] = $arguments;
$commandArguments = (!empty($arguments[3])) ? $arguments[3]->toArray() : [];

parent::setArguments(array_merge(
[$key, $string, $score],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSUGLEN extends RedisCommand
{
public function getId()
{
return 'FT.SUGLEN';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTDICTADD extends RedisCommand
{
public function getId()
{
return 'FT.DICTADD';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;

class FTCURSOR extends RedisCommand
{
public function getId()
{
return 'FT.CURSOR';
}

public function setArguments(array $arguments)
{
[$subcommand, $index, $cursorId] = $arguments;
$commandArguments = (!empty($arguments[3])) ? $arguments[3]->toArray() : [];

parent::setArguments(array_merge(
[$subcommand, $index, $cursorId],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTPROFILE extends RedisCommand
{
public function getId()
{
return 'FT.PROFILE';
}

public function setArguments(array $arguments)
{
[$index, $arguments] = $arguments;

parent::setArguments(array_merge(
[$index],
$arguments->toArray()
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;

class FTDROPINDEX extends RedisCommand
{
public function getId()
{
return 'FT.DROPINDEX';
}

public function setArguments(array $arguments)
{
[$index] = $arguments;
$commandArguments = [];

if (!empty($arguments[1])) {
$commandArguments = $arguments[1]->toArray();
}

parent::setArguments(array_merge(
[$index],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSYNDUMP extends RedisCommand
{
public function getId()
{
return 'FT.SYNDUMP';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;







class FTAGGREGATE extends RedisCommand
{
public function getId()
{
return 'FT.AGGREGATE';
}

public function setArguments(array $arguments)
{
[$index, $query] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];

parent::setArguments(array_merge(
[$index, $query],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;

class FTSPELLCHECK extends RedisCommand
{
public function getId()
{
return 'FT.SPELLCHECK';
}

public function setArguments(array $arguments)
{
[$index, $query] = $arguments;
$commandArguments = [];

if (!empty($arguments[2])) {
$commandArguments = $arguments[2]->toArray();
}

parent::setArguments(array_merge(
[$index, $query],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSEARCH extends RedisCommand
{
public function getId()
{
return 'FT.SEARCH';
}

public function setArguments(array $arguments)
{
[$index, $query] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];

parent::setArguments(array_merge(
[$index, $query],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTALIASDEL extends RedisCommand
{
public function getId()
{
return 'FT.ALIASDEL';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;








class FTCONFIG extends RedisCommand
{
public function getId()
{
return 'FT.CONFIG';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSUGGET extends RedisCommand
{
public function getId()
{
return 'FT.SUGGET';
}

public function setArguments(array $arguments)
{
[$key, $prefix] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];

parent::setArguments(array_merge(
[$key, $prefix],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTDICTDEL extends RedisCommand
{
public function getId()
{
return 'FT.DICTDEL';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTDICTDUMP extends RedisCommand
{
public function getId()
{
return 'FT.DICTDUMP';
}
}
<?php











namespace Predis\Command\Redis\Search;

use Predis\Command\Command as RedisCommand;






class FTSUGDEL extends RedisCommand
{
public function getId()
{
return 'FT.SUGDEL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LTRIM extends RedisCommand
{



public function getId()
{
return 'LTRIM';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PEXPIREAT extends RedisCommand
{



public function getId()
{
return 'PEXPIREAT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZREM extends RedisCommand
{



public function getId()
{
return 'ZREM';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PSETEX extends RedisCommand
{



public function getId()
{
return 'PSETEX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class KEYS extends RedisCommand
{



public function getId()
{
return 'KEYS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Aggregate;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\Weights;




class ZUNIONSTORE extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Weights {
Weights::setArguments as setWeights;
}
use Aggregate{
Aggregate::setArguments as setAggregate;
}

protected static $keysArgumentPositionOffset = 1;
protected static $weightsArgumentPositionOffset = 2;
protected static $aggregateArgumentPositionOffset = 3;




public function getId()
{
return 'ZUNIONSTORE';
}




public function setArguments(array $arguments)
{

if (!isset($arguments[3]) && (isset($arguments[2]['weights']) || isset($arguments[2]['aggregate']))) {
$options = array_pop($arguments);
array_push($arguments, $options['weights'] ?? []);
array_push($arguments, $options['aggregate'] ?? 'sum');
}

$this->setAggregate($arguments);
$arguments = $this->getArguments();

$this->setWeights($arguments);
$arguments = $this->getArguments();

$this->setKeys($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HSETNX extends RedisCommand
{



public function getId()
{
return 'HSETNX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LRANGE extends RedisCommand
{



public function getId()
{
return 'LRANGE';
}
}
<?php











namespace Predis\Command\Redis;

class BLMOVE extends LMOVE
{
public function getId()
{
return 'BLMOVE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class XLEN extends RedisCommand
{



public function getId()
{
return 'XLEN';
}
}
<?php











namespace Predis\Command\Redis;

class BLMPOP extends LMPOP
{
protected static $keysArgumentPositionOffset = 1;
protected static $leftRightArgumentPositionOffset = 2;
protected static $countArgumentPositionOffset = 3;

public function getId()
{
return 'BLMPOP';
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;








class TDIGESTCDF extends RedisCommand
{
public function getId()
{
return 'TDIGEST.CDF';
}




public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}


return array_map(function ($value) {
if (is_string($value) || !is_float($value)) {
return $value;
}

if (is_nan($value)) {
return 'nan';
}

switch ($value) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $value;
}
}, $data);
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTMERGE extends RedisCommand
{
public function getId()
{
return 'TDIGEST.MERGE';
}

public function setArguments(array $arguments)
{
$processedArguments = array_merge([$arguments[0], count($arguments[1])], $arguments[1]);

for ($i = 2, $iMax = count($arguments); $i < $iMax; $i++) {
if (is_int($arguments[$i]) && $arguments[$i] !== 0) {
array_push($processedArguments, 'COMPRESSION', $arguments[$i]);
} elseif (is_bool($arguments[$i]) && $arguments[$i]) {
$processedArguments[] = 'OVERRIDE';
}
}

parent::setArguments($processedArguments);
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTCREATE extends RedisCommand
{
public function getId()
{
return 'TDIGEST.CREATE';
}

public function setArguments(array $arguments)
{
if (!empty($arguments[1])) {
$arguments[2] = $arguments[1];
$arguments[1] = 'COMPRESSION';
} elseif (array_key_exists(1, $arguments) && $arguments[1] < 1) {
array_pop($arguments);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTBYREVRANK extends RedisCommand
{
public function getId()
{
return 'TDIGEST.BYREVRANK';
}




public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}


return array_map(function ($value) {
if (is_string($value) || !is_float($value)) {
return $value;
}

if (is_nan($value)) {
return 'nan';
}

switch ($value) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $value;
}
}, $data);
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTINFO extends RedisCommand
{
public function getId()
{
return 'TDIGEST.INFO';
}

public function parseResponse($data)
{
$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;
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;







class TDIGESTTRIMMED_MEAN extends RedisCommand
{
public function getId()
{
return 'TDIGEST.TRIMMED_MEAN';
}




public function parseResponse($data)
{
if (is_string($data) || !is_float($data)) {
return $data;
}


if (is_nan($data)) {
return 'nan';
}

switch ($data) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $data;
}
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTMIN extends RedisCommand
{
public function getId()
{
return 'TDIGEST.MIN';
}




public function parseResponse($data)
{
if (is_string($data) || !is_float($data)) {
return $data;
}


if (is_nan($data)) {
return 'nan';
}

switch ($data) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $data;
}
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTBYRANK extends RedisCommand
{
public function getId()
{
return 'TDIGEST.BYRANK';
}




public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}


return array_map(function ($value) {
if (is_string($value) || !is_float($value)) {
return $value;
}

if (is_nan($value)) {
return 'nan';
}

switch ($value) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $value;
}
}, $data);
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTQUANTILE extends RedisCommand
{
public function getId()
{
return 'TDIGEST.QUANTILE';
}




public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}


return array_map(function ($value) {
if (is_string($value) || !is_float($value)) {
return $value;
}

if (is_nan($value)) {
return 'nan';
}

switch ($value) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $value;
}
}, $data);
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTADD extends RedisCommand
{
public function getId()
{
return 'TDIGEST.ADD';
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTMAX extends RedisCommand
{
public function getId()
{
return 'TDIGEST.MAX';
}




public function parseResponse($data)
{
if (is_string($data) || !is_float($data)) {
return $data;
}


if (is_nan($data)) {
return 'nan';
}

switch ($data) {
case INF: return 'inf';
case -INF: return '-inf';
default: return $data;
}
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;








class TDIGESTREVRANK extends RedisCommand
{
public function getId()
{
return 'TDIGEST.REVRANK';
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;








class TDIGESTRANK extends RedisCommand
{
public function getId()
{
return 'TDIGEST.RANK';
}
}
<?php











namespace Predis\Command\Redis\TDigest;

use Predis\Command\Command as RedisCommand;






class TDIGESTRESET extends RedisCommand
{
public function getId()
{
return 'TDIGEST.RESET';
}
}
<?php











namespace Predis\Command\Redis;




class XREVRANGE extends XRANGE
{



public function getId()
{
return 'XREVRANGE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PFCOUNT extends RedisCommand
{



public function getId()
{
return 'PFCOUNT';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;







class EXPIRETIME extends RedisCommand
{
public function getId()
{
return 'EXPIRETIME';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZRANK extends RedisCommand
{



public function getId()
{
return 'ZRANK';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PING extends RedisCommand
{



public function getId()
{
return 'PING';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SAVE extends RedisCommand
{



public function getId()
{
return 'SAVE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GEODIST extends RedisCommand
{



public function getId()
{
return 'GEODIST';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GET extends RedisCommand
{



public function getId()
{
return 'GET';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PFMERGE extends RedisCommand
{



public function getId()
{
return 'PFMERGE';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GEOADD extends RedisCommand
{



public function getId()
{
return 'GEOADD';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
foreach (array_pop($arguments) as $item) {
$arguments = array_merge($arguments, $item);
}
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SDIFF extends RedisCommand
{



public function getId()
{
return 'SDIFF';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SELECT extends RedisCommand
{



public function getId()
{
return 'SELECT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class EXISTS extends RedisCommand
{



public function getId()
{
return 'EXISTS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class XRANGE extends RedisCommand
{



public function getId()
{
return 'XRANGE';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 4) {
$arguments[] = $arguments[3];
$arguments[3] = 'COUNT';
}

parent::setArguments($arguments);
}




public function parseResponse($data)
{
$result = [];
foreach ($data as $entry) {
$processed = [];
$count = count($entry[1]);

for ($i = 0; $i < $count; ++$i) {
$processed[$entry[1][$i]] = $entry[1][++$i];
}

$result[$entry[0]] = $processed;
}

return $result;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZLEXCOUNT extends RedisCommand
{



public function getId()
{
return 'ZLEXCOUNT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SMEMBERS extends RedisCommand
{



public function getId()
{
return 'SMEMBERS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class TTL extends RedisCommand
{



public function getId()
{
return 'TTL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;











class ZDIFFSTORE extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}

public static $keysArgumentPositionOffset = 1;

public function getId()
{
return 'ZDIFFSTORE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RPUSHX extends RedisCommand
{



public function getId()
{
return 'RPUSHX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LASTSAVE extends RedisCommand
{



public function getId()
{
return 'LASTSAVE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SETNX extends RedisCommand
{



public function getId()
{
return 'SETNX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BLPOP extends RedisCommand
{



public function getId()
{
return 'BLPOP';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
[$arguments, $timeout] = $arguments;
array_push($arguments, $timeout);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BitByte;






class BITCOUNT extends RedisCommand
{
use BitByte;




public function getId()
{
return 'BITCOUNT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class XTRIM extends RedisCommand
{



public function getId()
{
return 'XTRIM';
}




public function setArguments(array $arguments)
{
$args = [];
$options = $arguments[3] ?? [];

$args[] = $arguments[0];

if (is_array($arguments[1])) {
array_push($args, ...$arguments[1]);
} else {
$args[] = $arguments[1];
}

$args[] = $arguments[2];
if (isset($options['limit'])) {
$args[] = 'LIMIT';
$args[] = $options['limit'];
}

parent::setArguments($args);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SDIFFSTORE extends RedisCommand
{



public function getId()
{
return 'SDIFFSTORE';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$arguments = array_merge([$arguments[0]], $arguments[1]);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class MOVE extends RedisCommand
{



public function getId()
{
return 'MOVE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZREMRANGEBYSCORE extends RedisCommand
{



public function getId()
{
return 'ZREMRANGEBYSCORE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;







class EVAL_RO extends RedisCommand
{
use Keys;

protected static $keysArgumentPositionOffset = 1;

public function getId()
{
return 'EVAL_RO';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZRANGE extends RedisCommand
{



public function getId()
{
return 'ZRANGE';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 4) {
$lastType = gettype($arguments[3]);

if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') {

$arguments[3] = ['WITHSCORES' => 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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\With\WithScores;







class ZDIFF extends RedisCommand
{
use WithScores {
WithScores::setArguments as setWithScore;
}
use Keys {
Keys::setArguments as setKeys;
}

protected static $keysArgumentPositionOffset = 0;

public function getId()
{
return 'ZDIFF';
}

public function setArguments(array $arguments)
{
$this->setKeys($arguments);
$arguments = $this->getArguments();

$this->setWithScore($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SUBSTR extends RedisCommand
{



public function getId()
{
return 'SUBSTR';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PFADD extends RedisCommand
{



public function getId()
{
return 'PFADD';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PUBSUB extends RedisCommand
{



public function getId()
{
return 'PUBSUB';
}




public function parseResponse($data)
{
switch (strtolower($this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LINSERT extends RedisCommand
{



public function getId()
{
return 'LINSERT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HVALS extends RedisCommand
{



public function getId()
{
return 'HVALS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;






class FCALL extends RedisCommand
{
use Keys;

protected static $keysArgumentPositionOffset = 1;

public function getId()
{
return 'FCALL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HMSET extends RedisCommand
{



public function getId()
{
return 'HMSET';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$flattenedKVs = [$arguments[0]];
$args = $arguments[1];

foreach ($args as $k => $v) {
$flattenedKVs[] = $k;
$flattenedKVs[] = $v;
}

$arguments = $flattenedKVs;
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PUBLISH extends RedisCommand
{



public function getId()
{
return 'PUBLISH';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RENAME extends RedisCommand
{



public function getId()
{
return 'RENAME';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class XADD extends RedisCommand
{



public function getId()
{
return 'XADD';
}




public function setArguments(array $arguments)
{
$args = [];

$args[] = $arguments[0];
$options = $arguments[3] ?? [];

if (isset($options['nomkstream']) && $options['nomkstream']) {
$args[] = 'NOMKSTREAM';
}

if (isset($options['trim']) && is_array($options['trim'])) {
array_push($args, ...$options['trim']);

if (isset($options['limit'])) {
$args[] = 'LIMIT';
$args[] = $options['limit'];
}
}


$args[] = $arguments[2] ?? '*';
if (isset($arguments[1]) && is_array($arguments[1])) {
foreach ($arguments[1] as $key => $val) {
$args[] = $key;
$args[] = $val;
}
}

parent::setArguments($args);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HSET extends RedisCommand
{



public function getId()
{
return 'HSET';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class DUMP extends RedisCommand
{



public function getId()
{
return 'DUMP';
}
}
<?php











namespace Predis\Command\Redis;




class ZRANGEBYLEX extends ZRANGE
{



public function getId()
{
return 'ZRANGEBYLEX';
}




protected function prepareOptions($options)
{
$opts = array_change_key_case($options, CASE_UPPER);
$finalizedOpts = [];

if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
$limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);

$finalizedOpts[] = 'LIMIT';
$finalizedOpts[] = $limit['OFFSET'] ?? $limit[0];
$finalizedOpts[] = $limit['COUNT'] ?? $limit[1];
}

return $finalizedOpts;
}




protected function withScores()
{
return false;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PUNSUBSCRIBE extends RedisCommand
{



public function getId()
{
return 'PUNSUBSCRIBE';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class INCR extends RedisCommand
{



public function getId()
{
return 'INCR';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BITOP extends RedisCommand
{



public function getId()
{
return 'BITOP';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
[$operation, $destination] = $arguments;
$arguments = $arguments[2];
array_unshift($arguments, $operation, $destination);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BITFIELD extends RedisCommand
{



public function getId()
{
return 'BITFIELD';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SET extends RedisCommand
{



public function getId()
{
return 'SET';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GETBIT extends RedisCommand
{



public function getId()
{
return 'GETBIT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GEOHASH extends RedisCommand
{



public function getId()
{
return 'GEOHASH';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;









class GEORADIUS extends RedisCommand
{



public function getId()
{
return 'GEORADIUS';
}




public function setArguments(array $arguments)
{
if ($arguments && is_array(end($arguments))) {
$options = array_change_key_case(array_pop($arguments), CASE_UPPER);

if (isset($options['WITHCOORD']) && $options['WITHCOORD'] == true) {
$arguments[] = 'WITHCOORD';
}

if (isset($options['WITHDIST']) && $options['WITHDIST'] == true) {
$arguments[] = 'WITHDIST';
}

if (isset($options['WITHHASH']) && $options['WITHHASH'] == true) {
$arguments[] = 'WITHHASH';
}

if (isset($options['COUNT'])) {
$arguments[] = 'COUNT';
$arguments[] = $options['COUNT'];
}

if (isset($options['SORT'])) {
$arguments[] = strtoupper($options['SORT']);
}

if (isset($options['STORE'])) {
$arguments[] = 'STORE';
$arguments[] = $options['STORE'];
}

if (isset($options['STOREDIST'])) {
$arguments[] = 'STOREDIST';
$arguments[] = $options['STOREDIST'];
}
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZCOUNT extends RedisCommand
{



public function getId()
{
return 'ZCOUNT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Expire\ExpireOptions;







class EXPIREAT extends RedisCommand
{
use ExpireOptions;




public function getId()
{
return 'EXPIREAT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PERSIST extends RedisCommand
{



public function getId()
{
return 'PERSIST';
}
}
<?php











namespace Predis\Command\Redis;




class ZINTERSTORE extends ZUNIONSTORE
{



public function getId()
{
return 'ZINTERSTORE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;






class SMISMEMBER extends RedisCommand
{
public function getId()
{
return 'SMISMEMBER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HKEYS extends RedisCommand
{



public function getId()
{
return 'HKEYS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZADD extends RedisCommand
{



public function getId()
{
return 'ZADD';
}




public function setArguments(array $arguments)
{
if (is_array(end($arguments))) {
foreach (array_pop($arguments) as $member => $score) {
$arguments[] = $score;
$arguments[] = $member;
}
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class PTTL extends RedisCommand
{



public function getId()
{
return 'PTTL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HMGET extends RedisCommand
{



public function getId()
{
return 'HMGET';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;




class ZRANGEBYSCORE extends ZRANGE
{



public function getId()
{
return 'ZRANGEBYSCORE';
}




protected function prepareOptions($options)
{
$opts = array_change_key_case($options, CASE_UPPER);
$finalizedOpts = [];

if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
$limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);

$finalizedOpts[] = 'LIMIT';
$finalizedOpts[] = $limit['OFFSET'] ?? $limit[0];
$finalizedOpts[] = $limit['COUNT'] ?? $limit[1];
}

return array_merge($finalizedOpts, parent::prepareOptions($options));
}




protected function withScores()
{
$arguments = $this->getArguments();

for ($i = 3; $i < count($arguments); ++$i) {
switch (strtoupper($arguments[$i])) {
case 'WITHSCORES':
return true;

case 'LIMIT':
$i += 2;
break;
}
}

return false;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class INFO extends RedisCommand
{



public function getId()
{
return 'INFO';
}




public function parseResponse($data)
{
if (empty($data) || !$lines = preg_split('/\r?\n/', $data)) {
return [];
}

if (strpos($lines[0], '#') === 0) {
return $this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GETRANGE extends RedisCommand
{



public function getId()
{
return 'GETRANGE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class OBJECT_ extends RedisCommand
{



public function getId()
{
return 'OBJECT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HSTRLEN extends RedisCommand
{



public function getId()
{
return 'HSTRLEN';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SUNION extends RedisCommand
{



public function getId()
{
return 'SUNION';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\GeoBy;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\From\GeoFrom;
use Predis\Command\Traits\Sorting;
use Predis\Command\Traits\Storedist;






class GEOSEARCHSTORE extends RedisCommand
{
use GeoFrom {
GeoFrom::setArguments as setFrom;
}
use GeoBy {
GeoBy::setArguments as setBy;
}
use Sorting {
Sorting::setArguments as setSorting;
}
use Count {
Count::setArguments as setCount;
}
use Storedist {
Storedist::setArguments as setStoreDist;
}

protected static $sortArgumentPositionOffset = 4;
protected static $countArgumentPositionOffset = 5;
protected static $storeDistArgumentPositionOffset = 7;

public function getId()
{
return 'GEOSEARCHSTORE';
}

public function setArguments(array $arguments)
{
$this->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();
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class MULTI extends RedisCommand
{



public function getId()
{
return 'MULTI';
}
}
<?php











namespace Predis\Command\Redis;




class EVALSHA extends EVAL_
{



public function getId()
{
return 'EVALSHA';
}






public function getScriptHash()
{
return $this->getArgument(0);
}
}
<?php











namespace Predis\Command\Redis;




class MSETNX extends MSET
{



public function getId()
{
return 'MSETNX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZSCAN extends RedisCommand
{



public function getId()
{
return 'ZSCAN';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
$options = $this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SLAVEOF extends RedisCommand
{



public function getId()
{
return 'SLAVEOF';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 0 || $arguments[0] === 'NO ONE') {
$arguments = ['NO', 'ONE'];
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SISMEMBER extends RedisCommand
{



public function getId()
{
return 'SISMEMBER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;






class FCALL_RO extends RedisCommand
{
public function getId()
{
return 'FCALL_RO';
}

public function setArguments(array $arguments)
{
$processedArguments = array_merge([$arguments[0], count($arguments[1])], $arguments[1]);

if (count($arguments) > 2) {
for ($i = 2, $iMax = count($arguments); $i < $iMax; $i++) {
$processedArguments[] = $arguments[$i];
}
}

parent::setArguments($processedArguments);
}
}
<?php











namespace Predis\Command\Redis;







class EVALSHA_RO extends EVAL_RO
{
public function getId()
{
return 'EVALSHA_RO';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;






class LCS extends RedisCommand
{
public function getId()
{
return 'LCS';
}

public function setArguments(array $arguments)
{
if (isset($arguments[2]) && $arguments[2]) {
$arguments[2] = 'LEN';
}

if (isset($arguments[3]) && $arguments[3]) {
$arguments[3] = 'IDX';
}

if (isset($arguments[5]) && $arguments[5]) {
$arguments[5] = 'WITHMATCHLEN';
}

if (isset($arguments[4])) {
if ($arguments[4] !== 0) {
$argumentsBefore = array_slice($arguments, 0, 4);
$argumentsAfter = array_slice($arguments, 5);
$arguments = array_merge($argumentsBefore, ['MINMATCHLEN', $arguments[4]], $argumentsAfter);
} else {
$arguments[4] = false;
}
}

parent::setArguments($arguments);
$this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SETBIT extends RedisCommand
{



public function getId()
{
return 'SETBIT';
}
}
<?php











namespace Predis\Command\Redis\CountMinSketch;

use Predis\Command\Command as RedisCommand;






class CMSINFO extends RedisCommand
{
public function getId()
{
return 'CMS.INFO';
}

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;
}
}
<?php











namespace Predis\Command\Redis\CountMinSketch;

use Predis\Command\Command as RedisCommand;








class CMSMERGE extends RedisCommand
{
public function getId()
{
return 'CMS.MERGE';
}

public function setArguments(array $arguments)
{
$processedArguments = array_merge([$arguments[0], count($arguments[1])], $arguments[1]);

if (!empty($arguments[2])) {
$processedArguments[] = 'WEIGHTS';
$processedArguments = array_merge($processedArguments, $arguments[2]);
}

parent::setArguments($processedArguments);
}
}
<?php











namespace Predis\Command\Redis\CountMinSketch;

use Predis\Command\Command as RedisCommand;






class CMSINITBYPROB extends RedisCommand
{
public function getId()
{
return 'CMS.INITBYPROB';
}
}
<?php











namespace Predis\Command\Redis\CountMinSketch;

use Predis\Command\Command as RedisCommand;






class CMSINITBYDIM extends RedisCommand
{
public function getId()
{
return 'CMS.INITBYDIM';
}
}
<?php











namespace Predis\Command\Redis\CountMinSketch;

use Predis\Command\Command as RedisCommand;







class CMSINCRBY extends RedisCommand
{
public function getId()
{
return 'CMS.INCRBY';
}
}
<?php











namespace Predis\Command\Redis\CountMinSketch;

use Predis\Command\Command as RedisCommand;






class CMSQUERY extends RedisCommand
{
public function getId()
{
return 'CMS.QUERY';
}
}
<?php











namespace Predis\Command\Redis\AbstractCommand;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;

abstract class BZPOPBase extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}

protected static $keysArgumentPositionOffset = 0;

abstract public function getId(): string;

public function setArguments(array $arguments)
{
$this->setKeys($arguments, false);
}

public function parseResponse($data)
{
$key = array_shift($data);

if (null === $key) {
return [$key];
}

return array_combine([$key], [[$data[0] => $data[1]]]);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class UNSUBSCRIBE extends RedisCommand
{



public function getId()
{
return 'UNSUBSCRIBE';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HINCRBYFLOAT extends RedisCommand
{



public function getId()
{
return 'HINCRBYFLOAT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZINCRBY extends RedisCommand
{



public function getId()
{
return 'ZINCRBY';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SREM extends RedisCommand
{



public function getId()
{
return 'SREM';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RPUSH extends RedisCommand
{



public function getId()
{
return 'RPUSH';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;

class GETDEL extends RedisCommand
{
public function getId()
{
return 'GETDEL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HDEL extends RedisCommand
{



public function getId()
{
return 'HDEL';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\ByLexByScore;
use Predis\Command\Traits\Limit\Limit;
use Predis\Command\Traits\Rev;






class ZRANGESTORE extends RedisCommand
{
use ByLexByScore {
ByLexByScore::setArguments as setByLexByScoreArgument;
}
use Rev {
Rev::setArguments as setReversedArgument;
}
use Limit {
Limit::setArguments as setLimitArguments;
}

protected static $byLexByScoreArgumentPositionOffset = 4;
protected static $revArgumentPositionOffset = 5;
protected static $limitArgumentPositionOffset = 6;

public function getId()
{
return 'ZRANGESTORE';
}

public function setArguments(array $arguments)
{
$this->setByLexByScoreArgument($arguments);
$arguments = $this->getArguments();

$this->setReversedArgument($arguments);
$arguments = $this->getArguments();

$this->setLimitArguments($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class FLUSHALL extends RedisCommand
{



public function getId()
{
return 'FLUSHALL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SMOVE extends RedisCommand
{



public function getId()
{
return 'SMOVE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class APPEND extends RedisCommand
{



public function getId()
{
return 'APPEND';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class TOUCH extends RedisCommand
{



public function getId()
{
return 'TOUCH';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class EVAL_ extends RedisCommand
{



public function getId()
{
return 'EVAL';
}






public function getScriptHash()
{
return sha1($this->getArgument(0));
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class DISCARD extends RedisCommand
{



public function getId()
{
return 'DISCARD';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LLEN extends RedisCommand
{



public function getId()
{
return 'LLEN';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LREM extends RedisCommand
{



public function getId()
{
return 'LREM';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BGREWRITEAOF extends RedisCommand
{



public function getId()
{
return 'BGREWRITEAOF';
}




public function parseResponse($data)
{
return $data == 'Background append only file rewriting started';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\Limit\Limit;

class SINTERCARD extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Limit {
Limit::setArguments as setLimit;
}

protected static $keysArgumentPositionOffset = 0;
protected static $limitArgumentPositionOffset = 1;

public function getId()
{
return 'SINTERCARD';
}

public function setArguments(array $arguments)
{
$this->setLimit($arguments);
$arguments = $this->getArguments();

$this->setKeys($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HEXISTS extends RedisCommand
{



public function getId()
{
return 'HEXISTS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class TYPE extends RedisCommand
{



public function getId()
{
return 'TYPE';
}




public function parseResponse($data)
{
if (is_string($data)) {
return $data;
}


switch ($data) {
case 0: return 'none';
case 1: return 'string';
case 2: return 'set';
case 3: return 'list';
case 4: return 'zset';
case 5: return 'hash';
case 6: return 'stream';
default: return $data;
}
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;

class LMOVE extends RedisCommand
{
public function getId()
{
return 'LMOVE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SINTERSTORE extends RedisCommand
{



public function getId()
{
return 'SINTERSTORE';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$arguments = array_merge([$arguments[0]], $arguments[1]);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Strategy\StrategyResolverInterface;
use Predis\Command\Strategy\SubcommandStrategyResolver;







class FUNCTIONS extends RedisCommand
{



private $strategyResolver;

public function __construct()
{
$this->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();
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ECHO_ extends RedisCommand
{



public function getId()
{
return 'ECHO';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HSCAN extends RedisCommand
{



public function getId()
{
return 'HSCAN';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
$options = $this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SINTER extends RedisCommand
{



public function getId()
{
return 'SINTER';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SETEX extends RedisCommand
{



public function getId()
{
return 'SETEX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SORT extends RedisCommand
{



public function getId()
{
return 'SORT';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 1) {
parent::setArguments($arguments);

return;
}

$query = [$arguments[0]];
$sortParams = array_change_key_case($arguments[1], CASE_UPPER);

if (isset($sortParams['BY'])) {
$query[] = 'BY';
$query[] = $sortParams['BY'];
}

if (isset($sortParams['GET'])) {
$getargs = $sortParams['GET'];

if (is_array($getargs)) {
foreach ($getargs as $getarg) {
$query[] = 'GET';
$query[] = $getarg;
}
} else {
$query[] = 'GET';
$query[] = $getargs;
}
}

if (isset($sortParams['LIMIT'])
&& is_array($sortParams['LIMIT'])
&& count($sortParams['LIMIT']) == 2) {
$query[] = 'LIMIT';
$query[] = $sortParams['LIMIT'][0];
$query[] = $sortParams['LIMIT'][1];
}

if (isset($sortParams['SORT'])) {
$query[] = strtoupper($sortParams['SORT']);
}

if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) {
$query[] = 'ALPHA';
}

if (isset($sortParams['STORE'])) {
$query[] = 'STORE';
$query[] = $sortParams['STORE'];
}

parent::setArguments($query);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use UnexpectedValueException;

class GETEX extends RedisCommand
{



private static $modifierEnum = [
'ex' => '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);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BitByte;






class BITPOS extends RedisCommand
{
use BitByte;




public function getId()
{
return 'BITPOS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\GeoBy;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\From\GeoFrom;
use Predis\Command\Traits\Sorting;
use Predis\Command\Traits\With\WithCoord;
use Predis\Command\Traits\With\WithDist;
use Predis\Command\Traits\With\WithHash;










class GEOSEARCH extends RedisCommand
{
use GeoFrom {
GeoFrom::setArguments as setFrom;
}
use GeoBy {
GeoBy::setArguments as setBy;
}
use Sorting {
Sorting::setArguments as setSorting;
}
use Count {
Count::setArguments as setCount;
}
use WithCoord {
WithCoord::setArguments as setWithCoord;
}
use WithDist {
WithDist::setArguments as setWithDist;
}
use WithHash {
WithHash::setArguments as setWithHash;
}

protected static $sortArgumentPositionOffset = 3;
protected static $countArgumentPositionOffset = 4;
protected static $withCoordArgumentPositionOffset = 6;
protected static $withDistArgumentPositionOffset = 7;
protected static $withHashArgumentPositionOffset = 8;

public function getId()
{
return 'GEOSEARCH';
}

public function setArguments(array $arguments)
{
$this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class BRPOP extends RedisCommand
{



public function getId()
{
return 'BRPOP';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
[$arguments, $timeout] = $arguments;
array_push($arguments, $timeout);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZSCORE extends RedisCommand
{



public function getId()
{
return 'ZSCORE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;







class ACL extends RedisCommand
{
public function getId()
{
return 'ACL';
}




public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}

if ($data === array_values($data)) {
return $data;
}


$return = [];

array_walk($data, function ($value, $key) use (&$return) {
$return[] = $key;
$return[] = $value;
});

return $return;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LPUSH extends RedisCommand
{



public function getId()
{
return 'LPUSH';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SETRANGE extends RedisCommand
{



public function getId()
{
return 'SETRANGE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class MGET extends RedisCommand
{



public function getId()
{
return 'MGET';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZREMRANGEBYLEX extends RedisCommand
{



public function getId()
{
return 'ZREMRANGEBYLEX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\With\WithScores;











class ZRANDMEMBER extends RedisCommand
{
use WithScores;

public function getId()
{
return 'ZRANDMEMBER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Traits\With\WithScores;







class ZUNION extends ZUNIONSTORE
{
use WithScores;

protected static $keysArgumentPositionOffset = 0;
protected static $weightsArgumentPositionOffset = 1;
protected static $aggregateArgumentPositionOffset = 2;

public function getId()
{
return 'ZUNION';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZPOPMIN extends RedisCommand
{



public function getId()
{
return 'ZPOPMIN';
}




public function parseResponse($data)
{
$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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;







class WAITAOF extends RedisCommand
{
public function getId()
{
return 'WAITAOF';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class INCRBY extends RedisCommand
{



public function getId()
{
return 'INCRBY';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SUNIONSTORE extends RedisCommand
{



public function getId()
{
return 'SUNIONSTORE';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$arguments = array_merge([$arguments[0]], $arguments[1]);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class QUIT extends RedisCommand
{



public function getId()
{
return 'QUIT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SSCAN extends RedisCommand
{



public function getId()
{
return 'SSCAN';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
$options = $this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class UNWATCH extends RedisCommand
{



public function getId()
{
return 'UNWATCH';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;







class CLIENT extends RedisCommand
{



public function getId()
{
return 'CLIENT';
}




public function parseResponse($data)
{
$args = array_change_key_case($this->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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class RENAMENX extends RedisCommand
{



public function getId()
{
return 'RENAMENX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LINDEX extends RedisCommand
{



public function getId()
{
return 'LINDEX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\LeftRight;

class LMPOP extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use LeftRight {
LeftRight::setArguments as setLeftRight;
}
use Count {
Count::setArguments as setCount;
}

protected static $keysArgumentPositionOffset = 0;
protected static $leftRightArgumentPositionOffset = 1;
protected static $countArgumentPositionOffset = 2;

public function getId()
{
return 'LMPOP';
}

public function setArguments(array $arguments)
{
$this->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]];
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SCAN extends RedisCommand
{



public function getId()
{
return 'SCAN';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$options = $this->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;
}
}
<?php











namespace Predis\Command\Redis\Container;

use Predis\Response\Status;







class CLUSTER extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'CLUSTER';
}
}
<?php











namespace Predis\Command\Redis\Container\Search;

use Predis\Command\Argument\Search\CursorArguments;
use Predis\Command\Redis\Container\AbstractContainer;
use Predis\Response\Status;





class FTCURSOR extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'FTCURSOR';
}
}
<?php











namespace Predis\Command\Redis\Container\Search;

use Predis\Command\Redis\Container\AbstractContainer;
use Predis\Response\Status;






class FTCONFIG extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'FTCONFIG';
}
}
<?php











namespace Predis\Command\Redis\Container;

interface ContainerInterface
{








public function __call(string $subcommandID, array $arguments);






public function getContainerCommandId(): string;
}
<?php











namespace Predis\Command\Redis\Container;

use Predis\ClientConfiguration;
use Predis\ClientInterface;
use UnexpectedValueException;

class ContainerFactory
{
private const CONTAINER_NAMESPACE = "Predis\Command\Redis\Container";






private static $specialMappings = [
'FUNCTION' => 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;
}
}
<?php











namespace Predis\Command\Redis\Container;

use Predis\Response\Status;






class ACL extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'acl';
}
}
<?php











namespace Predis\Command\Redis\Container;

use Predis\ClientInterface;

abstract class AbstractContainer implements ContainerInterface
{



protected $client;

public function __construct(ClientInterface $client)
{
$this->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;
}
<?php











namespace Predis\Command\Redis\Container\Json;

use Predis\Command\Redis\Container\AbstractContainer;





class JSONDEBUG extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'JSONDEBUG';
}
}
<?php











namespace Predis\Command\Redis\Container;

use Predis\Response\Status;











class FunctionContainer extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'FUNCTIONS';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\ByArgument;
use Predis\Command\Traits\Get\Get;
use Predis\Command\Traits\Limit\LimitObject;
use Predis\Command\Traits\Sorting;








class SORT_RO extends RedisCommand
{
use ByArgument {
ByArgument::setArguments as setBy;
}
use LimitObject {
LimitObject::setArguments as setLimit;
}
use Get {
Get::setArguments as setGetArgument;
}
use Sorting {
Sorting::setArguments as setSorting;
}

protected static $byArgumentPositionOffset = 1;
protected static $getArgumentPositionOffset = 3;
protected static $sortArgumentPositionOffset = 4;

public function getId()
{
return 'SORT_RO';
}

public function setArguments(array $arguments)
{
$alpha = array_pop($arguments);

if (is_bool($alpha) && $alpha) {
$arguments[] = 'ALPHA';
} elseif (!is_bool($alpha)) {
$arguments[] = $alpha;
}

$this->setSorting($arguments);
$arguments = $this->getArguments();

$this->setGetArgument($arguments);
$arguments = $this->getArguments();

$this->setLimit($arguments);
$arguments = $this->getArguments();

$this->setBy($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZPOPMAX extends RedisCommand
{



public function getId()
{
return 'ZPOPMAX';
}




public function parseResponse($data)
{
$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;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;







class CONFIG extends RedisCommand
{



public function getId()
{
return 'CONFIG';
}




public function parseResponse($data)
{
if (is_array($data)) {
if ($data !== array_values($data)) {
return $data; 
}

$result = [];

for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}

return $result;
}

return $data;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\MinMaxModifier;







class ZMPOP extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Count {
Count::setArguments as setCount;
}
use MinMaxModifier;

protected static $keysArgumentPositionOffset = 0;
protected static $countArgumentPositionOffset = 2;
protected static $modifierArgumentPositionOffset = 1;

public function getId()
{
return 'ZMPOP';
}

public function setArguments(array $arguments)
{
$this->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]);
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONARRAPPEND extends RedisCommand
{
public function getId()
{
return 'JSON.ARRAPPEND';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Json\Indent;
use Predis\Command\Traits\Json\Newline;
use Predis\Command\Traits\Json\Space;






class JSONGET extends RedisCommand
{
use Indent {
Indent::setArguments as setIndent;
}
use Newline {
Newline::setArguments as setNewline;
}
use Space {
Space::setArguments as setSpace;
}

protected static $indentArgumentPositionOffset = 1;
protected static $newlineArgumentPositionOffset = 2;
protected static $spaceArgumentPositionOffset = 3;

public function getId()
{
return 'JSON.GET';
}

public function setArguments(array $arguments)
{
$this->setSpace($arguments);
$arguments = $this->getArguments();

$this->setNewline($arguments);
$arguments = $this->getArguments();

$this->setIndent($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Json\NxXxArgument;






class JSONSET extends RedisCommand
{
use NxXxArgument {
setArguments as setSubcommand;
}

protected static $nxXxArgumentPositionOffset = 3;

public function getId()
{
return 'JSON.SET';
}

public function setArguments(array $arguments)
{
$this->setSubcommand($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONTYPE extends RedisCommand
{
public function getId()
{
return 'JSON.TYPE';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONFORGET extends RedisCommand
{
public function getId()
{
return 'JSON.FORGET';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONARRINSERT extends RedisCommand
{
public function getId()
{
return 'JSON.ARRINSERT';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONSTRAPPEND extends RedisCommand
{
public function getId()
{
return 'JSON.STRAPPEND';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONARRLEN extends RedisCommand
{
public function getId()
{
return 'JSON.ARRLEN';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONDEL extends RedisCommand
{
public function getId()
{
return 'JSON.DEL';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;







class JSONMERGE extends RedisCommand
{
public function getId()
{
return 'JSON.MERGE';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONTOGGLE extends RedisCommand
{
public function getId()
{
return 'JSON.TOGGLE';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONOBJLEN extends RedisCommand
{
public function getId()
{
return 'JSON.OBJLEN';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONSTRLEN extends RedisCommand
{
public function getId()
{
return 'JSON.STRLEN';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONRESP extends RedisCommand
{
public function getId()
{
return 'JSON.RESP';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONARRTRIM extends RedisCommand
{
public function getId()
{
return 'JSON.ARRTRIM';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONMSET extends RedisCommand
{
public function getId()
{
return 'JSON.MSET';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONCLEAR extends RedisCommand
{
public function getId()
{
return 'JSON.CLEAR';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONARRINDEX extends RedisCommand
{
public function getId()
{
return 'JSON.ARRINDEX';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONDEBUG extends RedisCommand
{
public function getId()
{
return 'JSON.DEBUG';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;

class JSONMGET extends RedisCommand
{
public function getId()
{
return 'JSON.MGET';
}

public function setArguments(array $arguments)
{
$unpackedArguments = [];

foreach ($arguments[0] as $key) {
$unpackedArguments[] = $key;
}

$unpackedArguments[] = $arguments[1];

parent::setArguments($unpackedArguments);
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONARRPOP extends RedisCommand
{
public function getId()
{
return 'JSON.ARRPOP';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONOBJKEYS extends RedisCommand
{
public function getId()
{
return 'JSON.OBJKEYS';
}
}
<?php











namespace Predis\Command\Redis\Json;

use Predis\Command\Command as RedisCommand;






class JSONNUMINCRBY extends RedisCommand
{
public function getId()
{
return 'JSON.NUMINCRBY';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HGETALL extends RedisCommand
{



public function getId()
{
return 'HGETALL';
}




public function parseResponse($data)
{
if ($data !== array_values($data)) {
return $data; 
}

$result = [];

for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}

return $result;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HINCRBY extends RedisCommand
{



public function getId()
{
return 'HINCRBY';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;






class CFINFO extends RedisCommand
{
public function getId()
{
return 'CF.INFO';
}

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;
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BloomFilters\Capacity;
use Predis\Command\Traits\BloomFilters\Items;
use Predis\Command\Traits\BloomFilters\NoCreate;

class CFINSERT extends RedisCommand
{
use Capacity {
Capacity::setArguments as setCapacity;
}
use NoCreate {
NoCreate::setArguments as setNoCreate;
}
use Items {
Items::setArguments as setItems;
}

protected static $capacityArgumentPositionOffset = 1;
protected static $noCreateArgumentPositionOffset = 2;
protected static $itemsArgumentPositionOffset = 3;

public function getId()
{
return 'CF.INSERT';
}

public function setArguments(array $arguments)
{
$this->setNoCreate($arguments);
$arguments = $this->getArguments();

$this->setItems($arguments);
$arguments = $this->getArguments();

$this->setCapacity($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;







class CFCOUNT extends RedisCommand
{
public function getId()
{
return 'CF.COUNT';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;






class CFADD extends RedisCommand
{
public function getId()
{
return 'CF.ADD';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;






class CFADDNX extends RedisCommand
{
public function getId()
{
return 'CF.ADDNX';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;






class CFEXISTS extends RedisCommand
{
public function getId()
{
return 'CF.EXISTS';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BloomFilters\BucketSize;
use Predis\Command\Traits\BloomFilters\Expansion;
use Predis\Command\Traits\BloomFilters\MaxIterations;

class CFRESERVE extends RedisCommand
{
use BucketSize {
BucketSize::setArguments as setBucketSize;
}
use MaxIterations {
MaxIterations::setArguments as setMaxIterations;
}
use Expansion {
Expansion::setArguments as setExpansion;
}

protected static $bucketSizeArgumentPositionOffset = 2;
protected static $maxIterationsArgumentPositionOffset = 3;
protected static $expansionArgumentPositionOffset = 4;

public function getId()
{
return 'CF.RESERVE';
}

public function setArguments(array $arguments)
{
$this->setExpansion($arguments);
$arguments = $this->getArguments();

$this->setMaxIterations($arguments);
$arguments = $this->getArguments();

$this->setBucketSize($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;







class CFINSERTNX extends CFINSERT
{
public function getId()
{
return 'CF.INSERTNX';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;







class CFLOADCHUNK extends RedisCommand
{
public function getId()
{
return 'CF.LOADCHUNK';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;






class CFMEXISTS extends RedisCommand
{
public function getId()
{
return 'CF.MEXISTS';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;







class CFSCANDUMP extends RedisCommand
{
public function getId()
{
return 'CF.SCANDUMP';
}
}
<?php











namespace Predis\Command\Redis\CuckooFilter;

use Predis\Command\Command as RedisCommand;








class CFDEL extends RedisCommand
{
public function getId()
{
return 'CF.DEL';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class MONITOR extends RedisCommand
{



public function getId()
{
return 'MONITOR';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\Limit\Limit;







class ZINTERCARD extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Limit {
Limit::setArguments as setLimit;
}

protected static $keysArgumentPositionOffset = 0;
protected static $limitArgumentPositionOffset = 1;

public function getId()
{
return 'ZINTERCARD';
}

public function setArguments(array $arguments)
{
$this->setLimit($arguments);
$arguments = $this->getArguments();

$this->setKeys($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Expire\ExpireOptions;








class EXPIRE extends RedisCommand
{
use ExpireOptions;




public function getId()
{
return 'EXPIRE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class WATCH extends RedisCommand
{



public function getId()
{
return 'WATCH';
}




public function setArguments(array $arguments)
{
if (isset($arguments[0]) && is_array($arguments[0])) {
$arguments = $arguments[0];
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class ZREMRANGEBYRANK extends RedisCommand
{



public function getId()
{
return 'ZREMRANGEBYRANK';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class FLUSHDB extends RedisCommand
{



public function getId()
{
return 'FLUSHDB';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\DB;
use Predis\Command\Traits\Replace;






class COPY extends RedisCommand
{
use DB {
DB::setArguments as setDB;
}
use Replace {
Replace::setArguments as setReplace;
}

protected static $dbArgumentPositionOffset = 2;

public function getId()
{
return 'COPY';
}

public function setArguments(array $arguments)
{
$this->setDB($arguments);
$arguments = $this->getArguments();

$this->setReplace($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LSET extends RedisCommand
{



public function getId()
{
return 'LSET';
}
}
<?php











namespace Predis\Command\Redis\TopK;

use Predis\Command\Command as RedisCommand;







class TOPKQUERY extends RedisCommand
{
public function getId()
{
return 'TOPK.QUERY';
}
}
<?php











namespace Predis\Command\Redis\TopK;

use Predis\Command\Command as RedisCommand;









class TOPKADD extends RedisCommand
{
public function getId()
{
return 'TOPK.ADD';
}
}
<?php











namespace Predis\Command\Redis\TopK;

use Predis\Command\Command as RedisCommand;






class TOPKLIST extends RedisCommand
{
public function getId()
{
return 'TOPK.LIST';
}

public function setArguments(array $arguments)
{
if (!empty($arguments[1])) {
$arguments[1] = 'WITHCOUNT';
}

parent::setArguments($arguments);
$this->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';
}
}
<?php











namespace Predis\Command\Redis\TopK;

use Predis\Command\Command as RedisCommand;






class TOPKINFO extends RedisCommand
{
public function getId()
{
return 'TOPK.INFO';
}

public function parseResponse($data)
{
$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;
}
}
<?php











namespace Predis\Command\Redis\TopK;

use Predis\Command\Command as RedisCommand;






class TOPKRESERVE extends RedisCommand
{
public function getId()
{
return 'TOPK.RESERVE';
}

public function setArguments(array $arguments)
{
switch (count($arguments)) {
case 3:
$arguments[] = 7; 
$arguments[] = 0.9; 
break;
case 4:
$arguments[] = 0.9; 
break;
default:
parent::setArguments($arguments);

return;
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis\TopK;

use Predis\Command\Command as RedisCommand;








class TOPKINCRBY extends RedisCommand
{
public function getId()
{
return 'TOPK.INCRBY';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Traits\With\WithScores;







class ZINTER extends ZINTERSTORE
{
use WithScores;

protected static $keysArgumentPositionOffset = 0;
protected static $weightsArgumentPositionOffset = 1;
protected static $aggregateArgumentPositionOffset = 2;

public function getId()
{
return 'ZINTER';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SADD extends RedisCommand
{



public function getId()
{
return 'SADD';
}




public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class LPOP extends RedisCommand
{



public function getId()
{
return 'LPOP';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class TIME extends RedisCommand
{



public function getId()
{
return 'TIME';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\With\WithValues;









class HRANDFIELD extends RedisCommand
{
use WithValues;

public function getId()
{
return 'HRANDFIELD';
}




public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}


$return = [];

array_walk_recursive($data, function ($value) use (&$return) {
$return[] = $value;
});

return $return;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;









class ZMSCORE extends RedisCommand
{



public function getId()
{
return 'ZMSCORE';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class SLOWLOG extends RedisCommand
{



public function getId()
{
return 'SLOWLOG';
}




public function parseResponse($data)
{
if (is_array($data)) {
$log = [];

foreach ($data as $index => $entry) {
$log[$index] = [
'id' => $entry[0],
'timestamp' => $entry[1],
'duration' => $entry[2],
'command' => $entry[3],
];
}

return $log;
}

return $data;
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class STRLEN extends RedisCommand
{



public function getId()
{
return 'STRLEN';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class GEOPOS extends RedisCommand
{



public function getId()
{
return 'GEOPOS';
}




public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Redis\AbstractCommand\BZPOPBase;











class BZPOPMAX extends BZPOPBase
{
public function getId(): string
{
return 'BZPOPMAX';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class INCRBYFLOAT extends RedisCommand
{



public function getId()
{
return 'INCRBYFLOAT';
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Timeout;
use Predis\Command\Traits\To\ServerTo;

class FAILOVER extends RedisCommand
{
use ServerTo {
ServerTo::setArguments as setTo;
}
use Timeout {
Timeout::setArguments as setTimeout;
}

protected static $toArgumentPositionOffset = 0;
protected static $timeoutArgumentPositionOffset = 2;

public function getId()
{
return 'FAILOVER';
}

public function setArguments(array $arguments)
{
if (array_key_exists(1, $arguments) && false !== $arguments[1]) {
$arguments[1] = 'ABORT';
}

$this->setTimeout($arguments);
$arguments = $this->getArguments();

$this->setTo($arguments);
$this->filterArguments();
}
}
<?php











namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;




class HLEN extends RedisCommand
{



public function getId()
{
return 'HLEN';
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSCREATE extends RedisCommand
{
public function getId()
{
return 'TS.CREATE';
}

public function setArguments(array $arguments)
{
[$key] = $arguments;
$commandArguments = (!empty($arguments[1])) ? $arguments[1]->toArray() : [];

parent::setArguments(array_merge(
[$key],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;






class TSMREVRANGE extends TSMRANGE
{
public function getId()
{
return 'TS.MREVRANGE';
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSQUERYINDEX extends RedisCommand
{
public function getId()
{
return 'TS.QUERYINDEX';
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSDELETERULE extends RedisCommand
{
public function getId()
{
return 'TS.DELETERULE';
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSALTER extends RedisCommand
{
public function getId()
{
return 'TS.ALTER';
}

public function setArguments(array $arguments)
{
[$key] = $arguments;
$commandArguments = (!empty($arguments[1])) ? $arguments[1]->toArray() : [];

parent::setArguments(array_merge(
[$key],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;








class TSINCRBY extends RedisCommand
{
public function getId()
{
return 'TS.INCRBY';
}

public function setArguments(array $arguments)
{
[$key, $value] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];

parent::setArguments(array_merge(
[$key, $value],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;

class TSMGET extends RedisCommand
{
public function getId()
{
return 'TS.MGET';
}

public function setArguments(array $arguments)
{
$processedArguments = [];
$argumentsObject = array_shift($arguments);
$commandArguments = $argumentsObject->toArray();

array_push($processedArguments, 'FILTER', ...$arguments);

parent::setArguments(array_merge(
$commandArguments,
$processedArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSGET extends RedisCommand
{
public function getId()
{
return 'TS.GET';
}

public function setArguments(array $arguments)
{
[$key] = $arguments;
$commandArguments = (!empty($arguments[1])) ? $arguments[1]->toArray() : [];

parent::setArguments(array_merge(
[$key],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSINFO extends RedisCommand
{
public function getId()
{
return 'TS.INFO';
}

public function setArguments(array $arguments)
{
[$key] = $arguments;
$commandArguments = (!empty($arguments[1])) ? $arguments[1]->toArray() : [];

parent::setArguments(array_merge(
[$key],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSMADD extends RedisCommand
{
public function getId()
{
return 'TS.MADD';
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;








class TSDECRBY extends RedisCommand
{
public function getId()
{
return 'TS.DECRBY';
}

public function setArguments(array $arguments)
{
[$key, $value] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];

parent::setArguments(array_merge(
[$key, $value],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSMRANGE extends RedisCommand
{
public function getId()
{
return 'TS.MRANGE';
}

public function setArguments(array $arguments)
{
[$fromTimestamp, $toTimestamp] = $arguments;
$commandArguments = $arguments[2]->toArray();

parent::setArguments(array_merge(
[$fromTimestamp, $toTimestamp],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSADD extends RedisCommand
{
public function getId()
{
return 'TS.ADD';
}

public function setArguments(array $arguments)
{
[$key, $timestamp, $value] = $arguments;
$commandArguments = (!empty($arguments[3])) ? $arguments[3]->toArray() : [];

parent::setArguments(array_merge(
[$key, $timestamp, $value],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSRANGE extends RedisCommand
{
public function getId()
{
return 'TS.RANGE';
}

public function setArguments(array $arguments)
{
[$key, $fromTimestamp, $toTimestamp] = $arguments;
$commandArguments = (!empty($arguments[3])) ? $arguments[3]->toArray() : [];

parent::setArguments(array_merge(
[$key, $fromTimestamp, $toTimestamp],
$commandArguments
));
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSCREATERULE extends RedisCommand
{
public function getId()
{
return 'TS.CREATERULE';
}

public function setArguments(array $arguments)
{
[$sourceKey, $destKey, $aggregator, $bucketDuration] = $arguments;
$processedArguments = [$sourceKey, $destKey, 'AGGREGATION', $aggregator, $bucketDuration];

if (count($arguments) === 5) {
$processedArguments[] = $arguments[4];
}

parent::setArguments($processedArguments);
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;






class TSREVRANGE extends TSRANGE
{
public function getId()
{
return 'TS.REVRANGE';
}
}
<?php











namespace Predis\Command\Redis\TimeSeries;

use Predis\Command\Command as RedisCommand;






class TSDEL extends RedisCommand
{
public function getId()
{
return 'TS.DEL';
}
}
<?php











namespace Predis\Command;

use Predis\ClientConfiguration;
use Predis\Command\Redis\FUNCTIONS;










class RedisFactory extends Factory
{
private const COMMANDS_NAMESPACE = "Predis\Command\Redis";

public function __construct()
{
$this->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;
}
}
<?php











namespace Predis\Command;

use InvalidArgumentException;
use Predis\ClientException;
use Predis\Command\Processor\ProcessorInterface;








abstract class Factory implements FactoryInterface
{
protected $commands = [];
protected $processor;




public function supports(string ...$commandIDs): bool
{
foreach ($commandIDs as $commandID) {
if ($this->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;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class LoadStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
if (count($arguments) <= 2) {
return $arguments;
}

$processedArguments = [$arguments[0]];
$replace = array_pop($arguments);

if (is_bool($replace) && $replace) {
$processedArguments[] = 'REPLACE';
} elseif (!is_bool($replace)) {
$processedArguments[] = $replace;
}

$processedArguments[] = $arguments[1];

return $processedArguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class RestoreStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
$processedArguments = [$arguments[0], $arguments[1]];

if (array_key_exists(2, $arguments) && null !== $arguments[2]) {
$processedArguments[] = strtoupper($arguments[2]);
}

return $processedArguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class KillStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
return $arguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class StatsStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
return $arguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class FlushStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
$processedArguments = [$arguments[0]];

if (array_key_exists(1, $arguments) && null !== $arguments[1]) {
$processedArguments[] = strtoupper($arguments[1]);
}

return $processedArguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class ListStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
$processedArguments = [$arguments[0]];

if (array_key_exists(1, $arguments) && null !== $arguments[1]) {
array_push($processedArguments, 'LIBRARYNAME', $arguments[1]);
}

if (array_key_exists(2, $arguments) && true === $arguments[2]) {
$processedArguments[] = 'WITHCODE';
}

return $processedArguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class DumpStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
return $arguments;
}
}
<?php











namespace Predis\Command\Strategy\ContainerCommands\Functions;

use Predis\Command\Strategy\SubcommandStrategyInterface;

class DeleteStrategy implements SubcommandStrategyInterface
{



public function processArguments(array $arguments): array
{
return $arguments;
}
}
<?php











namespace Predis\Command\Strategy;

interface StrategyResolverInterface
{







public function resolve(string $commandId, string $subcommandId): SubcommandStrategyInterface;
}
<?php











namespace Predis\Command\Strategy;

use InvalidArgumentException;

class SubcommandStrategyResolver implements StrategyResolverInterface
{
private const CONTAINER_COMMANDS_NAMESPACE = 'Predis\Command\Strategy\ContainerCommands';




private $separator;

public function __construct(string $separator = null)
{
$this->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');
}
}
<?php











namespace Predis\Command\Strategy;

interface SubcommandStrategyInterface
{






public function processArguments(array $arguments): array;
}
<?php











namespace Predis\Command\Argument;




interface ArrayableArgument
{





public function toArray(): array;
}
<?php











namespace Predis\Command\Argument\Server;

class LimitOffsetCount implements LimitInterface
{
private const KEYWORD = 'LIMIT';




private $offset;




private $count;

public function __construct(int $offset, int $count)
{
$this->offset = $offset;
$this->count = $count;
}




public function toArray(): array
{
return [self::KEYWORD, $this->offset, $this->count];
}
}
<?php











namespace Predis\Command\Argument\Server;

use Predis\Command\Argument\ArrayableArgument;

interface LimitInterface extends ArrayableArgument
{
}
<?php











namespace Predis\Command\Argument\Server;

use Predis\Command\Argument\ArrayableArgument;

class To implements ArrayableArgument
{
private const KEYWORD = 'TO';
private const FORCE_KEYWORD = 'FORCE';




private $host;




private $port;




private $isForce;

public function __construct(string $host, int $port, bool $isForce = false)
{
$this->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;
}
}
<?php











namespace Predis\Command\Argument\Search;

class ExplainArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\Search;

use InvalidArgumentException;

class SpellcheckArguments extends CommonArguments
{



private $termsEnum = [
'include' => '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;
}
}
<?php











namespace Predis\Command\Argument\Search;

class SugGetArguments extends CommonArguments
{





public function fuzzy(): self
{
$this->arguments[] = 'FUZZY';

return $this;
}







public function max(int $num): self
{
array_push($this->arguments, 'MAX', $num);

return $this;
}
}
<?php











namespace Predis\Command\Argument\Search;

use InvalidArgumentException;

class SearchArguments extends CommonArguments
{



private $sortingEnum = [
'asc' => '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;
}
}
<?php











namespace Predis\Command\Argument\Search;

class SynUpdateArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\Search;

class AlterArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\Search;

use Predis\Command\Argument\ArrayableArgument;

class CommonArguments implements ArrayableArgument
{



protected $arguments = [];







public function language(string $defaultLanguage = 'english'): self
{
$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;
}
}
<?php











namespace Predis\Command\Argument\Search;

class SugAddArguments extends CommonArguments
{





public function incr(): self
{
$this->arguments[] = 'INCR';

return $this;
}
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

abstract class AbstractField implements FieldInterface
{
public const SORTABLE = true;
public const NOT_SORTABLE = false;
public const SORTABLE_UNF = 'UNF';




protected $fieldArguments = [];









protected function setCommonOptions(
string $fieldType,
string $identifier,
string $alias = '',
$sortable = self::NOT_SORTABLE,
bool $noIndex = false
): void {
$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;
}
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

class NumericField extends AbstractField
{






public function __construct(
string $identifier,
string $alias = '',
$sortable = self::NOT_SORTABLE,
bool $noIndex = false
) {
$this->setCommonOptions('NUMERIC', $identifier, $alias, $sortable, $noIndex);
}
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

class TextField extends AbstractField
{










public function __construct(
string $identifier,
string $alias = '',
$sortable = self::NOT_SORTABLE,
bool $noIndex = false,
bool $noStem = false,
string $phonetic = '',
int $weight = 1,
bool $withSuffixTrie = false
) {
$this->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';
}
}
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

class GeoField extends AbstractField
{






public function __construct(
string $identifier,
string $alias = '',
$sortable = self::NOT_SORTABLE,
bool $noIndex = false
) {
$this->setCommonOptions('GEO', $identifier, $alias, $sortable, $noIndex);
}
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

class TagField extends AbstractField
{








public function __construct(
string $identifier,
string $alias = '',
$sortable = self::NOT_SORTABLE,
bool $noIndex = false,
string $separator = ',',
bool $caseSensitive = false
) {
$this->setCommonOptions('TAG', $identifier, $alias, $sortable, $noIndex);

if ($separator !== ',') {
$this->fieldArguments[] = 'SEPARATOR';
$this->fieldArguments[] = $separator;
}

if ($caseSensitive) {
$this->fieldArguments[] = 'CASESENSITIVE';
}
}
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

use Predis\Command\Argument\ArrayableArgument;




interface FieldInterface extends ArrayableArgument
{
}
<?php











namespace Predis\Command\Argument\Search\SchemaFields;

class VectorField extends AbstractField
{



protected $fieldArguments = [];







public function __construct(
string $fieldName,
string $algorithm,
array $attributeNameValueDictionary,
string $alias = ''
) {
$this->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;
}
}
<?php











namespace Predis\Command\Argument\Search;

use InvalidArgumentException;

class CreateArguments extends CommonArguments
{



private $supportedDataTypesEnum = [
'hash' => '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;
}
}
<?php











namespace Predis\Command\Argument\Search;

use Predis\Command\Argument\ArrayableArgument;

class DropArguments implements ArrayableArgument
{



protected $arguments = [];






public function dd(): self
{
$this->arguments[] = 'DD';

return $this;
}




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











namespace Predis\Command\Argument\Search;

class AggregateArguments extends CommonArguments
{



private $sortingEnum = [
'asc' => '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;
}
}
<?php











namespace Predis\Command\Argument\Search;

use Predis\Command\Argument\ArrayableArgument;

class ProfileArguments implements ArrayableArgument
{



protected $arguments = [];






public function search(): self
{
$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;
}
}
<?php











namespace Predis\Command\Argument\Search;

use Predis\Command\Argument\ArrayableArgument;

class CursorArguments implements ArrayableArgument
{



protected $arguments = [];







public function count(int $readSize): self
{
array_push($this->arguments, 'COUNT', $readSize);

return $this;
}




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











namespace Predis\Command\Argument\Geospatial;

use UnexpectedValueException;

abstract class AbstractBy implements ByInterface
{



private static $unitEnum = ['m', 'km', 'ft', 'mi'];




protected $unit;




abstract public function toArray(): array;





protected function setUnit(string $unit): void
{
if (!in_array($unit, self::$unitEnum, true)) {
throw new UnexpectedValueException('Wrong value given for unit');
}

$this->unit = $unit;
}
}
<?php











namespace Predis\Command\Argument\Geospatial;

class FromLonLat implements FromInterface
{
private const KEYWORD = 'FROMLONLAT';




private $longitude;




private $latitude;

public function __construct(float $longitude, float $latitude)
{
$this->longitude = $longitude;
$this->latitude = $latitude;
}




public function toArray(): array
{
return [self::KEYWORD, $this->longitude, $this->latitude];
}
}
<?php











namespace Predis\Command\Argument\Geospatial;

class ByBox extends AbstractBy
{
private const KEYWORD = 'BYBOX';




private $width;




private $height;

public function __construct(int $width, int $height, string $unit)
{
$this->width = $width;
$this->height = $height;
$this->setUnit($unit);
}




public function toArray(): array
{
return [self::KEYWORD, $this->width, $this->height, $this->unit];
}
}
<?php











namespace Predis\Command\Argument\Geospatial;

class ByRadius extends AbstractBy
{
private const KEYWORD = 'BYRADIUS';




private $radius;

public function __construct(int $radius, string $unit)
{
$this->radius = $radius;
$this->setUnit($unit);
}




public function toArray(): array
{
return [self::KEYWORD, $this->radius, $this->unit];
}
}
<?php











namespace Predis\Command\Argument\Geospatial;

use Predis\Command\Argument\ArrayableArgument;

interface FromInterface extends ArrayableArgument
{
}
<?php











namespace Predis\Command\Argument\Geospatial;

use Predis\Command\Argument\ArrayableArgument;

interface ByInterface extends ArrayableArgument
{
}
<?php











namespace Predis\Command\Argument\Geospatial;

class FromMember implements FromInterface
{
private const KEYWORD = 'FROMMEMBER';




private $member;

public function __construct(string $member)
{
$this->member = $member;
}




public function toArray(): array
{
return [self::KEYWORD, $this->member];
}
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class DecrByArguments extends IncrByArguments
{
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class RangeArguments extends CommonArguments
{






public function filterByTs(int ...$ts): self
{
array_push($this->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;
}
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class AlterArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class IncrByArguments extends CommonArguments
{






public function timestamp($timeStamp): self
{
array_push($this->arguments, 'TIMESTAMP', $timeStamp);

return $this;
}






public function uncompressed(): self
{
$this->arguments[] = 'UNCOMPRESSED';

return $this;
}
}
<?php











namespace Predis\Command\Argument\TimeSeries;

use Predis\Command\Argument\ArrayableArgument;

class CommonArguments implements ArrayableArgument
{
public const POLICY_BLOCK = 'BLOCK';
public const POLICY_FIRST = 'FIRST';
public const POLICY_LAST = 'LAST';
public const POLICY_MIN = 'MIN';
public const POLICY_MAX = 'MAX';
public const POLICY_SUM = 'SUM';

public const ENCODING_UNCOMPRESSED = 'UNCOMPRESSED';
public const ENCODING_COMPRESSED = 'COMPRESSED';




protected $arguments = [];







public function retentionMsecs(int $retentionPeriod): self
{
array_push($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;
}
}
<?php











namespace Predis\Command\Argument\TimeSeries;

use Predis\Command\Argument\ArrayableArgument;

class InfoArguments implements ArrayableArgument
{



private $arguments = [];






public function debug(): self
{
$this->arguments[] = 'DEBUG';

return $this;
}




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











namespace Predis\Command\Argument\TimeSeries;

class MGetArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class CreateArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class AddArguments extends CommonArguments
{







public function onDuplicate(string $policy = self::POLICY_BLOCK): self
{
array_push($this->arguments, 'ON_DUPLICATE', $policy);

return $this;
}
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class GetArguments extends CommonArguments
{
}
<?php











namespace Predis\Command\Argument\TimeSeries;

class MRangeArguments extends RangeArguments
{






public function filter(string ...$filterExpressions): self
{
array_push($this->arguments, 'FILTER', ...$filterExpressions);

return $this;
}









public function groupBy(string $label, string $reducer): self
{
array_push($this->arguments, 'GROUPBY', $label, 'REDUCE', $reducer);

return $this;
}
}
<?php











namespace Predis\Command;








interface FactoryInterface
{







public function supports(string ...$commandIDs): bool;









public function create(string $commandID, array $arguments = []): CommandInterface;
}
<?php











namespace Predis\Command;












class RawFactory implements FactoryInterface
{



public function supports(string ...$commandIDs): bool
{
return true;
}




public function create(string $commandID, array $arguments = []): CommandInterface
{
return new RawCommand($commandID, $arguments);
}
}
<?php











namespace Predis\Command;




interface CommandInterface
{






public function getId();






public function setSlot($slot);






public function getSlot();






public function setArguments(array $arguments);






public function setRawArguments(array $arguments);






public function getArguments();








public function getArgument($index);








public function parseResponse($data);
}
<?php











namespace Predis\Command;







abstract class ScriptCommand extends Command
{



public function getId()
{
return 'EVALSHA';
}






abstract public function getScript();






public function getScriptHash()
{
return sha1($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());
}
}
<?php











namespace Predis\Command;




abstract class Command implements CommandInterface
{
private $slot;
private $arguments = [];




public function setArguments(array $arguments)
{
$this->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;
});
}
}
<?php











namespace Predis\Command;












final class RawCommand implements CommandInterface
{
private $slot;
private $commandID;
private $arguments;





public function __construct($commandID, array $arguments = [])
{
$this->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;
}
}
<?php











namespace Predis\Command\Traits;

trait BitByte
{
private static $argumentEnum = [
'bit' => '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);
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use Predis\Command\Command;
use UnexpectedValueException;




trait Error
{
private static $errorModifier = 'ERROR';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$errorArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use Predis\Command\Command;
use UnexpectedValueException;




trait BucketSize
{
private static $bucketSizeModifier = 'BUCKETSIZE';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$bucketSizeArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use UnexpectedValueException;

trait Expansion
{
private static $expansionModifier = 'EXPANSION';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$expansionArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use Predis\Command\Command;




trait Items
{
private static $itemsModifier = 'ITEMS';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$itemsArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use Predis\Command\Command;
use UnexpectedValueException;




trait Capacity
{
private static $capacityModifier = 'CAPACITY';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$capacityArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use Predis\Command\Command;
use UnexpectedValueException;




trait MaxIterations
{
private static $maxIterationsModifier = 'MAXITERATIONS';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$maxIterationsArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\BloomFilters;

use Predis\Command\Command;
use UnexpectedValueException;




trait NoCreate
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (
static::$noCreateArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits;

use UnexpectedValueException;

trait DB
{
private $dbModifier = 'DB';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$dbArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait Weights
{



private static $weightsModifier = 'WEIGHTS';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$weightsArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait LeftRight
{



private static $leftRightEnum = [
'left' => '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
));
}
}
<?php











namespace Predis\Command\Traits;

use UnexpectedValueException;

trait Sorting
{
private static $sortingEnum = [
'asc' => '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
));
}
}
<?php











namespace Predis\Command\Traits\To;

use Predis\Command\Argument\Server\To;

trait ServerTo
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$toArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\By;

use Predis\Command\Command;
use UnexpectedValueException;




trait ByLexByScore
{
private static $argumentsEnum = [
'bylex' => '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));
}
}
<?php











namespace Predis\Command\Traits\By;

use Predis\Command\Command;




trait ByArgument
{
private $byModifier = 'BY';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$byArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits\By;

use InvalidArgumentException;
use Predis\Command\Argument\Geospatial\ByInterface;

trait GeoBy
{
public function setArguments(array $arguments)
{
$argumentPositionOffset = $this->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;
}
}
<?php











namespace Predis\Command\Traits\With;

use Predis\Command\Command;






trait WithScores
{
public function setArguments(array $arguments)
{
$withScores = array_pop($arguments);

if (is_bool($withScores) && $withScores) {
$arguments[] = 'WITHSCORES';
} elseif (!is_bool($withScores)) {
$arguments[] = $withScores;
}

parent::setArguments($arguments);
}






private function isWithScoreModifier(): bool
{
$arguments = parent::getArguments();
$lastArgument = (!empty($arguments)) ? $arguments[count($arguments) - 1] : null;

return is_string($lastArgument) && strtoupper($lastArgument) === 'WITHSCORES';
}

public function parseResponse($data)
{
if ($this->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;
}
}
<?php











namespace Predis\Command\Traits\With;

use Predis\Command\Command;
use UnexpectedValueException;




trait WithCoord
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (
static::$withCoordArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits\With;

use UnexpectedValueException;

trait WithHash
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (
static::$withHashArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits\With;

use Predis\Command\Command;




trait WithValues
{
public function setArguments(array $arguments)
{
$withValues = array_pop($arguments);

if (is_bool($withValues) && $withValues) {
$arguments[] = 'WITHVALUES';
} elseif (!is_bool($withValues)) {
$arguments[] = $withValues;
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Traits\With;

use UnexpectedValueException;

trait WithDist
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (
static::$withDistArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait Rev
{
public function setArguments(array $arguments)
{
$argument = $arguments[static::$revArgumentPositionOffset];

if (false === $argument) {
parent::setArguments($arguments);

return;
}

if (true === $argument) {
$argument = 'REV';
} else {
throw new UnexpectedValueException('Wrong rev argument type');
}

$argumentsBefore = array_slice($arguments, 0, static::$revArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$revArgumentPositionOffset + 1);

parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
<?php











namespace Predis\Command\Traits\Limit;

use Predis\Command\Argument\Server\LimitInterface;

trait LimitObject
{
public function setArguments(array $arguments)
{
$argumentPositionOffset = $this->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;
}
}
<?php











namespace Predis\Command\Traits\Limit;

use Predis\Command\Command;
use UnexpectedValueException;




trait Limit
{
private static $limitModifier = 'LIMIT';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
$argumentsBefore = array_slice($arguments, 0, static::$limitArgumentPositionOffset);

if (
static::$limitArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait Storedist
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (
static::$storeDistArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits\From;

use InvalidArgumentException;
use Predis\Command\Argument\Geospatial\FromInterface;

trait GeoFrom
{
public function setArguments(array $arguments)
{
$argumentPositionOffset = $this->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;
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait MinMaxModifier
{



private $modifierEnum = [
'min' => '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]];
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait Keys
{
public function setArguments(array $arguments, bool $withNumkeys = true)
{
$argumentsLength = count($arguments);

if (
static::$keysArgumentPositionOffset > $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));
}
}
<?php











namespace Predis\Command\Traits\Get;

use UnexpectedValueException;

trait Get
{
private static $getModifier = 'GET';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$getArgumentPositionOffset >= $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));
}
}
<?php











namespace Predis\Command\Traits\Expire;

trait ExpireOptions
{
private static $argumentEnum = [
'nx' => '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);
}
}
<?php











namespace Predis\Command\Traits\Json;

use UnexpectedValueException;

trait Space
{
private static $spaceModifier = 'SPACE';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$spaceArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\Json;

use Predis\Command\Command;
use UnexpectedValueException;




trait NxXxArgument
{



private static $argumentEnum = [
'nx' => '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
));
}
}
<?php











namespace Predis\Command\Traits\Json;

use UnexpectedValueException;

trait Newline
{
private static $newlineModifier = 'NEWLINE';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$newlineArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits\Json;

use UnexpectedValueException;

trait Indent
{
private static $indentModifier = 'INDENT';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$indentArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait Aggregate
{



private static $aggregateValuesEnum = [
'min' => '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
));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;
use UnexpectedValueException;




trait Count
{
private $countModifier = 'COUNT';
private $anyModifier = 'ANY';

public function setArguments(array $arguments, bool $any = false)
{
$argumentsLength = count($arguments);

if (static::$countArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command\Traits;

use Predis\Command\Command;




trait Replace
{
public function setArguments(array $arguments)
{
$replace = array_pop($arguments);

if (is_bool($replace) && $replace) {
$arguments[] = 'REPLACE';
} elseif (!is_bool($replace)) {
$arguments[] = $replace;
}

parent::setArguments($arguments);
}
}
<?php











namespace Predis\Command\Traits;

use UnexpectedValueException;

trait Timeout
{
private static $timeoutModifier = 'TIMEOUT';

public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);

if (static::$timeoutArgumentPositionOffset >= $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
));
}
}
<?php











namespace Predis\Command;




interface PrefixableCommandInterface extends CommandInterface
{





public function prefixKeys($prefix);
}
<?php











namespace Predis\Replication;

use Predis\ClientException;




class MissingMasterException extends ClientException
{
}
<?php











namespace Predis\Replication;

use Predis\CommunicationException;





class RoleException extends CommunicationException
{
}
<?php











namespace Predis\Replication;

use Predis\Command\CommandInterface;
use Predis\NotSupportedException;




class ReplicationStrategy
{
protected $disallowed;
protected $readonly;
protected $readonlySHA1;
protected $loadBalancing = true;

public function __construct()
{
$this->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;
}
}
<?php











namespace Predis;

use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Search\AggregateArguments;
use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
use Predis\Command\Argument\Search\ExplainArguments;
use Predis\Command\Argument\Search\ProfileArguments;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Argument\Search\SearchArguments;
use Predis\Command\Argument\Search\SugAddArguments;
use Predis\Command\Argument\Search\SugGetArguments;
use Predis\Command\Argument\Search\SynUpdateArguments;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\Argument\TimeSeries\AddArguments;
use Predis\Command\Argument\TimeSeries\AlterArguments as TSAlterArguments;
use Predis\Command\Argument\TimeSeries\CreateArguments as TSCreateArguments;
use Predis\Command\Argument\TimeSeries\DecrByArguments;
use Predis\Command\Argument\TimeSeries\GetArguments;
use Predis\Command\Argument\TimeSeries\IncrByArguments;
use Predis\Command\Argument\TimeSeries\InfoArguments;
use Predis\Command\Argument\TimeSeries\MGetArguments;
use Predis\Command\Argument\TimeSeries\MRangeArguments;
use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\CommandInterface;
use Predis\Command\Redis\Container\ACL;
use Predis\Command\Redis\Container\CLUSTER;
use Predis\Command\Redis\Container\FunctionContainer;
use Predis\Command\Redis\Container\Json\JSONDEBUG;
use Predis\Command\Redis\Container\Search\FTCONFIG;
use Predis\Command\Redis\Container\Search\FTCURSOR;
















































































































































































































































































































interface ClientContextInterface
{







public function executeCommand(CommandInterface $command);









public function __call($method, $arguments);








public function execute($callable = null);
}
<?php











namespace Predis\Configuration;









class Options implements OptionsInterface
{

protected $handlers = [
'aggregate' => 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;
}
}
<?php











namespace Predis\Configuration;





interface OptionInterface
{








public function filter(OptionsInterface $options, $value);








public function getDefault(OptionsInterface $options);
}
<?php











namespace Predis\Configuration\Option;

use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;





class Exceptions implements OptionInterface
{



public function filter(OptionsInterface $options, $value)
{
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}




public function getDefault(OptionsInterface $options)
{
return true;
}
}
<?php











namespace Predis\Configuration\Option;

use InvalidArgumentException;
use Predis\Cluster\Hash;
use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;




class CRC16 implements OptionInterface
{








protected function getHashGeneratorByDescription(OptionsInterface $options, $description)
{
if ($description === 'predis') {
return new Hash\CRC16();
} elseif ($description === 'phpiredis') {
return new Hash\PhpiredisCRC16();
} else {
throw new InvalidArgumentException(
'String value for the crc16 option must be either `predis` or `phpiredis`'
);
}
}




public function filter(OptionsInterface $options, $value)
{
if (is_callable($value)) {
$value = call_user_func($value, $options);
}

if (is_string($value)) {
return $this->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();
}
}
<?php











namespace Predis\Configuration\Option;

use InvalidArgumentException;
use Predis\Cluster\RedisStrategy;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\Cluster\PredisCluster;
use Predis\Connection\Cluster\RedisCluster;






class Cluster extends Aggregate
{



public function filter(OptionsInterface $options, $value)
{
if (is_string($value)) {
$value = $this->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()
);
}
}
<?php











namespace Predis\Configuration\Option;

use InvalidArgumentException;
use Predis\Command\FactoryInterface;
use Predis\Command\RawFactory;
use Predis\Command\RedisFactory;
use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;




class Commands implements OptionInterface
{



public function filter(OptionsInterface $options, $value)
{
if (is_callable($value)) {
$value = call_user_func($value, $options);
}

if ($value instanceof FactoryInterface) {
return $value;
} elseif (is_array($value)) {
return $this->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;
}
}
<?php











namespace Predis\Configuration\Option;

use InvalidArgumentException;
use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\Factory;
use Predis\Connection\FactoryInterface;
use Predis\Connection\PhpiredisSocketConnection;
use Predis\Connection\PhpiredisStreamConnection;
use Predis\Connection\RelayConnection;








class Connections implements OptionInterface
{



public function filter(OptionsInterface $options, $value)
{
if (is_callable($value)) {
$value = call_user_func($value, $options);
}

if ($value instanceof FactoryInterface) {
return $value;
} elseif (is_array($value)) {
return $this->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;
}
}
<?php











namespace Predis\Configuration\Option;

use InvalidArgumentException;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\Replication\MasterSlaveReplication;
use Predis\Connection\Replication\SentinelReplication;





class Replication extends Aggregate
{



public function filter(OptionsInterface $options, $value)
{
if (is_string($value)) {
$value = $this->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()
);
}
}
<?php











namespace Predis\Configuration\Option;

use Predis\Command\Processor\KeyPrefixProcessor;
use Predis\Command\Processor\ProcessorInterface;
use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;





class Prefix implements OptionInterface
{



public function filter(OptionsInterface $options, $value)
{
if (is_callable($value)) {
$value = call_user_func($value, $options);
}

if ($value instanceof ProcessorInterface) {
return $value;
}

return new KeyPrefixProcessor((string) $value);
}




public function getDefault(OptionsInterface $options)
{

}
}
<?php











namespace Predis\Configuration\Option;

use InvalidArgumentException;
use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\NodeConnectionInterface;










class Aggregate implements OptionInterface
{



public function filter(OptionsInterface $options, $value)
{
if (!is_callable($value)) {
throw new InvalidArgumentException(sprintf(
'%s expects a callable object acting as an aggregate connection initializer',
static::class
));
}

return $this->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;
}
}
<?php











namespace Predis\Configuration;

use Predis\Command\Processor\ProcessorInterface;










interface OptionsInterface
{







public function getDefault($option);








public function defined($option);








public function __isset($option);








public function __get($option);
}
<?php











namespace Predis;

use ArrayIterator;
use InvalidArgumentException;
use IteratorAggregate;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Command\Redis\Container\ContainerFactory;
use Predis\Command\Redis\Container\ContainerInterface;
use Predis\Command\ScriptCommand;
use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Connection\ParametersInterface;
use Predis\Connection\RelayConnection;
use Predis\Monitor\Consumer as MonitorConsumer;
use Predis\Pipeline\Atomic;
use Predis\Pipeline\FireAndForget;
use Predis\Pipeline\Pipeline;
use Predis\Pipeline\RelayAtomic;
use Predis\Pipeline\RelayPipeline;
use Predis\PubSub\Consumer as PubSubConsumer;
use Predis\PubSub\RelayConsumer as RelayPubSubConsumer;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ResponseInterface;
use Predis\Response\ServerException;
use Predis\Transaction\MultiExec as MultiExecTransaction;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;










class Client implements ClientInterface, IteratorAggregate
{
public const VERSION = '2.2.2';


private $options;


private $connection;


private $commands;





public function __construct($parameters = null, $options = null)
{
$this->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);
}
}
<?php











namespace Predis\Pipeline;

use Predis\Connection\ConnectionInterface;
use Predis\Connection\RelayConnection;
use Predis\Response\Error;
use Predis\Response\ServerException;
use Relay\Exception as RelayException;
use SplQueue;

class RelayPipeline extends Pipeline
{








protected function executePipeline(ConnectionInterface $connection, SplQueue $commands)
{

$client = $connection->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);
}
}
}
<?php











namespace Predis\Pipeline;

use Predis\Connection\ConnectionInterface;
use SplQueue;




class FireAndForget extends Pipeline
{



protected function executePipeline(ConnectionInterface $connection, SplQueue $commands)
{
while (!$commands->isEmpty()) {
$connection->writeRequest($commands->dequeue());
}

$connection->disconnect();

return [];
}
}
<?php











namespace Predis\Pipeline;

use Predis\CommunicationException;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\NotSupportedException;
use SplQueue;





class ConnectionErrorProof extends Pipeline
{



protected function getConnection()
{
return $this->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;
}
}
<?php











namespace Predis\Pipeline;

use Exception;
use InvalidArgumentException;
use Predis\ClientContextInterface;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Command\CommandInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\Replication\ReplicationInterface;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ResponseInterface;
use Predis\Response\ServerException;
use SplQueue;







class Pipeline implements ClientContextInterface
{
protected $client;
private $pipeline;

private $responses = [];
private $running = false;




public function __construct(ClientInterface $client)
{
$this->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;
}
}
<?php











namespace Predis\Pipeline;

use Predis\Connection\ConnectionInterface;
use Predis\Response\Error;
use Predis\Response\ServerException;
use Relay\Exception as RelayException;
use SplQueue;

class RelayAtomic extends Atomic
{



protected function executePipeline(ConnectionInterface $connection, SplQueue $commands)
{

$client = $connection->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);
}
}
}
<?php











namespace Predis\Pipeline;

use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ResponseInterface;
use Predis\Response\ServerException;
use SplQueue;




class Atomic extends Pipeline
{



public function __construct(ClientInterface $client)
{
if (!$client->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;
}
}
<?php











namespace Predis\Cluster\Distributor;

use Predis\Cluster\Hash\HashGeneratorInterface;





interface DistributorInterface
{






public function add($node, $weight = null);






public function remove($node);









public function getSlot($hash);








public function getBySlot($slot);








public function getByHash($hash);








public function get($value);






public function getHashGenerator();
}
<?php











namespace Predis\Cluster\Distributor;







class KetamaRing extends HashRing
{
public const DEFAULT_REPLICAS = 160;




public function __construct($nodeHashCallback = null)
{
parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
}




protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
{
$nodeObject = $node['object'];
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));

for ($i = 0; $i < $replicas; ++$i) {
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));

foreach ($unpackedDigest as $key) {
$ring[$key] = $nodeObject;
}
}
}




public function hash($value)
{
$hash = unpack('V', md5($value, true));

return $hash[1];
}




protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
{


return $lower < $ringKeysCount ? $lower : 0;
}
}
<?php











namespace Predis\Cluster\Distributor;

use Predis\Cluster\Hash\HashGeneratorInterface;







class HashRing implements DistributorInterface, HashGeneratorInterface
{
public const DEFAULT_REPLICAS = 128;
public const DEFAULT_WEIGHT = 100;

private $ring;
private $ringKeys;
private $ringKeysCount;
private $replicas;
private $nodeHashCallback;
private $nodes = [];





public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
{
$this->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;
}
}
<?php











namespace Predis\Cluster\Distributor;

use Exception;




class EmptyRingException extends Exception
{
}
<?php











namespace Predis\Cluster;

use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use OutOfBoundsException;
use Predis\Connection\NodeConnectionInterface;
use ReturnTypeWillChange;
use Traversable;




class SlotMap implements ArrayAccess, IteratorAggregate, Countable
{
private $slots = [];








public static function isValid($slot)
{
return $slot >= 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);
}
}
<?php











namespace Predis\Cluster;

use Predis\Cluster\Hash\CRC16;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\NotSupportedException;





class RedisStrategy extends ClusterStrategy
{
protected $hashGenerator;




public function __construct(HashGeneratorInterface $hashGenerator = null)
{
parent::__construct();

$this->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");
}
}
<?php











namespace Predis\Cluster\Hash;




class CRC16 implements HashGeneratorInterface
{
private static $CCITT_16 = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
];




public function hash($value)
{

$crc = 0;
$CCITT_16 = self::$CCITT_16;

$value = (string) $value;
$strlen = strlen($value);

for ($i = 0; $i < $strlen; ++$i) {
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
}

return $crc;
}
}
<?php











namespace Predis\Cluster\Hash;





interface HashGeneratorInterface
{







public function hash($value);
}
<?php











namespace Predis\Cluster\Hash;

use Predis\NotSupportedException;






class PhpiredisCRC16 implements HashGeneratorInterface
{
public function __construct()
{
if (!function_exists('phpiredis_utils_crc16')) {

throw new NotSupportedException(
'This hash generator requires a compatible version of ext-phpiredis'
);

}
}




public function hash($value)
{
return phpiredis_utils_crc16($value);
}
}
<?php











namespace Predis\Cluster;

use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Cluster\Distributor\HashRing;




class PredisStrategy extends ClusterStrategy
{
protected $distributor;




public function __construct(DistributorInterface $distributor = null)
{
parent::__construct();

$this->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;
}
}
<?php











namespace Predis\Cluster;

use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptCommand;




abstract class ClusterStrategy implements StrategyInterface
{
protected $commands;

public function __construct()
{
$this->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;
}
}
<?php











namespace Predis\Cluster;

use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Command\CommandInterface;







interface StrategyInterface
{








public function getSlot(CommandInterface $command);









public function getSlotByKey($key);






public function getDistributor();
}
<?php








require __DIR__.'/src/Autoloader.php';

Predis\Autoloader::register();
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function is_subclass_of;
use function strcasecmp;

final class ObjectType extends Type
{



private $className;




private $allowsNull;

public function __construct(TypeName $className, bool $allowsNull)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class UnknownType extends Type
{
public function isAssignable(Type $other): bool
{
return true;
}

public function name(): string
{
return 'unknown type';
}

public function asString(): string
{
return '';
}

public function allowsNull(): bool
{
return true;
}




public function isUnknown(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class MixedType extends Type
{
public function isAssignable(Type $other): bool
{
return !$other instanceof VoidType;
}

public function asString(): string
{
return 'mixed';
}

public function name(): string
{
return 'mixed';
}

public function allowsNull(): bool
{
return true;
}




public function isMixed(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class GenericObjectType extends Type
{



private $allowsNull;

public function __construct(bool $nullable)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class StaticType extends Type
{



private $className;




private $allowsNull;

public function __construct(TypeName $className, bool $allowsNull)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function strtolower;

final class SimpleType extends Type
{



private $name;




private $allowsNull;




private $value;

public function __construct(string $name, bool $nullable, $value = null)
{
$this->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;
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class VoidType extends Type
{
public function isAssignable(Type $other): bool
{
return $other instanceof self;
}

public function name(): string
{
return 'void';
}

public function allowsNull(): bool
{
return false;
}




public function isVoid(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class FalseType extends Type
{
public function isAssignable(Type $other): bool
{
if ($other instanceof self) {
return true;
}

return $other instanceof SimpleType &&
$other->name() === 'bool' &&
$other->value() === false;
}

public function name(): string
{
return 'false';
}

public function allowsNull(): bool
{
return false;
}




public function isFalse(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function assert;
use function count;
use function implode;
use function in_array;
use function sort;

final class IntersectionType extends Type
{



private $types;




public function __construct(Type ...$types)
{
$this->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;
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class NullType extends Type
{
public function isAssignable(Type $other): bool
{
return !($other instanceof VoidType);
}

public function name(): string
{
return 'null';
}

public function asString(): string
{
return 'null';
}

public function allowsNull(): bool
{
return true;
}




public function isNull(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function assert;
use function class_exists;
use function is_iterable;
use ReflectionClass;
use ReflectionException;

final class IterableType extends Type
{



private $allowsNull;

public function __construct(bool $nullable)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class NeverType extends Type
{
public function isAssignable(Type $other): bool
{
return $other instanceof self;
}

public function name(): string
{
return 'never';
}

public function allowsNull(): bool
{
return false;
}




public function isNever(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class TrueType extends Type
{
public function isAssignable(Type $other): bool
{
if ($other instanceof self) {
return true;
}

return $other instanceof SimpleType &&
$other->name() === 'bool' &&
$other->value() === true;
}

public function name(): string
{
return 'true';
}

public function allowsNull(): bool
{
return false;
}




public function isTrue(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function assert;
use function class_exists;
use function count;
use function explode;
use function function_exists;
use function is_array;
use function is_object;
use function is_string;
use Closure;
use ReflectionClass;
use ReflectionException;
use ReflectionObject;

final class CallableType extends Type
{



private $allowsNull;

public function __construct(bool $nullable)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function count;
use function implode;
use function sort;

final class UnionType extends Type
{



private $types;




public function __construct(Type ...$types)
{
$this->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'
);
}
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use const PHP_VERSION;
use function get_class;
use function gettype;
use function strtolower;
use function version_compare;

abstract class Type
{
public static function fromValue($value, bool $allowsNull): self
{
if ($allowsNull === false) {
if ($value === true) {
return new TrueType;
}

if ($value === false) {
return new FalseType;
}
}

$typeName = gettype($value);

if ($typeName === 'object') {
return new ObjectType(TypeName::fromQualifiedName(get_class($value)), $allowsNull);
}

$type = self::fromName($typeName, $allowsNull);

if ($type instanceof SimpleType) {
$type = new SimpleType($typeName, $allowsNull, $value);
}

return $type;
}

public static function fromName(string $typeName, bool $allowsNull): self
{
if (version_compare(PHP_VERSION, '8.1.0-dev', '>=') && 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;
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function assert;
use ReflectionFunctionAbstract;
use ReflectionIntersectionType;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionType;
use ReflectionUnionType;

final class ReflectionMapper
{



public function fromParameterTypes(ReflectionFunctionAbstract $functionOrMethod): array
{
$parameters = [];

foreach ($functionOrMethod->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();
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use function array_pop;
use function explode;
use function implode;
use function substr;
use ReflectionClass;

final class TypeName
{



private $namespaceName;




private $simpleName;

public static function fromQualifiedName(string $fullClassName): self
{
if ($fullClassName[0] === '\\') {
$fullClassName = substr($fullClassName, 1);
}

$classNameParts = explode('\\', $fullClassName);

$simpleName = array_pop($classNameParts);
$namespaceName = implode('\\', $classNameParts);

return new self($namespaceName, $simpleName);
}

public static function fromReflection(ReflectionClass $type): self
{
return new self(
$type->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Type;

final class Parameter
{



private $name;




private $type;




public function __construct(string $name, Type $type)
{
$this->name = $name;
$this->type = $type;
}

public function name(): string
{
return $this->name;
}

public function type(): Type
{
return $this->type;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\GlobalState;

use function array_diff;
use function array_key_exists;
use function array_keys;
use function array_merge;
use function function_exists;
use function get_defined_functions;
use function in_array;
use function is_array;
use ReflectionClass;
use ReflectionProperty;




class Restorer
{







public function restoreFunctions(Snapshot $snapshot): void
{
if (!function_exists('uopz_delete')) {
throw new RuntimeException('The uopz_delete() function is required for this operation');
}

$functions = get_defined_functions();

foreach (array_diff($functions['user'], $snapshot->functions()) as $function) {
uopz_delete($function);
}
}




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]);
}
}
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\GlobalState;

use const PHP_EOL;
use function is_array;
use function is_scalar;
use function serialize;
use function sprintf;
use function var_export;




final class CodeExporter
{
public function constants(Snapshot $snapshot): string
{
$result = '';

foreach ($snapshot->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\GlobalState;

use function in_array;
use function strpos;
use ReflectionClass;

final class ExcludeList
{



private $globalVariables = [];




private $classes = [];




private $classNamePrefixes = [];




private $parentClasses = [];




private $interfaces = [];




private $staticAttributes = [];

public function addGlobalVariable(string $variableName): void
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\GlobalState;

use const PHP_VERSION_ID;
use function array_keys;
use function array_merge;
use function array_reverse;
use function func_get_args;
use function get_declared_classes;
use function get_declared_interfaces;
use function get_declared_traits;
use function get_defined_constants;
use function get_defined_functions;
use function get_included_files;
use function in_array;
use function ini_get_all;
use function is_array;
use function is_object;
use function is_resource;
use function is_scalar;
use function serialize;
use function unserialize;
use ReflectionClass;
use SebastianBergmann\ObjectReflector\ObjectReflector;
use SebastianBergmann\RecursionContext\Context;
use Throwable;




class Snapshot
{



private $excludeList;




private $globalVariables = [];




private $superGlobalArrays = [];




private $superGlobalVariables = [];




private $staticAttributes = [];




private $iniSettings = [];




private $includedFiles = [];




private $constants = [];




private $functions = [];




private $interfaces = [];




private $classes = [];




private $traits = [];




public function __construct(?ExcludeList $excludeList = null, bool $includeGlobalVariables = true, bool $includeStaticAttributes = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\GlobalState;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\GlobalState;

final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class TraitUnit extends CodeUnit
{



public function isTrait(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class ClassUnit extends CodeUnit
{



public function isClass(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use Iterator;

final class CodeUnitCollectionIterator implements Iterator
{



private $codeUnits;




private $position = 0;

public function __construct(CodeUnitCollection $collection)
{
$this->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++;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use function array_merge;
use function count;
use Countable;
use IteratorAggregate;

final class CodeUnitCollection implements Countable, IteratorAggregate
{



private $codeUnits = [];




public static function fromArray(array $items): self
{
$collection = new self;

foreach ($items as $item) {
$collection->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class FunctionUnit extends CodeUnit
{



public function isFunction(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class InterfaceUnit extends CodeUnit
{



public function isInterface(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class TraitMethodUnit extends CodeUnit
{



public function isTraitMethod(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use function range;
use function sprintf;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;




abstract class CodeUnit
{



private $name;




private $sourceFileName;





private $sourceLines;







public static function forClass(string $className): ClassUnit
{
self::ensureUserDefinedClass($className);

$reflector = self::reflectorForClass($className);

return new ClassUnit(
$className,
$reflector->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
);
}

}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class InterfaceMethodUnit extends CodeUnit
{



public function isInterfaceMethod(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;




final class ClassMethodUnit extends CodeUnit
{



public function isClassMethod(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use function array_keys;
use function array_merge;
use function array_unique;
use function array_values;
use function class_exists;
use function explode;
use function function_exists;
use function interface_exists;
use function ksort;
use function method_exists;
use function sort;
use function sprintf;
use function str_replace;
use function strpos;
use function trait_exists;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;

final class Mapper
{



public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits): array
{
$result = [];

foreach ($codeUnits as $codeUnit) {
$sourceFileName = $codeUnit->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 === '<public>') {
return $this->publicMethodsOfClass($firstPart);
}

if ($secondPart === '<!public>') {
return $this->protectedAndPrivateMethodsOfClass($firstPart);
}

if ($secondPart === '<protected>') {
return $this->protectedMethodsOfClass($firstPart);
}

if ($secondPart === '<!protected>') {
return $this->publicAndPrivateMethodsOfClass($firstPart);
}

if ($secondPart === '<private>') {
return $this->privateMethodsOfClass($firstPart);
}

if ($secondPart === '<!private>') {
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('<extended>', '', $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
);
}

}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use RuntimeException;

final class ReflectionException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use RuntimeException;

final class NoTraitException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnit;

use RuntimeException;

final class InvalidCodeUnitException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\RecursionContext;

use const PHP_INT_MAX;
use const PHP_INT_MIN;
use function array_key_exists;
use function array_pop;
use function array_slice;
use function count;
use function is_array;
use function is_object;
use function random_int;
use function spl_object_hash;
use SplObjectStorage;





final class Context
{



private $arrays;




private $objects;




public function __construct()
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\RecursionContext;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\RecursionContext;

final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeUnitReverseLookup;

use function array_merge;
use function assert;
use function get_declared_classes;
use function get_declared_traits;
use function get_defined_functions;
use function is_array;
use function range;
use ReflectionClass;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use ReflectionMethod;




class Wizard
{



private $lookupTable = [];




private $processedClasses = [];




private $processedFunctions = [];







public function lookup($filename, $lineNumber)
{
if (!isset($this->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;
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ObjectEnumerator;

use function array_merge;
use function func_get_args;
use function is_array;
use function is_object;
use SebastianBergmann\ObjectReflector\ObjectReflector;
use SebastianBergmann\RecursionContext\Context;





class Enumerator
{








public function enumerate($variable)
{
if (!is_array($variable) && !is_object($variable)) {
throw new InvalidArgumentException;
}

if (isset(func_get_args()[1])) {
if (!func_get_args()[1] instanceof Context) {
throw new InvalidArgumentException;
}

$processed = func_get_args()[1];
} else {
$processed = new Context;
}

$objects = [];

if ($processed->contains($variable)) {
return $objects;
}

$array = $variable;
$processed->add($variable);

if (is_array($variable)) {
foreach ($array as $element) {
if (!is_array($element) && !is_object($element)) {
continue;
}

$objects = array_merge(
$objects,
$this->enumerate($element, $processed)
);
}
} else {
$objects[] = $variable;

$reflector = new 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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ObjectEnumerator;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ObjectEnumerator;

class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ObjectReflector;

use function count;
use function explode;
use function get_class;
use function is_object;

class ObjectReflector
{





public function getAttributes($object): array
{
if (!is_object($object)) {
throw new InvalidArgumentException;
}

$attributes = [];
$className = get_class($object);

foreach ((array) $object as $name => $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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ObjectReflector;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ObjectReflector;

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









namespace SebastianBergmann;

final class Version
{



private $path;




private $release;




private $version;

public function __construct(string $release, string $path)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

use function count;
use Countable;
use IteratorAggregate;




final class ComplexityCollection implements Countable, IteratorAggregate
{



private $items = [];

public static function fromList(Complexity ...$items): self
{
return new self($items);
}




private function __construct(array $items)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

use Iterator;

final class ComplexityCollectionIterator implements Iterator
{



private $items;




private $position = 0;

public function __construct(ComplexityCollection $items)
{
$this->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++;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;




final class Complexity
{



private $name;




private $cyclomaticComplexity;

public function __construct(string $name, int $cyclomaticComplexity)
{
$this->name = $name;
$this->cyclomaticComplexity = $cyclomaticComplexity;
}

public function name(): string
{
return $this->name;
}

public function cyclomaticComplexity(): int
{
return $this->cyclomaticComplexity;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

use PhpParser\Error;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitor\ParentConnectingVisitor;
use PhpParser\ParserFactory;

final class Calculator
{



public function calculateForSourceFile(string $sourceFile): ComplexityCollection
{
return $this->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();
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

use function assert;
use function is_array;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;

final class ComplexityCalculatingVisitor extends NodeVisitorAbstract
{



private $result = [];




private $shortCircuitTraversal;

public function __construct(bool $shortCircuitTraversal)
{
$this->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();
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

use function get_class;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\BinaryOp\LogicalAnd;
use PhpParser\Node\Expr\BinaryOp\LogicalOr;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Stmt\Case_;
use PhpParser\Node\Stmt\Catch_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\While_;
use PhpParser\NodeVisitorAbstract;

final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract
{



private $cyclomaticComplexity = 1;

public function enterNode(Node $node): void
{

switch (get_class($node)) {
case BooleanAnd::class:
case BooleanOr::class:
case Case_::class:
case Catch_::class:
case ElseIf_::class:
case For_::class:
case Foreach_::class:
case If_::class:
case LogicalAnd::class:
case LogicalOr::class:
case Ternary::class:
case While_::class:
$this->cyclomaticComplexity++;
}
}

public function cyclomaticComplexity(): int
{
return $this->cyclomaticComplexity;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Complexity;

final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Exporter;

use function bin2hex;
use function count;
use function function_exists;
use function get_class;
use function get_resource_type;
use function gettype;
use function implode;
use function ini_get;
use function ini_set;
use function is_array;
use function is_float;
use function is_object;
use function is_resource;
use function is_string;
use function mb_strlen;
use function mb_substr;
use function preg_match;
use function spl_object_hash;
use function sprintf;
use function str_repeat;
use function str_replace;
use function strlen;
use function substr;
use function var_export;
use SebastianBergmann\RecursionContext\Context;
use SplObjectStorage;












class Exporter
{

















public function export($value, $indentation = 0)
{
return $this->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(
'<lf>',
"\n",
str_replace(
["\r\n", "\n\r", "\r", "\n"],
['\r\n<lf>', '\n\r<lf>', '\r<lf>', '\n<lf>'],
$value
)
) .
"'";
}

$whitespace = str_repeat(' ', 4 * $indentation);

if (!$processed) {
$processed = new Context;
}

if (is_array($value)) {
if (($key = $processed->contains($value)) !== false) {
return 'Array &' . $key;
}

$array = $value;
$key = $processed->add($value);
$values = '';

if (count($array) > 0) {
foreach ($array as $k => $v) {
$values .= sprintf(
'%s    %s => %s' . "\n",
$whitespace,
$this->recursiveExport($k, $indentation),
$this->recursiveExport($value[$k], $indentation + 1, $processed)
);
}

$values = "\n" . $values . $whitespace;
}

return sprintf('Array &%s (%s)', $key, $values);
}

if (is_object($value)) {
$class = get_class($value);

if ($hash = $processed->contains($value)) {
return sprintf('%s Object &%s', $class, $hash);
}

$hash = $processed->add($value);
$values = '';
$array = $this->toArray($value);

if (count($array) > 0) {
foreach ($array as $k => $v) {
$values .= sprintf(
'%s    %s => %s' . "\n",
$whitespace,
$this->recursiveExport($k, $indentation),
$this->recursiveExport($v, $indentation + 1, $processed)
);
}

$values = "\n" . $values . $whitespace;
}

return sprintf('%s Object &%s (%s)', $class, $hash, $values);
}

return var_export($value, true);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Environment;

use const PHP_BINARY;
use const PHP_BINDIR;
use const PHP_MAJOR_VERSION;
use const PHP_SAPI;
use const PHP_VERSION;
use function array_map;
use function array_merge;
use function defined;
use function escapeshellarg;
use function explode;
use function extension_loaded;
use function getenv;
use function ini_get;
use function is_readable;
use function parse_ini_file;
use function php_ini_loaded_file;
use function php_ini_scanned_files;
use function phpversion;
use function sprintf;
use function strpos;




final class Runtime
{



private static $binary;





public function canCollectCodeCoverage(): bool
{
return $this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Environment;

use const DIRECTORY_SEPARATOR;
use const PHP_OS;
use const PHP_OS_FAMILY;
use function defined;

final class OperatingSystem
{




public function getFamily(): string
{
if (defined('PHP_OS_FAMILY')) {
return PHP_OS_FAMILY;
}

if (DIRECTORY_SEPARATOR === '\\') {
return 'Windows';
}

switch (PHP_OS) {
case 'Darwin':
return 'Darwin';

case 'DragonFly':
case 'FreeBSD':
case 'NetBSD':
case 'OpenBSD':
return 'BSD';

case 'Linux':
return 'Linux';

case 'SunOS':
return 'Solaris';

default:
return 'Unknown';
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Environment;

use const DIRECTORY_SEPARATOR;
use const STDIN;
use const STDOUT;
use function defined;
use function fclose;
use function fstat;
use function function_exists;
use function getenv;
use function is_resource;
use function is_string;
use function posix_isatty;
use function preg_match;
use function proc_close;
use function proc_open;
use function sapi_windows_vt100_support;
use function shell_exec;
use function stream_get_contents;
use function stream_isatty;
use function trim;

final class Console
{



public const STDIN = 0;




public const STDOUT = 1;




public const STDERR = 2;







public function hasColorSupport(): bool
{
if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}

if ($this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

use function array_pop;
use function count;
use function max;
use function preg_match;
use function preg_split;




final class Parser
{



public function parse(string $string): array
{
$lines = preg_split('(\r\n|\r|\n)', $string);

if (!empty($lines) && $lines[count($lines) - 1] === '') {
array_pop($lines);
}

$lineCount = count($lines);
$diffs = [];
$diff = null;
$collected = [];

for ($i = 0; $i < $lineCount; ++$i) {
if (preg_match('#^---\h+"?(?P<file>[^\\v\\t"]+)#', $lines[$i], $fromMatch) &&
preg_match('#^\\+\\+\\+\\h+"?(?P<file>[^\\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<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\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<type>[+ -])?(?P<line>.*)/', $line, $match)) {
$type = Line::UNCHANGED;

if ($match['type'] === '+') {
$type = Line::ADDED;
} elseif ($match['type'] === '-') {
$type = Line::REMOVED;
}

$diffLines[] = new Line($type, $match['line']);

if (null !== $chunk) {
$chunk->setLines($diffLines);
}
}
}

$diff->setChunks($chunks);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

final class Chunk
{



private $start;




private $startRange;




private $end;




private $endRange;




private $lines;

public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = [])
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

final class Diff
{



private $from;




private $to;




private $chunks;




public function __construct(string $from, string $to, array $chunks = [])
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

use function array_reverse;
use function count;
use function max;
use SplFixedArray;

final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
{



public function calculate(array $from, array $to): array
{
$common = [];
$fromLength = count($from);
$toLength = count($to);
$width = $fromLength + 1;
$matrix = new SplFixedArray($width * ($toLength + 1));

for ($i = 0; $i <= $fromLength; ++$i) {
$matrix[$i] = 0;
}

for ($j = 0; $j <= $toLength; ++$j) {
$matrix[$j * $width] = 0;
}

for ($i = 1; $i <= $fromLength; ++$i) {
for ($j = 1; $j <= $toLength; ++$j) {
$o = ($j * $width) + $i;


$firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0;

if ($matrix[$o - 1] > $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);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

final class Line
{
public const ADDED = 1;

public const REMOVED = 2;

public const UNCHANGED = 3;




private $type;




private $content;

public function __construct(int $type = self::UNCHANGED, string $content = '')
{
$this->type = $type;
$this->content = $content;
}

public function getContent(): string
{
return $this->content;
}

public function getType(): int
{
return $this->type;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

use const PHP_INT_SIZE;
use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;
use function array_shift;
use function array_unshift;
use function array_values;
use function count;
use function current;
use function end;
use function get_class;
use function gettype;
use function is_array;
use function is_object;
use function is_string;
use function key;
use function min;
use function preg_split;
use function prev;
use function reset;
use function sprintf;
use function substr;
use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;

final class Differ
{
public const OLD = 0;

public const ADDED = 1;

public const REMOVED = 2;

public const DIFF_LINE_END_WARNING = 3;

public const NO_LINE_END_EOF_WARNING = 4;




private $outputBuilder;






public function __construct($outputBuilder = null)
{
if ($outputBuilder instanceof DiffOutputBuilderInterface) {
$this->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, <null> 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];
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

use function array_fill;
use function array_merge;
use function array_reverse;
use function array_slice;
use function count;
use function in_array;
use function max;

final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
{



public function calculate(array $from, array $to): array
{
$cFrom = count($from);
$cTo = count($to);

if ($cFrom === 0) {
return [];
}

if ($cFrom === 1) {
if (in_array($from[0], $to, true)) {
return [$from[0]];
}

return [];
}

$i = (int) ($cFrom / 2);
$fromStart = array_slice($from, 0, $i);
$fromEnd = array_slice($from, $i);
$llB = $this->length($fromStart, $to);
$llE = $this->length(array_reverse($fromEnd), array_reverse($to));
$jMax = 0;
$max = 0;

for ($j = 0; $j <= $cTo; $j++) {
$m = $llB[$j] + $llE[$cTo - $j];

if ($m >= $max) {
$max = $m;
$jMax = $j;
}
}

$toStart = array_slice($to, 0, $jMax);
$toEnd = array_slice($to, $jMax);

return array_merge(
$this->calculate($fromStart, $toStart),
$this->calculate($fromEnd, $toEnd)
);
}

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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

interface LongestCommonSubsequenceCalculator
{



public function calculate(array $from, array $to): array;
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

use function get_class;
use function gettype;
use function is_object;
use function sprintf;
use Exception;

final class ConfigurationException extends InvalidArgumentException
{
public function __construct(
string $option,
string $expected,
$value,
int $code = 0,
?Exception $previous = null
) {
parent::__construct(
sprintf(
'Option "%s" must be %s, got "%s".',
$option,
$expected,
is_object($value) ? get_class($value) : (null === $value ? '<null>' : gettype($value) . '#' . $value)
),
$code,
$previous
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff;

class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff\Output;





interface DiffOutputBuilderInterface
{
public function getDiff(array $diff): string;
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff\Output;

use function fclose;
use function fopen;
use function fwrite;
use function stream_get_contents;
use function substr;
use SebastianBergmann\Diff\Differ;





final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface
{



private $header;

public function __construct(string $header = "--- Original\n+++ New\n")
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff\Output;

use function array_merge;
use function array_splice;
use function count;
use function fclose;
use function fopen;
use function fwrite;
use function is_bool;
use function is_int;
use function is_string;
use function max;
use function min;
use function sprintf;
use function stream_get_contents;
use function substr;
use SebastianBergmann\Diff\ConfigurationException;
use SebastianBergmann\Diff\Differ;






final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface
{
private static $default = [
'collapseRanges' => 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 <null>', $options[$option]);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff\Output;

use function array_splice;
use function count;
use function fclose;
use function fopen;
use function fwrite;
use function max;
use function min;
use function stream_get_contents;
use function strlen;
use function substr;
use SebastianBergmann\Diff\Differ;




final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder
{



private $collapseRanges = true;




private $commonLineThreshold = 6;




private $contextLines = 3;




private $header;




private $addLineNumbers;

public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false)
{
$this->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]);
}
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Diff\Output;

use function count;

abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface
{




protected function getCommonChunks(array $diff, int $lineThreshold = 5): array
{
$diffSize = count($diff);
$capturing = false;
$chunkStart = 0;
$chunkSize = 0;
$commonChunks = [];

for ($i = 0; $i < $diffSize; ++$i) {
if ($diff[$i][1] === 0 ) {
if ($capturing === false) {
$capturing = true;
$chunkStart = $i;
$chunkSize = 0;
} else {
++$chunkSize;
}
} elseif ($capturing !== false) {
if ($chunkSize >= $lineThreshold) {
$commonChunks[$chunkStart] = $chunkStart + $chunkSize;
}

$capturing = false;
}
}

if ($capturing !== false && $chunkSize >= $lineThreshold) {
$commonChunks[$chunkStart] = $chunkStart + $chunkSize;
}

return $commonChunks;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\ResourceOperations;

final class ResourceOperations
{



public static function getFunctions(): array
{
return [
'Directory::close',
'Directory::read',
'Directory::rewind',
'DirectoryIterator::openFile',
'FilesystemIterator::openFile',
'Gmagick::readimagefile',
'HttpResponse::getRequestBodyStream',
'HttpResponse::getStream',
'HttpResponse::setStream',
'Imagick::pingImageFile',
'Imagick::readImageFile',
'Imagick::writeImageFile',
'Imagick::writeImagesFile',
'MongoGridFSCursor::__construct',
'MongoGridFSFile::getResource',
'MysqlndUhConnection::stmtInit',
'MysqlndUhConnection::storeResult',
'MysqlndUhConnection::useResult',
'PDF_activate_item',
'PDF_add_launchlink',
'PDF_add_locallink',
'PDF_add_nameddest',
'PDF_add_note',
'PDF_add_pdflink',
'PDF_add_table_cell',
'PDF_add_textflow',
'PDF_add_thumbnail',
'PDF_add_weblink',
'PDF_arc',
'PDF_arcn',
'PDF_attach_file',
'PDF_begin_document',
'PDF_begin_font',
'PDF_begin_glyph',
'PDF_begin_item',
'PDF_begin_layer',
'PDF_begin_page',
'PDF_begin_page_ext',
'PDF_begin_pattern',
'PDF_begin_template',
'PDF_begin_template_ext',
'PDF_circle',
'PDF_clip',
'PDF_close',
'PDF_close_image',
'PDF_close_pdi',
'PDF_close_pdi_page',
'PDF_closepath',
'PDF_closepath_fill_stroke',
'PDF_closepath_stroke',
'PDF_concat',
'PDF_continue_text',
'PDF_create_3dview',
'PDF_create_action',
'PDF_create_annotation',
'PDF_create_bookmark',
'PDF_create_field',
'PDF_create_fieldgroup',
'PDF_create_gstate',
'PDF_create_pvf',
'PDF_create_textflow',
'PDF_curveto',
'PDF_define_layer',
'PDF_delete',
'PDF_delete_pvf',
'PDF_delete_table',
'PDF_delete_textflow',
'PDF_encoding_set_char',
'PDF_end_document',
'PDF_end_font',
'PDF_end_glyph',
'PDF_end_item',
'PDF_end_layer',
'PDF_end_page',
'PDF_end_page_ext',
'PDF_end_pattern',
'PDF_end_template',
'PDF_endpath',
'PDF_fill',
'PDF_fill_imageblock',
'PDF_fill_pdfblock',
'PDF_fill_stroke',
'PDF_fill_textblock',
'PDF_findfont',
'PDF_fit_image',
'PDF_fit_pdi_page',
'PDF_fit_table',
'PDF_fit_textflow',
'PDF_fit_textline',
'PDF_get_apiname',
'PDF_get_buffer',
'PDF_get_errmsg',
'PDF_get_errnum',
'PDF_get_parameter',
'PDF_get_pdi_parameter',
'PDF_get_pdi_value',
'PDF_get_value',
'PDF_info_font',
'PDF_info_matchbox',
'PDF_info_table',
'PDF_info_textflow',
'PDF_info_textline',
'PDF_initgraphics',
'PDF_lineto',
'PDF_load_3ddata',
'PDF_load_font',
'PDF_load_iccprofile',
'PDF_load_image',
'PDF_makespotcolor',
'PDF_moveto',
'PDF_new',
'PDF_open_ccitt',
'PDF_open_file',
'PDF_open_image',
'PDF_open_image_file',
'PDF_open_memory_image',
'PDF_open_pdi',
'PDF_open_pdi_document',
'PDF_open_pdi_page',
'PDF_pcos_get_number',
'PDF_pcos_get_stream',
'PDF_pcos_get_string',
'PDF_place_image',
'PDF_place_pdi_page',
'PDF_process_pdi',
'PDF_rect',
'PDF_restore',
'PDF_resume_page',
'PDF_rotate',
'PDF_save',
'PDF_scale',
'PDF_set_border_color',
'PDF_set_border_dash',
'PDF_set_border_style',
'PDF_set_gstate',
'PDF_set_info',
'PDF_set_layer_dependency',
'PDF_set_parameter',
'PDF_set_text_pos',
'PDF_set_value',
'PDF_setcolor',
'PDF_setdash',
'PDF_setdashpattern',
'PDF_setflat',
'PDF_setfont',
'PDF_setgray',
'PDF_setgray_fill',
'PDF_setgray_stroke',
'PDF_setlinecap',
'PDF_setlinejoin',
'PDF_setlinewidth',
'PDF_setmatrix',
'PDF_setmiterlimit',
'PDF_setrgbcolor',
'PDF_setrgbcolor_fill',
'PDF_setrgbcolor_stroke',
'PDF_shading',
'PDF_shading_pattern',
'PDF_shfill',
'PDF_show',
'PDF_show_boxed',
'PDF_show_xy',
'PDF_skew',
'PDF_stringwidth',
'PDF_stroke',
'PDF_suspend_page',
'PDF_translate',
'PDF_utf16_to_utf8',
'PDF_utf32_to_utf16',
'PDF_utf8_to_utf16',
'PDO::pgsqlLOBOpen',
'RarEntry::getStream',
'SQLite3::openBlob',
'SWFMovie::saveToFile',
'SplFileInfo::openFile',
'SplFileObject::openFile',
'SplTempFileObject::openFile',
'V8Js::compileString',
'V8Js::executeScript',
'Vtiful\Kernel\Excel::setColumn',
'Vtiful\Kernel\Excel::setRow',
'Vtiful\Kernel\Format::align',
'Vtiful\Kernel\Format::bold',
'Vtiful\Kernel\Format::italic',
'Vtiful\Kernel\Format::underline',
'XMLWriter::openMemory',
'XMLWriter::openURI',
'ZipArchive::getStream',
'Zookeeper::setLogStream',
'apc_bin_dumpfile',
'apc_bin_loadfile',
'bbcode_add_element',
'bbcode_add_smiley',
'bbcode_create',
'bbcode_destroy',
'bbcode_parse',
'bbcode_set_arg_parser',
'bbcode_set_flags',
'bcompiler_read',
'bcompiler_write_class',
'bcompiler_write_constant',
'bcompiler_write_exe_footer',
'bcompiler_write_file',
'bcompiler_write_footer',
'bcompiler_write_function',
'bcompiler_write_functions_from_file',
'bcompiler_write_header',
'bcompiler_write_included_filename',
'bzclose',
'bzerrno',
'bzerror',
'bzerrstr',
'bzflush',
'bzopen',
'bzread',
'bzwrite',
'cairo_surface_write_to_png',
'closedir',
'copy',
'crack_closedict',
'crack_opendict',
'cubrid_bind',
'cubrid_close_prepare',
'cubrid_close_request',
'cubrid_col_get',
'cubrid_col_size',
'cubrid_column_names',
'cubrid_column_types',
'cubrid_commit',
'cubrid_connect',
'cubrid_connect_with_url',
'cubrid_current_oid',
'cubrid_db_parameter',
'cubrid_disconnect',
'cubrid_drop',
'cubrid_fetch',
'cubrid_free_result',
'cubrid_get',
'cubrid_get_autocommit',
'cubrid_get_charset',
'cubrid_get_class_name',
'cubrid_get_db_parameter',
'cubrid_get_query_timeout',
'cubrid_get_server_info',
'cubrid_insert_id',
'cubrid_is_instance',
'cubrid_lob2_bind',
'cubrid_lob2_close',
'cubrid_lob2_export',
'cubrid_lob2_import',
'cubrid_lob2_new',
'cubrid_lob2_read',
'cubrid_lob2_seek',
'cubrid_lob2_seek64',
'cubrid_lob2_size',
'cubrid_lob2_size64',
'cubrid_lob2_tell',
'cubrid_lob2_tell64',
'cubrid_lob2_write',
'cubrid_lob_export',
'cubrid_lob_get',
'cubrid_lob_send',
'cubrid_lob_size',
'cubrid_lock_read',
'cubrid_lock_write',
'cubrid_move_cursor',
'cubrid_next_result',
'cubrid_num_cols',
'cubrid_num_rows',
'cubrid_pconnect',
'cubrid_pconnect_with_url',
'cubrid_prepare',
'cubrid_put',
'cubrid_query',
'cubrid_rollback',
'cubrid_schema',
'cubrid_seq_add',
'cubrid_seq_drop',
'cubrid_seq_insert',
'cubrid_seq_put',
'cubrid_set_add',
'cubrid_set_autocommit',
'cubrid_set_db_parameter',
'cubrid_set_drop',
'cubrid_set_query_timeout',
'cubrid_unbuffered_query',
'curl_close',
'curl_copy_handle',
'curl_errno',
'curl_error',
'curl_escape',
'curl_exec',
'curl_getinfo',
'curl_multi_add_handle',
'curl_multi_close',
'curl_multi_errno',
'curl_multi_exec',
'curl_multi_getcontent',
'curl_multi_info_read',
'curl_multi_remove_handle',
'curl_multi_select',
'curl_multi_setopt',
'curl_pause',
'curl_reset',
'curl_setopt',
'curl_setopt_array',
'curl_share_close',
'curl_share_errno',
'curl_share_init',
'curl_share_setopt',
'curl_unescape',
'cyrus_authenticate',
'cyrus_bind',
'cyrus_close',
'cyrus_connect',
'cyrus_query',
'cyrus_unbind',
'db2_autocommit',
'db2_bind_param',
'db2_client_info',
'db2_close',
'db2_column_privileges',
'db2_columns',
'db2_commit',
'db2_conn_error',
'db2_conn_errormsg',
'db2_connect',
'db2_cursor_type',
'db2_exec',
'db2_execute',
'db2_fetch_array',
'db2_fetch_assoc',
'db2_fetch_both',
'db2_fetch_object',
'db2_fetch_row',
'db2_field_display_size',
'db2_field_name',
'db2_field_num',
'db2_field_precision',
'db2_field_scale',
'db2_field_type',
'db2_field_width',
'db2_foreign_keys',
'db2_free_result',
'db2_free_stmt',
'db2_get_option',
'db2_last_insert_id',
'db2_lob_read',
'db2_next_result',
'db2_num_fields',
'db2_num_rows',
'db2_pclose',
'db2_pconnect',
'db2_prepare',
'db2_primary_keys',
'db2_procedure_columns',
'db2_procedures',
'db2_result',
'db2_rollback',
'db2_server_info',
'db2_set_option',
'db2_special_columns',
'db2_statistics',
'db2_stmt_error',
'db2_stmt_errormsg',
'db2_table_privileges',
'db2_tables',
'dba_close',
'dba_delete',
'dba_exists',
'dba_fetch',
'dba_firstkey',
'dba_insert',
'dba_nextkey',
'dba_open',
'dba_optimize',
'dba_popen',
'dba_replace',
'dba_sync',
'dbplus_add',
'dbplus_aql',
'dbplus_close',
'dbplus_curr',
'dbplus_find',
'dbplus_first',
'dbplus_flush',
'dbplus_freelock',
'dbplus_freerlocks',
'dbplus_getlock',
'dbplus_getunique',
'dbplus_info',
'dbplus_last',
'dbplus_lockrel',
'dbplus_next',
'dbplus_open',
'dbplus_prev',
'dbplus_rchperm',
'dbplus_rcreate',
'dbplus_rcrtexact',
'dbplus_rcrtlike',
'dbplus_restorepos',
'dbplus_rkeys',
'dbplus_ropen',
'dbplus_rquery',
'dbplus_rrename',
'dbplus_rsecindex',
'dbplus_runlink',
'dbplus_rzap',
'dbplus_savepos',
'dbplus_setindex',
'dbplus_setindexbynumber',
'dbplus_sql',
'dbplus_tremove',
'dbplus_undo',
'dbplus_undoprepare',
'dbplus_unlockrel',
'dbplus_unselect',
'dbplus_update',
'dbplus_xlockrel',
'dbplus_xunlockrel',
'deflate_add',
'dio_close',
'dio_fcntl',
'dio_open',
'dio_read',
'dio_seek',
'dio_stat',
'dio_tcsetattr',
'dio_truncate',
'dio_write',
'dir',
'eio_busy',
'eio_cancel',
'eio_chmod',
'eio_chown',
'eio_close',
'eio_custom',
'eio_dup2',
'eio_fallocate',
'eio_fchmod',
'eio_fchown',
'eio_fdatasync',
'eio_fstat',
'eio_fstatvfs',
'eio_fsync',
'eio_ftruncate',
'eio_futime',
'eio_get_last_error',
'eio_grp',
'eio_grp_add',
'eio_grp_cancel',
'eio_grp_limit',
'eio_link',
'eio_lstat',
'eio_mkdir',
'eio_mknod',
'eio_nop',
'eio_open',
'eio_read',
'eio_readahead',
'eio_readdir',
'eio_readlink',
'eio_realpath',
'eio_rename',
'eio_rmdir',
'eio_seek',
'eio_sendfile',
'eio_stat',
'eio_statvfs',
'eio_symlink',
'eio_sync',
'eio_sync_file_range',
'eio_syncfs',
'eio_truncate',
'eio_unlink',
'eio_utime',
'eio_write',
'enchant_broker_describe',
'enchant_broker_dict_exists',
'enchant_broker_free',
'enchant_broker_free_dict',
'enchant_broker_get_dict_path',
'enchant_broker_get_error',
'enchant_broker_init',
'enchant_broker_list_dicts',
'enchant_broker_request_dict',
'enchant_broker_request_pwl_dict',
'enchant_broker_set_dict_path',
'enchant_broker_set_ordering',
'enchant_dict_add_to_personal',
'enchant_dict_add_to_session',
'enchant_dict_check',
'enchant_dict_describe',
'enchant_dict_get_error',
'enchant_dict_is_in_session',
'enchant_dict_quick_check',
'enchant_dict_store_replacement',
'enchant_dict_suggest',
'event_add',
'event_base_free',
'event_base_loop',
'event_base_loopbreak',
'event_base_loopexit',
'event_base_new',
'event_base_priority_init',
'event_base_reinit',
'event_base_set',
'event_buffer_base_set',
'event_buffer_disable',
'event_buffer_enable',
'event_buffer_fd_set',
'event_buffer_free',
'event_buffer_new',
'event_buffer_priority_set',
'event_buffer_read',
'event_buffer_set_callback',
'event_buffer_timeout_set',
'event_buffer_watermark_set',
'event_buffer_write',
'event_del',
'event_free',
'event_new',
'event_priority_set',
'event_set',
'event_timer_add',
'event_timer_del',
'event_timer_pending',
'event_timer_set',
'expect_expectl',
'expect_popen',
'fam_cancel_monitor',
'fam_close',
'fam_monitor_collection',
'fam_monitor_directory',
'fam_monitor_file',
'fam_next_event',
'fam_open',
'fam_pending',
'fam_resume_monitor',
'fam_suspend_monitor',
'fann_cascadetrain_on_data',
'fann_cascadetrain_on_file',
'fann_clear_scaling_params',
'fann_copy',
'fann_create_from_file',
'fann_create_shortcut_array',
'fann_create_standard',
'fann_create_standard_array',
'fann_create_train',
'fann_create_train_from_callback',
'fann_descale_input',
'fann_descale_output',
'fann_descale_train',
'fann_destroy',
'fann_destroy_train',
'fann_duplicate_train_data',
'fann_get_MSE',
'fann_get_activation_function',
'fann_get_activation_steepness',
'fann_get_bias_array',
'fann_get_bit_fail',
'fann_get_bit_fail_limit',
'fann_get_cascade_activation_functions',
'fann_get_cascade_activation_functions_count',
'fann_get_cascade_activation_steepnesses',
'fann_get_cascade_activation_steepnesses_count',
'fann_get_cascade_candidate_change_fraction',
'fann_get_cascade_candidate_limit',
'fann_get_cascade_candidate_stagnation_epochs',
'fann_get_cascade_max_cand_epochs',
'fann_get_cascade_max_out_epochs',
'fann_get_cascade_min_cand_epochs',
'fann_get_cascade_min_out_epochs',
'fann_get_cascade_num_candidate_groups',
'fann_get_cascade_num_candidates',
'fann_get_cascade_output_change_fraction',
'fann_get_cascade_output_stagnation_epochs',
'fann_get_cascade_weight_multiplier',
'fann_get_connection_array',
'fann_get_connection_rate',
'fann_get_errno',
'fann_get_errstr',
'fann_get_layer_array',
'fann_get_learning_momentum',
'fann_get_learning_rate',
'fann_get_network_type',
'fann_get_num_input',
'fann_get_num_layers',
'fann_get_num_output',
'fann_get_quickprop_decay',
'fann_get_quickprop_mu',
'fann_get_rprop_decrease_factor',
'fann_get_rprop_delta_max',
'fann_get_rprop_delta_min',
'fann_get_rprop_delta_zero',
'fann_get_rprop_increase_factor',
'fann_get_sarprop_step_error_shift',
'fann_get_sarprop_step_error_threshold_factor',
'fann_get_sarprop_temperature',
'fann_get_sarprop_weight_decay_shift',
'fann_get_total_connections',
'fann_get_total_neurons',
'fann_get_train_error_function',
'fann_get_train_stop_function',
'fann_get_training_algorithm',
'fann_init_weights',
'fann_length_train_data',
'fann_merge_train_data',
'fann_num_input_train_data',
'fann_num_output_train_data',
'fann_randomize_weights',
'fann_read_train_from_file',
'fann_reset_errno',
'fann_reset_errstr',
'fann_run',
'fann_save',
'fann_save_train',
'fann_scale_input',
'fann_scale_input_train_data',
'fann_scale_output',
'fann_scale_output_train_data',
'fann_scale_train',
'fann_scale_train_data',
'fann_set_activation_function',
'fann_set_activation_function_hidden',
'fann_set_activation_function_layer',
'fann_set_activation_function_output',
'fann_set_activation_steepness',
'fann_set_activation_steepness_hidden',
'fann_set_activation_steepness_layer',
'fann_set_activation_steepness_output',
'fann_set_bit_fail_limit',
'fann_set_callback',
'fann_set_cascade_activation_functions',
'fann_set_cascade_activation_steepnesses',
'fann_set_cascade_candidate_change_fraction',
'fann_set_cascade_candidate_limit',
'fann_set_cascade_candidate_stagnation_epochs',
'fann_set_cascade_max_cand_epochs',
'fann_set_cascade_max_out_epochs',
'fann_set_cascade_min_cand_epochs',
'fann_set_cascade_min_out_epochs',
'fann_set_cascade_num_candidate_groups',
'fann_set_cascade_output_change_fraction',
'fann_set_cascade_output_stagnation_epochs',
'fann_set_cascade_weight_multiplier',
'fann_set_error_log',
'fann_set_input_scaling_params',
'fann_set_learning_momentum',
'fann_set_learning_rate',
'fann_set_output_scaling_params',
'fann_set_quickprop_decay',
'fann_set_quickprop_mu',
'fann_set_rprop_decrease_factor',
'fann_set_rprop_delta_max',
'fann_set_rprop_delta_min',
'fann_set_rprop_delta_zero',
'fann_set_rprop_increase_factor',
'fann_set_sarprop_step_error_shift',
'fann_set_sarprop_step_error_threshold_factor',
'fann_set_sarprop_temperature',
'fann_set_sarprop_weight_decay_shift',
'fann_set_scaling_params',
'fann_set_train_error_function',
'fann_set_train_stop_function',
'fann_set_training_algorithm',
'fann_set_weight',
'fann_set_weight_array',
'fann_shuffle_train_data',
'fann_subset_train_data',
'fann_test',
'fann_test_data',
'fann_train',
'fann_train_epoch',
'fann_train_on_data',
'fann_train_on_file',
'fbsql_affected_rows',
'fbsql_autocommit',
'fbsql_blob_size',
'fbsql_change_user',
'fbsql_clob_size',
'fbsql_close',
'fbsql_commit',
'fbsql_connect',
'fbsql_create_blob',
'fbsql_create_clob',
'fbsql_create_db',
'fbsql_data_seek',
'fbsql_database',
'fbsql_database_password',
'fbsql_db_query',
'fbsql_db_status',
'fbsql_drop_db',
'fbsql_errno',
'fbsql_error',
'fbsql_fetch_array',
'fbsql_fetch_assoc',
'fbsql_fetch_field',
'fbsql_fetch_lengths',
'fbsql_fetch_object',
'fbsql_fetch_row',
'fbsql_field_flags',
'fbsql_field_len',
'fbsql_field_name',
'fbsql_field_seek',
'fbsql_field_table',
'fbsql_field_type',
'fbsql_free_result',
'fbsql_get_autostart_info',
'fbsql_hostname',
'fbsql_insert_id',
'fbsql_list_dbs',
'fbsql_list_fields',
'fbsql_list_tables',
'fbsql_next_result',
'fbsql_num_fields',
'fbsql_num_rows',
'fbsql_password',
'fbsql_pconnect',
'fbsql_query',
'fbsql_read_blob',
'fbsql_read_clob',
'fbsql_result',
'fbsql_rollback',
'fbsql_rows_fetched',
'fbsql_select_db',
'fbsql_set_characterset',
'fbsql_set_lob_mode',
'fbsql_set_password',
'fbsql_set_transaction',
'fbsql_start_db',
'fbsql_stop_db',
'fbsql_table_name',
'fbsql_username',
'fclose',
'fdf_add_doc_javascript',
'fdf_add_template',
'fdf_close',
'fdf_create',
'fdf_enum_values',
'fdf_get_ap',
'fdf_get_attachment',
'fdf_get_encoding',
'fdf_get_file',
'fdf_get_flags',
'fdf_get_opt',
'fdf_get_status',
'fdf_get_value',
'fdf_get_version',
'fdf_next_field_name',
'fdf_open',
'fdf_open_string',
'fdf_remove_item',
'fdf_save',
'fdf_save_string',
'fdf_set_ap',
'fdf_set_encoding',
'fdf_set_file',
'fdf_set_flags',
'fdf_set_javascript_action',
'fdf_set_on_import_javascript',
'fdf_set_opt',
'fdf_set_status',
'fdf_set_submit_form_action',
'fdf_set_target_frame',
'fdf_set_value',
'fdf_set_version',
'feof',
'fflush',
'ffmpeg_frame::__construct',
'ffmpeg_frame::toGDImage',
'fgetc',
'fgetcsv',
'fgets',
'fgetss',
'file',
'file_get_contents',
'file_put_contents',
'finfo::buffer',
'finfo::file',
'finfo_buffer',
'finfo_close',
'finfo_file',
'finfo_open',
'finfo_set_flags',
'flock',
'fopen',
'fpassthru',
'fprintf',
'fputcsv',
'fputs',
'fread',
'fscanf',
'fseek',
'fstat',
'ftell',
'ftp_alloc',
'ftp_append',
'ftp_cdup',
'ftp_chdir',
'ftp_chmod',
'ftp_close',
'ftp_delete',
'ftp_exec',
'ftp_fget',
'ftp_fput',
'ftp_get',
'ftp_get_option',
'ftp_login',
'ftp_mdtm',
'ftp_mkdir',
'ftp_mlsd',
'ftp_nb_continue',
'ftp_nb_fget',
'ftp_nb_fput',
'ftp_nb_get',
'ftp_nb_put',
'ftp_nlist',
'ftp_pasv',
'ftp_put',
'ftp_pwd',
'ftp_quit',
'ftp_raw',
'ftp_rawlist',
'ftp_rename',
'ftp_rmdir',
'ftp_set_option',
'ftp_site',
'ftp_size',
'ftp_systype',
'ftruncate',
'fwrite',
'get_resource_type',
'gmp_div',
'gnupg::init',
'gnupg_adddecryptkey',
'gnupg_addencryptkey',
'gnupg_addsignkey',
'gnupg_cleardecryptkeys',
'gnupg_clearencryptkeys',
'gnupg_clearsignkeys',
'gnupg_decrypt',
'gnupg_decryptverify',
'gnupg_encrypt',
'gnupg_encryptsign',
'gnupg_export',
'gnupg_geterror',
'gnupg_getprotocol',
'gnupg_import',
'gnupg_init',
'gnupg_keyinfo',
'gnupg_setarmor',
'gnupg_seterrormode',
'gnupg_setsignmode',
'gnupg_sign',
'gnupg_verify',
'gupnp_context_get_host_ip',
'gupnp_context_get_port',
'gupnp_context_get_subscription_timeout',
'gupnp_context_host_path',
'gupnp_context_new',
'gupnp_context_set_subscription_timeout',
'gupnp_context_timeout_add',
'gupnp_context_unhost_path',
'gupnp_control_point_browse_start',
'gupnp_control_point_browse_stop',
'gupnp_control_point_callback_set',
'gupnp_control_point_new',
'gupnp_device_action_callback_set',
'gupnp_device_info_get',
'gupnp_device_info_get_service',
'gupnp_root_device_get_available',
'gupnp_root_device_get_relative_location',
'gupnp_root_device_new',
'gupnp_root_device_set_available',
'gupnp_root_device_start',
'gupnp_root_device_stop',
'gupnp_service_action_get',
'gupnp_service_action_return',
'gupnp_service_action_return_error',
'gupnp_service_action_set',
'gupnp_service_freeze_notify',
'gupnp_service_info_get',
'gupnp_service_info_get_introspection',
'gupnp_service_introspection_get_state_variable',
'gupnp_service_notify',
'gupnp_service_proxy_action_get',
'gupnp_service_proxy_action_set',
'gupnp_service_proxy_add_notify',
'gupnp_service_proxy_callback_set',
'gupnp_service_proxy_get_subscribed',
'gupnp_service_proxy_remove_notify',
'gupnp_service_proxy_send_action',
'gupnp_service_proxy_set_subscribed',
'gupnp_service_thaw_notify',
'gzclose',
'gzeof',
'gzgetc',
'gzgets',
'gzgetss',
'gzpassthru',
'gzputs',
'gzread',
'gzrewind',
'gzseek',
'gztell',
'gzwrite',
'hash_update_stream',
'http\Env\Response::send',
'http_get_request_body_stream',
'ibase_add_user',
'ibase_affected_rows',
'ibase_backup',
'ibase_blob_add',
'ibase_blob_cancel',
'ibase_blob_close',
'ibase_blob_create',
'ibase_blob_get',
'ibase_blob_open',
'ibase_close',
'ibase_commit',
'ibase_commit_ret',
'ibase_connect',
'ibase_db_info',
'ibase_delete_user',
'ibase_drop_db',
'ibase_execute',
'ibase_fetch_assoc',
'ibase_fetch_object',
'ibase_fetch_row',
'ibase_field_info',
'ibase_free_event_handler',
'ibase_free_query',
'ibase_free_result',
'ibase_gen_id',
'ibase_maintain_db',
'ibase_modify_user',
'ibase_name_result',
'ibase_num_fields',
'ibase_num_params',
'ibase_param_info',
'ibase_pconnect',
'ibase_prepare',
'ibase_query',
'ibase_restore',
'ibase_rollback',
'ibase_rollback_ret',
'ibase_server_info',
'ibase_service_attach',
'ibase_service_detach',
'ibase_set_event_handler',
'ibase_trans',
'ifx_affected_rows',
'ifx_close',
'ifx_connect',
'ifx_do',
'ifx_error',
'ifx_fetch_row',
'ifx_fieldproperties',
'ifx_fieldtypes',
'ifx_free_result',
'ifx_getsqlca',
'ifx_htmltbl_result',
'ifx_num_fields',
'ifx_num_rows',
'ifx_pconnect',
'ifx_prepare',
'ifx_query',
'image2wbmp',
'imageaffine',
'imagealphablending',
'imageantialias',
'imagearc',
'imagebmp',
'imagechar',
'imagecharup',
'imagecolorallocate',
'imagecolorallocatealpha',
'imagecolorat',
'imagecolorclosest',
'imagecolorclosestalpha',
'imagecolorclosesthwb',
'imagecolordeallocate',
'imagecolorexact',
'imagecolorexactalpha',
'imagecolormatch',
'imagecolorresolve',
'imagecolorresolvealpha',
'imagecolorset',
'imagecolorsforindex',
'imagecolorstotal',
'imagecolortransparent',
'imageconvolution',
'imagecopy',
'imagecopymerge',
'imagecopymergegray',
'imagecopyresampled',
'imagecopyresized',
'imagecrop',
'imagecropauto',
'imagedashedline',
'imagedestroy',
'imageellipse',
'imagefill',
'imagefilledarc',
'imagefilledellipse',
'imagefilledpolygon',
'imagefilledrectangle',
'imagefilltoborder',
'imagefilter',
'imageflip',
'imagefttext',
'imagegammacorrect',
'imagegd',
'imagegd2',
'imagegetclip',
'imagegif',
'imagegrabscreen',
'imagegrabwindow',
'imageinterlace',
'imageistruecolor',
'imagejpeg',
'imagelayereffect',
'imageline',
'imageopenpolygon',
'imagepalettecopy',
'imagepalettetotruecolor',
'imagepng',
'imagepolygon',
'imagepsencodefont',
'imagepsextendfont',
'imagepsfreefont',
'imagepsloadfont',
'imagepsslantfont',
'imagepstext',
'imagerectangle',
'imageresolution',
'imagerotate',
'imagesavealpha',
'imagescale',
'imagesetbrush',
'imagesetclip',
'imagesetinterpolation',
'imagesetpixel',
'imagesetstyle',
'imagesetthickness',
'imagesettile',
'imagestring',
'imagestringup',
'imagesx',
'imagesy',
'imagetruecolortopalette',
'imagettftext',
'imagewbmp',
'imagewebp',
'imagexbm',
'imap_append',
'imap_body',
'imap_bodystruct',
'imap_check',
'imap_clearflag_full',
'imap_close',
'imap_create',
'imap_createmailbox',
'imap_delete',
'imap_deletemailbox',
'imap_expunge',
'imap_fetch_overview',
'imap_fetchbody',
'imap_fetchheader',
'imap_fetchmime',
'imap_fetchstructure',
'imap_fetchtext',
'imap_gc',
'imap_get_quota',
'imap_get_quotaroot',
'imap_getacl',
'imap_getmailboxes',
'imap_getsubscribed',
'imap_header',
'imap_headerinfo',
'imap_headers',
'imap_list',
'imap_listmailbox',
'imap_listscan',
'imap_listsubscribed',
'imap_lsub',
'imap_mail_copy',
'imap_mail_move',
'imap_mailboxmsginfo',
'imap_msgno',
'imap_num_msg',
'imap_num_recent',
'imap_ping',
'imap_rename',
'imap_renamemailbox',
'imap_reopen',
'imap_savebody',
'imap_scan',
'imap_scanmailbox',
'imap_search',
'imap_set_quota',
'imap_setacl',
'imap_setflag_full',
'imap_sort',
'imap_status',
'imap_subscribe',
'imap_thread',
'imap_uid',
'imap_undelete',
'imap_unsubscribe',
'inflate_add',
'inflate_get_read_len',
'inflate_get_status',
'ingres_autocommit',
'ingres_autocommit_state',
'ingres_charset',
'ingres_close',
'ingres_commit',
'ingres_connect',
'ingres_cursor',
'ingres_errno',
'ingres_error',
'ingres_errsqlstate',
'ingres_escape_string',
'ingres_execute',
'ingres_fetch_array',
'ingres_fetch_assoc',
'ingres_fetch_object',
'ingres_fetch_proc_return',
'ingres_fetch_row',
'ingres_field_length',
'ingres_field_name',
'ingres_field_nullable',
'ingres_field_precision',
'ingres_field_scale',
'ingres_field_type',
'ingres_free_result',
'ingres_next_error',
'ingres_num_fields',
'ingres_num_rows',
'ingres_pconnect',
'ingres_prepare',
'ingres_query',
'ingres_result_seek',
'ingres_rollback',
'ingres_set_environment',
'ingres_unbuffered_query',
'inotify_add_watch',
'inotify_init',
'inotify_queue_len',
'inotify_read',
'inotify_rm_watch',
'kadm5_chpass_principal',
'kadm5_create_principal',
'kadm5_delete_principal',
'kadm5_destroy',
'kadm5_flush',
'kadm5_get_policies',
'kadm5_get_principal',
'kadm5_get_principals',
'kadm5_init_with_password',
'kadm5_modify_principal',
'ldap_add',
'ldap_bind',
'ldap_close',
'ldap_compare',
'ldap_control_paged_result',
'ldap_control_paged_result_response',
'ldap_count_entries',
'ldap_delete',
'ldap_errno',
'ldap_error',
'ldap_exop',
'ldap_exop_passwd',
'ldap_exop_refresh',
'ldap_exop_whoami',
'ldap_first_attribute',
'ldap_first_entry',
'ldap_first_reference',
'ldap_free_result',
'ldap_get_attributes',
'ldap_get_dn',
'ldap_get_entries',
'ldap_get_option',
'ldap_get_values',
'ldap_get_values_len',
'ldap_mod_add',
'ldap_mod_del',
'ldap_mod_replace',
'ldap_modify',
'ldap_modify_batch',
'ldap_next_attribute',
'ldap_next_entry',
'ldap_next_reference',
'ldap_parse_exop',
'ldap_parse_reference',
'ldap_parse_result',
'ldap_rename',
'ldap_sasl_bind',
'ldap_set_option',
'ldap_set_rebind_proc',
'ldap_sort',
'ldap_start_tls',
'ldap_unbind',
'libxml_set_streams_context',
'm_checkstatus',
'm_completeauthorizations',
'm_connect',
'm_connectionerror',
'm_deletetrans',
'm_destroyconn',
'm_getcell',
'm_getcellbynum',
'm_getcommadelimited',
'm_getheader',
'm_initconn',
'm_iscommadelimited',
'm_maxconntimeout',
'm_monitor',
'm_numcolumns',
'm_numrows',
'm_parsecommadelimited',
'm_responsekeys',
'm_responseparam',
'm_returnstatus',
'm_setblocking',
'm_setdropfile',
'm_setip',
'm_setssl',
'm_setssl_cafile',
'm_setssl_files',
'm_settimeout',
'm_transactionssent',
'm_transinqueue',
'm_transkeyval',
'm_transnew',
'm_transsend',
'm_validateidentifier',
'm_verifyconnection',
'm_verifysslcert',
'mailparse_determine_best_xfer_encoding',
'mailparse_msg_create',
'mailparse_msg_extract_part',
'mailparse_msg_extract_part_file',
'mailparse_msg_extract_whole_part_file',
'mailparse_msg_free',
'mailparse_msg_get_part',
'mailparse_msg_get_part_data',
'mailparse_msg_get_structure',
'mailparse_msg_parse',
'mailparse_msg_parse_file',
'mailparse_stream_encode',
'mailparse_uudecode_all',
'maxdb::use_result',
'maxdb_affected_rows',
'maxdb_connect',
'maxdb_disable_rpl_parse',
'maxdb_dump_debug_info',
'maxdb_embedded_connect',
'maxdb_enable_reads_from_master',
'maxdb_enable_rpl_parse',
'maxdb_errno',
'maxdb_error',
'maxdb_fetch_lengths',
'maxdb_field_tell',
'maxdb_get_host_info',
'maxdb_get_proto_info',
'maxdb_get_server_info',
'maxdb_get_server_version',
'maxdb_info',
'maxdb_init',
'maxdb_insert_id',
'maxdb_master_query',
'maxdb_more_results',
'maxdb_next_result',
'maxdb_num_fields',
'maxdb_num_rows',
'maxdb_rpl_parse_enabled',
'maxdb_rpl_probe',
'maxdb_select_db',
'maxdb_sqlstate',
'maxdb_stmt::result_metadata',
'maxdb_stmt_affected_rows',
'maxdb_stmt_errno',
'maxdb_stmt_error',
'maxdb_stmt_num_rows',
'maxdb_stmt_param_count',
'maxdb_stmt_result_metadata',
'maxdb_stmt_sqlstate',
'maxdb_thread_id',
'maxdb_use_result',
'maxdb_warning_count',
'mcrypt_enc_get_algorithms_name',
'mcrypt_enc_get_block_size',
'mcrypt_enc_get_iv_size',
'mcrypt_enc_get_key_size',
'mcrypt_enc_get_modes_name',
'mcrypt_enc_get_supported_key_sizes',
'mcrypt_enc_is_block_algorithm',
'mcrypt_enc_is_block_algorithm_mode',
'mcrypt_enc_is_block_mode',
'mcrypt_enc_self_test',
'mcrypt_generic',
'mcrypt_generic_deinit',
'mcrypt_generic_end',
'mcrypt_generic_init',
'mcrypt_module_close',
'mcrypt_module_open',
'mdecrypt_generic',
'mkdir',
'mqseries_back',
'mqseries_begin',
'mqseries_close',
'mqseries_cmit',
'mqseries_conn',
'mqseries_connx',
'mqseries_disc',
'mqseries_get',
'mqseries_inq',
'mqseries_open',
'mqseries_put',
'mqseries_put1',
'mqseries_set',
'msg_get_queue',
'msg_receive',
'msg_remove_queue',
'msg_send',
'msg_set_queue',
'msg_stat_queue',
'msql_affected_rows',
'msql_close',
'msql_connect',
'msql_create_db',
'msql_data_seek',
'msql_db_query',
'msql_drop_db',
'msql_fetch_array',
'msql_fetch_field',
'msql_fetch_object',
'msql_fetch_row',
'msql_field_flags',
'msql_field_len',
'msql_field_name',
'msql_field_seek',
'msql_field_table',
'msql_field_type',
'msql_free_result',
'msql_list_dbs',
'msql_list_fields',
'msql_list_tables',
'msql_num_fields',
'msql_num_rows',
'msql_pconnect',
'msql_query',
'msql_result',
'msql_select_db',
'mssql_bind',
'mssql_close',
'mssql_connect',
'mssql_data_seek',
'mssql_execute',
'mssql_fetch_array',
'mssql_fetch_assoc',
'mssql_fetch_batch',
'mssql_fetch_field',
'mssql_fetch_object',
'mssql_fetch_row',
'mssql_field_length',
'mssql_field_name',
'mssql_field_seek',
'mssql_field_type',
'mssql_free_result',
'mssql_free_statement',
'mssql_init',
'mssql_next_result',
'mssql_num_fields',
'mssql_num_rows',
'mssql_pconnect',
'mssql_query',
'mssql_result',
'mssql_rows_affected',
'mssql_select_db',
'mysql_affected_rows',
'mysql_client_encoding',
'mysql_close',
'mysql_connect',
'mysql_create_db',
'mysql_data_seek',
'mysql_db_name',
'mysql_db_query',
'mysql_drop_db',
'mysql_errno',
'mysql_error',
'mysql_fetch_array',
'mysql_fetch_assoc',
'mysql_fetch_field',
'mysql_fetch_lengths',
'mysql_fetch_object',
'mysql_fetch_row',
'mysql_field_flags',
'mysql_field_len',
'mysql_field_name',
'mysql_field_seek',
'mysql_field_table',
'mysql_field_type',
'mysql_free_result',
'mysql_get_host_info',
'mysql_get_proto_info',
'mysql_get_server_info',
'mysql_info',
'mysql_insert_id',
'mysql_list_dbs',
'mysql_list_fields',
'mysql_list_processes',
'mysql_list_tables',
'mysql_num_fields',
'mysql_num_rows',
'mysql_pconnect',
'mysql_ping',
'mysql_query',
'mysql_real_escape_string',
'mysql_result',
'mysql_select_db',
'mysql_set_charset',
'mysql_stat',
'mysql_tablename',
'mysql_thread_id',
'mysql_unbuffered_query',
'mysqlnd_uh_convert_to_mysqlnd',
'ncurses_bottom_panel',
'ncurses_del_panel',
'ncurses_delwin',
'ncurses_getmaxyx',
'ncurses_getyx',
'ncurses_hide_panel',
'ncurses_keypad',
'ncurses_meta',
'ncurses_move_panel',
'ncurses_mvwaddstr',
'ncurses_new_panel',
'ncurses_newpad',
'ncurses_newwin',
'ncurses_panel_above',
'ncurses_panel_below',
'ncurses_panel_window',
'ncurses_pnoutrefresh',
'ncurses_prefresh',
'ncurses_replace_panel',
'ncurses_show_panel',
'ncurses_top_panel',
'ncurses_waddch',
'ncurses_waddstr',
'ncurses_wattroff',
'ncurses_wattron',
'ncurses_wattrset',
'ncurses_wborder',
'ncurses_wclear',
'ncurses_wcolor_set',
'ncurses_werase',
'ncurses_wgetch',
'ncurses_whline',
'ncurses_wmouse_trafo',
'ncurses_wmove',
'ncurses_wnoutrefresh',
'ncurses_wrefresh',
'ncurses_wstandend',
'ncurses_wstandout',
'ncurses_wvline',
'newt_button',
'newt_button_bar',
'newt_checkbox',
'newt_checkbox_get_value',
'newt_checkbox_set_flags',
'newt_checkbox_set_value',
'newt_checkbox_tree',
'newt_checkbox_tree_add_item',
'newt_checkbox_tree_find_item',
'newt_checkbox_tree_get_current',
'newt_checkbox_tree_get_entry_value',
'newt_checkbox_tree_get_multi_selection',
'newt_checkbox_tree_get_selection',
'newt_checkbox_tree_multi',
'newt_checkbox_tree_set_current',
'newt_checkbox_tree_set_entry',
'newt_checkbox_tree_set_entry_value',
'newt_checkbox_tree_set_width',
'newt_compact_button',
'newt_component_add_callback',
'newt_component_takes_focus',
'newt_create_grid',
'newt_draw_form',
'newt_entry',
'newt_entry_get_value',
'newt_entry_set',
'newt_entry_set_filter',
'newt_entry_set_flags',
'newt_form',
'newt_form_add_component',
'newt_form_add_components',
'newt_form_add_hot_key',
'newt_form_destroy',
'newt_form_get_current',
'newt_form_run',
'newt_form_set_background',
'newt_form_set_height',
'newt_form_set_size',
'newt_form_set_timer',
'newt_form_set_width',
'newt_form_watch_fd',
'newt_grid_add_components_to_form',
'newt_grid_basic_window',
'newt_grid_free',
'newt_grid_get_size',
'newt_grid_h_close_stacked',
'newt_grid_h_stacked',
'newt_grid_place',
'newt_grid_set_field',
'newt_grid_simple_window',
'newt_grid_v_close_stacked',
'newt_grid_v_stacked',
'newt_grid_wrapped_window',
'newt_grid_wrapped_window_at',
'newt_label',
'newt_label_set_text',
'newt_listbox',
'newt_listbox_append_entry',
'newt_listbox_clear',
'newt_listbox_clear_selection',
'newt_listbox_delete_entry',
'newt_listbox_get_current',
'newt_listbox_get_selection',
'newt_listbox_insert_entry',
'newt_listbox_item_count',
'newt_listbox_select_item',
'newt_listbox_set_current',
'newt_listbox_set_current_by_key',
'newt_listbox_set_data',
'newt_listbox_set_entry',
'newt_listbox_set_width',
'newt_listitem',
'newt_listitem_get_data',
'newt_listitem_set',
'newt_radio_get_current',
'newt_radiobutton',
'newt_run_form',
'newt_scale',
'newt_scale_set',
'newt_scrollbar_set',
'newt_textbox',
'newt_textbox_get_num_lines',
'newt_textbox_reflowed',
'newt_textbox_set_height',
'newt_textbox_set_text',
'newt_vertical_scrollbar',
'oci_bind_array_by_name',
'oci_bind_by_name',
'oci_cancel',
'oci_close',
'oci_commit',
'oci_connect',
'oci_define_by_name',
'oci_error',
'oci_execute',
'oci_fetch',
'oci_fetch_all',
'oci_fetch_array',
'oci_fetch_assoc',
'oci_fetch_object',
'oci_fetch_row',
'oci_field_is_null',
'oci_field_name',
'oci_field_precision',
'oci_field_scale',
'oci_field_size',
'oci_field_type',
'oci_field_type_raw',
'oci_free_cursor',
'oci_free_statement',
'oci_get_implicit_resultset',
'oci_new_collection',
'oci_new_connect',
'oci_new_cursor',
'oci_new_descriptor',
'oci_num_fields',
'oci_num_rows',
'oci_parse',
'oci_pconnect',
'oci_register_taf_callback',
'oci_result',
'oci_rollback',
'oci_server_version',
'oci_set_action',
'oci_set_client_identifier',
'oci_set_client_info',
'oci_set_module_name',
'oci_set_prefetch',
'oci_statement_type',
'oci_unregister_taf_callback',
'odbc_autocommit',
'odbc_close',
'odbc_columnprivileges',
'odbc_columns',
'odbc_commit',
'odbc_connect',
'odbc_cursor',
'odbc_data_source',
'odbc_do',
'odbc_error',
'odbc_errormsg',
'odbc_exec',
'odbc_execute',
'odbc_fetch_array',
'odbc_fetch_into',
'odbc_fetch_row',
'odbc_field_len',
'odbc_field_name',
'odbc_field_num',
'odbc_field_precision',
'odbc_field_scale',
'odbc_field_type',
'odbc_foreignkeys',
'odbc_free_result',
'odbc_gettypeinfo',
'odbc_next_result',
'odbc_num_fields',
'odbc_num_rows',
'odbc_pconnect',
'odbc_prepare',
'odbc_primarykeys',
'odbc_procedurecolumns',
'odbc_procedures',
'odbc_result',
'odbc_result_all',
'odbc_rollback',
'odbc_setoption',
'odbc_specialcolumns',
'odbc_statistics',
'odbc_tableprivileges',
'odbc_tables',
'openal_buffer_create',
'openal_buffer_data',
'openal_buffer_destroy',
'openal_buffer_get',
'openal_buffer_loadwav',
'openal_context_create',
'openal_context_current',
'openal_context_destroy',
'openal_context_process',
'openal_context_suspend',
'openal_device_close',
'openal_device_open',
'openal_source_create',
'openal_source_destroy',
'openal_source_get',
'openal_source_pause',
'openal_source_play',
'openal_source_rewind',
'openal_source_set',
'openal_source_stop',
'openal_stream',
'opendir',
'openssl_csr_new',
'openssl_dh_compute_key',
'openssl_free_key',
'openssl_pkey_export',
'openssl_pkey_free',
'openssl_pkey_get_details',
'openssl_spki_new',
'openssl_x509_free',
'pclose',
'pfsockopen',
'pg_affected_rows',
'pg_cancel_query',
'pg_client_encoding',
'pg_close',
'pg_connect_poll',
'pg_connection_busy',
'pg_connection_reset',
'pg_connection_status',
'pg_consume_input',
'pg_convert',
'pg_copy_from',
'pg_copy_to',
'pg_dbname',
'pg_delete',
'pg_end_copy',
'pg_escape_bytea',
'pg_escape_identifier',
'pg_escape_literal',
'pg_escape_string',
'pg_execute',
'pg_fetch_all',
'pg_fetch_all_columns',
'pg_fetch_array',
'pg_fetch_assoc',
'pg_fetch_row',
'pg_field_name',
'pg_field_num',
'pg_field_size',
'pg_field_table',
'pg_field_type',
'pg_field_type_oid',
'pg_flush',
'pg_free_result',
'pg_get_notify',
'pg_get_pid',
'pg_get_result',
'pg_host',
'pg_insert',
'pg_last_error',
'pg_last_notice',
'pg_last_oid',
'pg_lo_close',
'pg_lo_create',
'pg_lo_export',
'pg_lo_import',
'pg_lo_open',
'pg_lo_read',
'pg_lo_read_all',
'pg_lo_seek',
'pg_lo_tell',
'pg_lo_truncate',
'pg_lo_unlink',
'pg_lo_write',
'pg_meta_data',
'pg_num_fields',
'pg_num_rows',
'pg_options',
'pg_parameter_status',
'pg_ping',
'pg_port',
'pg_prepare',
'pg_put_line',
'pg_query',
'pg_query_params',
'pg_result_error',
'pg_result_error_field',
'pg_result_seek',
'pg_result_status',
'pg_select',
'pg_send_execute',
'pg_send_prepare',
'pg_send_query',
'pg_send_query_params',
'pg_set_client_encoding',
'pg_set_error_verbosity',
'pg_socket',
'pg_trace',
'pg_transaction_status',
'pg_tty',
'pg_untrace',
'pg_update',
'pg_version',
'php_user_filter::filter',
'proc_close',
'proc_get_status',
'proc_terminate',
'ps_add_bookmark',
'ps_add_launchlink',
'ps_add_locallink',
'ps_add_note',
'ps_add_pdflink',
'ps_add_weblink',
'ps_arc',
'ps_arcn',
'ps_begin_page',
'ps_begin_pattern',
'ps_begin_template',
'ps_circle',
'ps_clip',
'ps_close',
'ps_close_image',
'ps_closepath',
'ps_closepath_stroke',
'ps_continue_text',
'ps_curveto',
'ps_delete',
'ps_end_page',
'ps_end_pattern',
'ps_end_template',
'ps_fill',
'ps_fill_stroke',
'ps_findfont',
'ps_get_buffer',
'ps_get_parameter',
'ps_get_value',
'ps_hyphenate',
'ps_include_file',
'ps_lineto',
'ps_makespotcolor',
'ps_moveto',
'ps_new',
'ps_open_file',
'ps_open_image',
'ps_open_image_file',
'ps_open_memory_image',
'ps_place_image',
'ps_rect',
'ps_restore',
'ps_rotate',
'ps_save',
'ps_scale',
'ps_set_border_color',
'ps_set_border_dash',
'ps_set_border_style',
'ps_set_info',
'ps_set_parameter',
'ps_set_text_pos',
'ps_set_value',
'ps_setcolor',
'ps_setdash',
'ps_setflat',
'ps_setfont',
'ps_setgray',
'ps_setlinecap',
'ps_setlinejoin',
'ps_setlinewidth',
'ps_setmiterlimit',
'ps_setoverprintmode',
'ps_setpolydash',
'ps_shading',
'ps_shading_pattern',
'ps_shfill',
'ps_show',
'ps_show2',
'ps_show_boxed',
'ps_show_xy',
'ps_show_xy2',
'ps_string_geometry',
'ps_stringwidth',
'ps_stroke',
'ps_symbol',
'ps_symbol_name',
'ps_symbol_width',
'ps_translate',
'px_close',
'px_create_fp',
'px_date2string',
'px_delete',
'px_delete_record',
'px_get_field',
'px_get_info',
'px_get_parameter',
'px_get_record',
'px_get_schema',
'px_get_value',
'px_insert_record',
'px_new',
'px_numfields',
'px_numrecords',
'px_open_fp',
'px_put_record',
'px_retrieve_record',
'px_set_blob_file',
'px_set_parameter',
'px_set_tablename',
'px_set_targetencoding',
'px_set_value',
'px_timestamp2string',
'px_update_record',
'radius_acct_open',
'radius_add_server',
'radius_auth_open',
'radius_close',
'radius_config',
'radius_create_request',
'radius_demangle',
'radius_demangle_mppe_key',
'radius_get_attr',
'radius_put_addr',
'radius_put_attr',
'radius_put_int',
'radius_put_string',
'radius_put_vendor_addr',
'radius_put_vendor_attr',
'radius_put_vendor_int',
'radius_put_vendor_string',
'radius_request_authenticator',
'radius_salt_encrypt_attr',
'radius_send_request',
'radius_server_secret',
'radius_strerror',
'readdir',
'readfile',
'recode_file',
'rename',
'rewind',
'rewinddir',
'rmdir',
'rpm_close',
'rpm_get_tag',
'rpm_open',
'sapi_windows_vt100_support',
'scandir',
'sem_acquire',
'sem_get',
'sem_release',
'sem_remove',
'set_file_buffer',
'shm_attach',
'shm_detach',
'shm_get_var',
'shm_has_var',
'shm_put_var',
'shm_remove',
'shm_remove_var',
'shmop_close',
'shmop_delete',
'shmop_open',
'shmop_read',
'shmop_size',
'shmop_write',
'socket_accept',
'socket_addrinfo_bind',
'socket_addrinfo_connect',
'socket_addrinfo_explain',
'socket_bind',
'socket_clear_error',
'socket_close',
'socket_connect',
'socket_export_stream',
'socket_get_option',
'socket_get_status',
'socket_getopt',
'socket_getpeername',
'socket_getsockname',
'socket_import_stream',
'socket_last_error',
'socket_listen',
'socket_read',
'socket_recv',
'socket_recvfrom',
'socket_recvmsg',
'socket_send',
'socket_sendmsg',
'socket_sendto',
'socket_set_block',
'socket_set_blocking',
'socket_set_nonblock',
'socket_set_option',
'socket_set_timeout',
'socket_shutdown',
'socket_write',
'sqlite_close',
'sqlite_fetch_string',
'sqlite_has_more',
'sqlite_open',
'sqlite_popen',
'sqlsrv_begin_transaction',
'sqlsrv_cancel',
'sqlsrv_client_info',
'sqlsrv_close',
'sqlsrv_commit',
'sqlsrv_connect',
'sqlsrv_execute',
'sqlsrv_fetch',
'sqlsrv_fetch_array',
'sqlsrv_fetch_object',
'sqlsrv_field_metadata',
'sqlsrv_free_stmt',
'sqlsrv_get_field',
'sqlsrv_has_rows',
'sqlsrv_next_result',
'sqlsrv_num_fields',
'sqlsrv_num_rows',
'sqlsrv_prepare',
'sqlsrv_query',
'sqlsrv_rollback',
'sqlsrv_rows_affected',
'sqlsrv_send_stream_data',
'sqlsrv_server_info',
'ssh2_auth_agent',
'ssh2_auth_hostbased_file',
'ssh2_auth_none',
'ssh2_auth_password',
'ssh2_auth_pubkey_file',
'ssh2_disconnect',
'ssh2_exec',
'ssh2_fetch_stream',
'ssh2_fingerprint',
'ssh2_methods_negotiated',
'ssh2_publickey_add',
'ssh2_publickey_init',
'ssh2_publickey_list',
'ssh2_publickey_remove',
'ssh2_scp_recv',
'ssh2_scp_send',
'ssh2_sftp',
'ssh2_sftp_chmod',
'ssh2_sftp_lstat',
'ssh2_sftp_mkdir',
'ssh2_sftp_readlink',
'ssh2_sftp_realpath',
'ssh2_sftp_rename',
'ssh2_sftp_rmdir',
'ssh2_sftp_stat',
'ssh2_sftp_symlink',
'ssh2_sftp_unlink',
'ssh2_shell',
'ssh2_tunnel',
'stomp_connect',
'streamWrapper::stream_cast',
'stream_bucket_append',
'stream_bucket_make_writeable',
'stream_bucket_new',
'stream_bucket_prepend',
'stream_context_create',
'stream_context_get_default',
'stream_context_get_options',
'stream_context_get_params',
'stream_context_set_default',
'stream_context_set_params',
'stream_copy_to_stream',
'stream_encoding',
'stream_filter_append',
'stream_filter_prepend',
'stream_filter_remove',
'stream_get_contents',
'stream_get_line',
'stream_get_meta_data',
'stream_isatty',
'stream_set_blocking',
'stream_set_chunk_size',
'stream_set_read_buffer',
'stream_set_timeout',
'stream_set_write_buffer',
'stream_socket_accept',
'stream_socket_client',
'stream_socket_enable_crypto',
'stream_socket_get_name',
'stream_socket_recvfrom',
'stream_socket_sendto',
'stream_socket_server',
'stream_socket_shutdown',
'stream_supports_lock',
'svn_fs_abort_txn',
'svn_fs_apply_text',
'svn_fs_begin_txn2',
'svn_fs_change_node_prop',
'svn_fs_check_path',
'svn_fs_contents_changed',
'svn_fs_copy',
'svn_fs_delete',
'svn_fs_dir_entries',
'svn_fs_file_contents',
'svn_fs_file_length',
'svn_fs_is_dir',
'svn_fs_is_file',
'svn_fs_make_dir',
'svn_fs_make_file',
'svn_fs_node_created_rev',
'svn_fs_node_prop',
'svn_fs_props_changed',
'svn_fs_revision_prop',
'svn_fs_revision_root',
'svn_fs_txn_root',
'svn_fs_youngest_rev',
'svn_repos_create',
'svn_repos_fs',
'svn_repos_fs_begin_txn_for_commit',
'svn_repos_fs_commit_txn',
'svn_repos_open',
'sybase_affected_rows',
'sybase_close',
'sybase_connect',
'sybase_data_seek',
'sybase_fetch_array',
'sybase_fetch_assoc',
'sybase_fetch_field',
'sybase_fetch_object',
'sybase_fetch_row',
'sybase_field_seek',
'sybase_free_result',
'sybase_num_fields',
'sybase_num_rows',
'sybase_pconnect',
'sybase_query',
'sybase_result',
'sybase_select_db',
'sybase_set_message_handler',
'sybase_unbuffered_query',
'tmpfile',
'udm_add_search_limit',
'udm_alloc_agent',
'udm_alloc_agent_array',
'udm_cat_list',
'udm_cat_path',
'udm_check_charset',
'udm_clear_search_limits',
'udm_crc32',
'udm_errno',
'udm_error',
'udm_find',
'udm_free_agent',
'udm_free_res',
'udm_get_doc_count',
'udm_get_res_field',
'udm_get_res_param',
'udm_hash32',
'udm_load_ispell_data',
'udm_set_agent_param',
'unlink',
'vfprintf',
'w32api_init_dtype',
'wddx_add_vars',
'wddx_packet_end',
'wddx_packet_start',
'xml_get_current_byte_index',
'xml_get_current_column_number',
'xml_get_current_line_number',
'xml_get_error_code',
'xml_parse',
'xml_parse_into_struct',
'xml_parser_create',
'xml_parser_create_ns',
'xml_parser_free',
'xml_parser_get_option',
'xml_parser_set_option',
'xml_set_character_data_handler',
'xml_set_default_handler',
'xml_set_element_handler',
'xml_set_end_namespace_decl_handler',
'xml_set_external_entity_ref_handler',
'xml_set_notation_decl_handler',
'xml_set_object',
'xml_set_processing_instruction_handler',
'xml_set_start_namespace_decl_handler',
'xml_set_unparsed_entity_decl_handler',
'xmlrpc_server_add_introspection_data',
'xmlrpc_server_call_method',
'xmlrpc_server_create',
'xmlrpc_server_destroy',
'xmlrpc_server_register_introspection_callback',
'xmlrpc_server_register_method',
'xmlwriter_end_attribute',
'xmlwriter_end_cdata',
'xmlwriter_end_comment',
'xmlwriter_end_document',
'xmlwriter_end_dtd',
'xmlwriter_end_dtd_attlist',
'xmlwriter_end_dtd_element',
'xmlwriter_end_dtd_entity',
'xmlwriter_end_element',
'xmlwriter_end_pi',
'xmlwriter_flush',
'xmlwriter_full_end_element',
'xmlwriter_open_memory',
'xmlwriter_open_uri',
'xmlwriter_output_memory',
'xmlwriter_set_indent',
'xmlwriter_set_indent_string',
'xmlwriter_start_attribute',
'xmlwriter_start_attribute_ns',
'xmlwriter_start_cdata',
'xmlwriter_start_comment',
'xmlwriter_start_document',
'xmlwriter_start_dtd',
'xmlwriter_start_dtd_attlist',
'xmlwriter_start_dtd_element',
'xmlwriter_start_dtd_entity',
'xmlwriter_start_element',
'xmlwriter_start_element_ns',
'xmlwriter_start_pi',
'xmlwriter_text',
'xmlwriter_write_attribute',
'xmlwriter_write_attribute_ns',
'xmlwriter_write_cdata',
'xmlwriter_write_comment',
'xmlwriter_write_dtd',
'xmlwriter_write_dtd_attlist',
'xmlwriter_write_dtd_element',
'xmlwriter_write_dtd_entity',
'xmlwriter_write_element',
'xmlwriter_write_element_ns',
'xmlwriter_write_pi',
'xmlwriter_write_raw',
'xslt_create',
'yaz_addinfo',
'yaz_ccl_conf',
'yaz_ccl_parse',
'yaz_close',
'yaz_database',
'yaz_element',
'yaz_errno',
'yaz_error',
'yaz_es',
'yaz_es_result',
'yaz_get_option',
'yaz_hits',
'yaz_itemorder',
'yaz_present',
'yaz_range',
'yaz_record',
'yaz_scan',
'yaz_scan_result',
'yaz_schema',
'yaz_search',
'yaz_sort',
'yaz_syntax',
'zip_close',
'zip_entry_close',
'zip_entry_compressedsize',
'zip_entry_compressionmethod',
'zip_entry_filesize',
'zip_entry_name',
'zip_entry_open',
'zip_entry_read',
'zip_open',
'zip_read',
];
}
}
#!/usr/bin/env php
<?php declare(strict_types=1);









$functions = require __DIR__ . '/FunctionSignatureMap.php';
$resourceFunctions = [];

foreach ($functions as $function => $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 = <<<EOT
<?php declare(strict_types=1);
/*
 * This file is part of resource-operations.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace SebastianBergmann\ResourceOperations;

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);

<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use SplObjectStorage;




class SplObjectStorageComparator extends Comparator
{








public function accepts($expected, $actual)
{
return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage;
}












public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
{
foreach ($actual as $object) {
if (!$expected->contains($object)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
'Failed asserting that two objects are equal.'
);
}
}

foreach ($expected as $object) {
if (!$actual->contains($object)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
'Failed asserting that two objects are equal.'
);
}
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function sprintf;
use function strtolower;
use DOMDocument;
use DOMNode;
use ValueError;




class DOMNodeComparator extends ObjectComparator
{








public function accepts($expected, $actual)
{
return $expected instanceof DOMNode && $actual instanceof DOMNode;
}













public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])
{
$expectedAsString = $this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function abs;
use function floor;
use function sprintf;
use DateInterval;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Exception;




class DateTimeComparator extends ObjectComparator
{








public function accepts($expected, $actual)
{
return ($expected instanceof DateTime || $expected instanceof DateTimeInterface) &&
($actual instanceof DateTime || $actual instanceof DateTimeInterface);
}














public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])
{


$absDelta = abs($delta);
$delta = new DateInterval(sprintf('PT%dS', $absDelta));
$delta->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';
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function array_unshift;




class Factory
{



private static $instance;




private $customComparators = [];




private $defaultComparators = [];




public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self; 
}

return self::$instance;
}




public function __construct()
{
$this->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);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function array_key_exists;
use function is_array;
use function sort;
use function sprintf;
use function str_replace;
use function trim;








class ArrayComparator extends Comparator
{








public function accepts($expected, $actual)
{
return is_array($expected) && is_array($actual);
}













public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])
{
if ($canonicalize) {
sort($expected);
sort($actual);
}

$remaining = $actual;
$actualAsString = "Array (\n";
$expectedAsString = "Array (\n";
$equal = true;

foreach ($expected as $key => $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));
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function gettype;
use function sprintf;




class TypeComparator extends Comparator
{








public function accepts($expected, $actual)
{
return true;
}












public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
{
if (gettype($expected) != gettype($actual)) {
throw new ComparisonFailure(
$expected,
$actual,

'',
'',
false,
sprintf(
'%s does not match expected type "%s".',
$this->exporter->shortenedExport($actual),
gettype($expected)
)
);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function get_class;
use function in_array;
use function is_object;
use function sprintf;
use function substr_replace;




class ObjectComparator extends ArrayComparator
{








public function accepts($expected, $actual)
{
return is_object($expected) && is_object($actual);
}













public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])
{
if (get_class($actual) !== get_class($expected)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
sprintf(
'%s is not instance of expected class "%s".',
$this->exporter->export($actual),
get_class($expected)
)
);
}


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);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function is_resource;




class ResourceComparator extends Comparator
{








public function accepts($expected, $actual)
{
return is_resource($expected) && is_resource($actual);
}












public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
{
if ($actual != $expected) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual)
);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use RuntimeException;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;




class ComparisonFailure extends RuntimeException
{





protected $expected;






protected $actual;






protected $expectedAsString;






protected $actualAsString;




protected $identical;







protected $message;












public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '')
{
$this->expected = $expected;
$this->actual = $actual;
$this->expectedAsString = $expectedAsString;
$this->actualAsString = $actualAsString;
$this->message = $message;
}

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();
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function abs;
use function is_float;
use function is_infinite;
use function is_nan;
use function is_numeric;
use function is_string;
use function sprintf;




class NumericComparator extends ScalarComparator
{








public function accepts($expected, $actual)
{

return is_numeric($expected) && is_numeric($actual) &&
!(is_string($expected) && is_string($actual));
}












public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
{
if ($this->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);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use Exception;




class ExceptionComparator extends ObjectComparator
{








public function accepts($expected, $actual)
{
return $expected instanceof Exception && $actual instanceof Exception;
}









protected function toArray($object)
{
$array = parent::toArray($object);

unset(
$array['file'],
$array['line'],
$array['trace'],
$array['string'],
$array['xdebug_message']
);

return $array;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use SebastianBergmann\Exporter\Exporter;




abstract class Comparator
{



protected $factory;




protected $exporter;

public function __construct()
{
$this->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);
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function is_bool;
use function is_object;
use function is_scalar;
use function is_string;
use function method_exists;
use function sprintf;
use function strtolower;




class ScalarComparator extends Comparator
{










public function accepts($expected, $actual)
{
return ((is_scalar($expected) xor null === $expected) &&
(is_scalar($actual) xor null === $actual))

|| (is_string($expected) && is_object($actual) && method_exists($actual, '__toString'))
|| (is_object($expected) && method_exists($expected, '__toString') && is_string($actual));
}












public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
{
$expectedToCompare = $expected;
$actualToCompare = $actual;



if ((is_string($expected) && !is_bool($actual)) || (is_string($actual) && !is_bool($expected))) {
$expectedToCompare = (string) $expectedToCompare;
$actualToCompare = (string) $actualToCompare;

if ($ignoreCase) {
$expectedToCompare = strtolower($expectedToCompare);
$actualToCompare = strtolower($actualToCompare);
}
}

if ($expectedToCompare !== $actualToCompare && is_string($expected) && is_string($actual)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
'Failed asserting that two strings are equal.'
);
}

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)
)
);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use function is_float;
use function is_numeric;






class DoubleComparator extends NumericComparator
{





public const EPSILON = 0.0000000001;









public function accepts($expected, $actual)
{
return (is_float($expected) || is_float($actual)) && is_numeric($expected) && is_numeric($actual);
}












public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
{
if ($delta == 0) {
$delta = self::EPSILON;
}

parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Comparator;

use PHPUnit\Framework\MockObject\MockObject;




class MockObjectComparator extends ObjectComparator
{








public function accepts($expected, $actual)
{
return $expected instanceof MockObject && $actual instanceof MockObject;
}









protected function toArray($object)
{
$array = parent::toArray($object);

unset($array['__phpunit_invocationMocker']);

return $array;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CliParser;

use function array_map;
use function array_merge;
use function array_shift;
use function array_slice;
use function assert;
use function count;
use function current;
use function explode;
use function is_array;
use function is_int;
use function is_string;
use function key;
use function next;
use function preg_replace;
use function reset;
use function sort;
use function strlen;
use function strpos;
use function strstr;
use function substr;

final class Parser
{









public function parse(array $argv, string $shortOptions, ?array $longOptions = null): array
{
if (empty($argv)) {
return [[], []];
}

$options = [];
$nonOptions = [];

if ($longOptions) {
sort($longOptions);
}

if (isset($argv[0][0]) && $argv[0][0] !== '-') {
array_shift($argv);
}

reset($argv);

$argv = array_map('trim', $argv);

while (false !== $arg = current($argv)) {
$i = key($argv);

assert(is_int($i));

next($argv);

if ($arg === '') {
continue;
}

if ($arg === '--') {
$nonOptions = array_merge($nonOptions, array_slice($argv, $i + 1));

break;
}

if ($arg[0] !== '-' || (strlen($arg) > 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);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CliParser;

use function sprintf;
use RuntimeException;

final class AmbiguousOptionException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Option "%s" is ambiguous',
$option
)
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CliParser;

use function sprintf;
use RuntimeException;

final class UnknownOptionException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Unknown option "%s"',
$option
)
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CliParser;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CliParser;

use function sprintf;
use RuntimeException;

final class OptionDoesNotAllowArgumentException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Option "%s" does not allow an argument',
$option
)
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CliParser;

use function sprintf;
use RuntimeException;

final class RequiredOptionArgumentMissingException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Required argument for option "%s" is missing',
$option
)
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;

use function array_merge;
use function array_unique;
use function count;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\NodeVisitorAbstract;

final class LineCountingVisitor extends NodeVisitorAbstract
{



private $linesOfCode;




private $comments = [];




private $linesWithStatements = [];

public function __construct(int $linesOfCode)
{
$this->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;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;

use function substr_count;
use PhpParser\Error;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;

final class Counter
{



public function countInSourceFile(string $sourceFile): LinesOfCode
{
return $this->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();
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;




final class LinesOfCode
{



private $linesOfCode;




private $commentLinesOfCode;




private $nonCommentLinesOfCode;




private $logicalLinesOfCode;





public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode, int $logicalLinesOfCode)
{
if ($linesOfCode < 0) {
throw new NegativeValueException('$linesOfCode must not be negative');
}

if ($commentLinesOfCode < 0) {
throw new NegativeValueException('$commentLinesOfCode must not be negative');
}

if ($nonCommentLinesOfCode < 0) {
throw new NegativeValueException('$nonCommentLinesOfCode must not be negative');
}

if ($logicalLinesOfCode < 0) {
throw new NegativeValueException('$logicalLinesOfCode must not be negative');
}

if ($linesOfCode - $commentLinesOfCode !== $nonCommentLinesOfCode) {
throw new IllogicalValuesException('$linesOfCode !== $commentLinesOfCode + $nonCommentLinesOfCode');
}

$this->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(),
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;

use InvalidArgumentException;

final class NegativeValueException extends InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;

use LogicException;

final class IllogicalValuesException extends LogicException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\LinesOfCode;

final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\PHPUnit\TestCase;
use Codeception\TestInterface;
use PHPUnit\Framework\Assert;

/**
 * Wrapper for basic shell commands and shell output
 *
 * ## Responsibility
 * * Maintainer: **davert**
 * * Status: **stable**
 * * Contact: codecept@davert.mail.ua
 *
 * *Please review the code of non-stable modules and provide patches if you have issues.*
 */
class Cli extends Module
{
    public string $output = '';

    public int $result;

    public function _before(TestInterface $test): void
    {
        $this->output = '';
    }

    /**
     * Executes a shell command.
     * Fails if exit code is > 0. You can disable this by passing `false` as second argument
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $I->seeResultCodeIs(0);
     * ```
     */
    public function seeResultCodeIs(int $code): void
    {
        $this->assertEquals($this->result, $code, "result code is {$code}");
    }

    /**
     * Checks result code
     *
     * ```php
     * <?php
     * $I->seeResultCodeIsNot(0);
     * ```
     */
    public function seeResultCodeIsNot(int $code): void
    {
        $this->assertNotEquals($this->result, $code, "result code is {$code}");
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Exception\ModuleException;
use Codeception\Lib\Interfaces\RequiresPackage;
use Codeception\Module;
use Codeception\TestInterface;
use Exception;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Exception\AMQPProtocolChannelException;
use PhpAmqpLib\Message\AMQPMessage;

/**
 * This module interacts with message broker software that implements
 * the Advanced Message Queuing Protocol (AMQP) standard. For example, RabbitMQ (tested).
 *
 * <div class="alert alert-info">
 * To use this module with Composer you need <em>"php-amqplib/php-amqplib": "~2.4"</em> package.
 * </div>
 *
 * ## 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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $message = $I->grabMessageFromQueue('queue.emails');
     * ```
     */
    public function grabMessageFromQueue(string $queue): ?AMQPMessage
    {
        return $this->getChannel()->basic_get($queue);
    }

    /**
     * Purge a specific queue defined in config.
     *
     * ``` php
     * <?php
     * $I->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
     * <?php
     * $I->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;
                }
            }
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Configuration;
use Codeception\Exception\ModuleConfigException;
use Codeception\Exception\ModuleException;
use Codeception\Lib\DbPopulator;
use Codeception\Lib\Driver\Db as Driver;
use Codeception\Lib\Interfaces\Db as DbInterface;
use Codeception\Lib\Notification;
use Codeception\Module;
use Codeception\TestInterface;
use Codeception\Util\ActionSequence;
use Exception;
use InvalidArgumentException;
use PDO;
use PDOException;

/**
 * Access a database.
 *
 * The most important function of this module is to clean a database before each test.
 * This module also provides actions to perform checks in a database, e.g. [seeInDatabase()](https://codeception.com/docs/modules/Db#seeInDatabase)
 *
 * In order to have your database populated with data you need a raw SQL dump.
 * Simply put the dump in the `tests/_data` directory (by default) and specify the path in the config.
 * The next time after the database is cleared, all your data will be restored from the dump.
 * Don't forget to include `CREATE TABLE` statements in the dump.
 *
 * Supported and tested databases are:
 *
 * * MySQL
 * * SQLite (i.e. just one file)
 * * PostgreSQL
 *
 * Also available:
 *
 * * MS SQL
 * * Oracle
 *
 * Connection is done by database Drivers, which are stored in the `Codeception\Lib\Driver` namespace.
 * [Check out the drivers](https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Lib/Driver)
 * if you run into problems loading dumps and cleaning databases.
 *
 * ## Config
 *
 * * dsn *required* - PDO DSN
 * * user *required* - username to access database
 * * password *required* - password
 * * dump - path to database dump
 * * populate: false - whether the the dump should be loaded before the test suite is started
 * * cleanup: false - whether the dump should be reloaded before each test
 * * reconnect: false - whether the module should reconnect to the database before each test
 * * waitlock: 0 - wait lock (in seconds) that the database session should use for DDL statements
 * * ssl_key - path to the SSL key (MySQL specific, @see https://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-key)
 * * ssl_cert - path to the SSL certificate (MySQL specific, @see https://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-ssl-cert)
 * * ssl_ca - path to the SSL certificate authority (MySQL specific, @see https://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-ssl-ca)
 * * ssl_verify_server_cert - disables certificate CN verification (MySQL specific, @see https://php.net/manual/de/ref.pdo-mysql.php)
 * * ssl_cipher - list of one or more permissible ciphers to use for SSL encryption (MySQL specific, @see https://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-cipher)
 * * databases - include more database configs and switch between them in tests.
 * * initial_queries - list of queries to be executed right after connection to the database has been initiated, i.e. creating the database if it does not exist or preparing the database collation
 * * skip_cleanup_if_failed - Do not perform the cleanup if the tests failed. If this is used, manual cleanup might be required when re-running
 * ## Example
 *
 *     modules:
 *        enabled:
 *           - Db:
 *              dsn: 'mysql:host=localhost;dbname=testdb'
 *              user: 'root'
 *              password: ''
 *              dump: 'tests/_data/dump.sql'
 *              populate: true
 *              cleanup: true
 *              reconnect: true
 *              waitlock: 10
 *              skip_cleanup_if_failed: true
 *              ssl_key: '/path/to/client-key.pem'
 *              ssl_cert: '/path/to/client-cert.pem'
 *              ssl_ca: '/path/to/ca-cert.pem'
 *              ssl_verify_server_cert: false
 *              ssl_cipher: 'AES256-SHA'
 *              initial_queries:
 *                  - 'CREATE DATABASE IF NOT EXISTS temp_db;'
 *                  - 'USE temp_db;'
 *                  - 'SET NAMES utf8;'
 *
 * ## Example with multi-dumps
 *     modules:
 *          enabled:
 *             - Db:
 *                dsn: 'mysql:host=localhost;dbname=testdb'
 *                user: 'root'
 *                password: ''
 *                dump:
 *                   - 'tests/_data/dump.sql'
 *                   - 'tests/_data/dump-2.sql'
 *
 * ## Example with multi-databases
 *
 *     modules:
 *        enabled:
 *           - Db:
 *              dsn: 'mysql:host=localhost;dbname=testdb'
 *              user: 'root'
 *              password: ''
 *              databases:
 *                 db2:
 *                    dsn: 'mysql:host=localhost;dbname=testdb2'
 *                    user: 'userdb2'
 *                    password: ''
 *
 * ## Example with Sqlite
 *
 *     modules:
 *        enabled:
 *           - Db:
 *              dsn: 'sqlite:relative/path/to/sqlite-database.db'
 *              user: ''
 *              password: ''
 *
 * ## SQL data dump
 *
 * There are two ways of loading the dump into your database:
 *
 * ### Populator
 *
 * The recommended approach is to configure a `populator`, an external command to load a dump. Command parameters like host, username, password, database
 * can be obtained from the config and inserted into placeholders:
 *
 * For MySQL:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - Db:
 *          dsn: 'mysql:host=localhost;dbname=testdb'
 *          user: 'root'
 *          password: ''
 *          dump: 'tests/_data/dump.sql'
 *          populate: true # run populator before all tests
 *          cleanup: true # run populator before each test
 *          populator: 'mysql -u $user -h $host $dbname < $dump'
 * ```
 *
 * For PostgreSQL (using pg_restore)
 *
 * ```
 * modules:
 *    enabled:
 *       - Db:
 *          dsn: 'pgsql:host=localhost;dbname=testdb'
 *          user: 'root'
 *          password: ''
 *          dump: 'tests/_data/db_backup.dump'
 *          populate: true # run populator before all tests
 *          cleanup: true # run populator before each test
 *          populator: 'pg_restore -u $user -h $host -D $dbname < $dump'
 * ```
 *
 *  Variable names are being taken from config and DSN which has a `keyword=value` format, so you should expect to have a variable named as the
 *  keyword with the full value inside it.
 *
 *  PDO dsn elements for the supported drivers:
 *  * MySQL: [PDO_MYSQL DSN](https://secure.php.net/manual/en/ref.pdo-mysql.connection.php)
 *  * SQLite: [PDO_SQLITE DSN](https://secure.php.net/manual/en/ref.pdo-sqlite.connection.php) - use _relative_ path from the project root
 *  * PostgreSQL: [PDO_PGSQL DSN](https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php)
 *  * MSSQL: [PDO_SQLSRV DSN](https://secure.php.net/manual/en/ref.pdo-sqlsrv.connection.php)
 *  * Oracle: [PDO_OCI DSN](https://secure.php.net/manual/en/ref.pdo-oci.connection.php)
 *
 * ### Dump
 *
 * Db module by itself can load SQL dump without external tools by using current database connection.
 * This approach is system-independent, however, it is slower than using a populator and may have parsing issues (see below).
 *
 * Provide a path to SQL file in `dump` config option:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - Db:
 *          dsn: 'mysql:host=localhost;dbname=testdb'
 *          user: 'root'
 *          password: ''
 *          populate: true # load dump before all tests
 *          cleanup: true # load dump for each test
 *          dump: 'tests/_data/dump.sql'
 * ```
 *
 *  To parse SQL Db file, it should follow this specification:
 *  * Comments are permitted.
 *  * The `dump.sql` may contain multiline statements.
 *  * The delimiter, a semi-colon in this case, must be on the same line as the last statement:
 *
 * ```sql
 * -- Add a few contacts to the table.
 * REPLACE INTO `Contacts` (`created`, `modified`, `status`, `contact`, `first`, `last`) VALUES
 * (NOW(), NOW(), 1, 'Bob Ross', 'Bob', 'Ross'),
 * (NOW(), NOW(), 1, 'Fred Flintstone', 'Fred', 'Flintstone');
 *
 * -- Remove existing orders for testing.
 * DELETE FROM `Order`;
 * ```
 * ## Query generation
 *
 * `seeInDatabase`, `dontSeeInDatabase`, `seeNumRecords`, `grabFromDatabase` and `grabNumRecords` methods
 * accept arrays as criteria. WHERE condition is generated using item key as a field name and
 * item value as a field value.
 *
 * Example:
 * ```php
 * <?php
 * $I->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
 * <?php
 * $I->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
 * <?php
 * $I->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<string, mixed>
     */
    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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $mails = $I->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
     * <?php
     * $mail = $I->grabFromDatabase('users', 'email', array('name' => 'Davert'));
     * ```
     * Comparison expressions can be used as well:
     *
     * ```php
     * <?php
     * $postNum = $I->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
     * <?php
     * $mail = $I->grabEntryFromDatabase('users', array('name' => 'Davert'));
     * ```
     * Comparison expressions can be used as well:
     *
     * ```php
     * <?php
     * $post = $I->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
     * <?php
     * $mail = $I->grabEntriesFromDatabase('users', array('name' => 'Davert'));
     * ```
     * Comparison expressions can be used as well:
     *
     * ```php
     * <?php
     * $post = $I->grabEntriesFromDatabase('posts', ['num_comments >=' => 100]);
     * $user = $I->grabEntriesFromDatabase('users', ['email like' => 'miles%']);
     * ```
     *
     * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`.
     *
     * @return array<array<string, mixed>> 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
     * <?php
     * $I->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);
    }
}
<?php

namespace Codeception\Lib\Interfaces;

interface Db
{
    /**
     * Asserts that a row with the given column values exists.
     * Provide table name and column values.
     *
     * ```php
     * <?php
     * $I->seeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']);
     * ```
     * Fails if no such user found.
     *
     * Comparison expressions can be used as well:
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $I->dontSeeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']);
     * ```
     * Fails if such user was found.
     *
     * Comparison expressions can be used as well:
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $mail = $I->grabFromDatabase('users', 'email', array('name' => 'Davert'));
     * ```
     * Comparison expressions can be used as well:
     *
     * ```php
     * <?php
     * $post = $I->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 = []);
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use PDO;

class MySql extends Db
{
    public function cleanup(): void
    {
        $this->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];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Exception\ModuleException;
use Exception;
use InvalidArgumentException;
use PDO;
use PDOException;
use PDOStatement;

class Db
{
    protected ?PDO $dbh = null;

    protected string $dsn;

    protected string $user;

    protected string $password;

    /**
     * @see https://www.php.net/manual/de/pdo.construct.php
     */
    protected ?array $options = [];

    /**
     * Associative array with table name => 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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Configuration;
use Codeception\Exception\ModuleException;
use PDO;

class Sqlite extends Db
{
    protected bool $hasSnapshot = false;
    
    protected string $filename = '';

    public function __construct(string $dsn, string $user = null, string $password = null, array $options = null)
    {
        $filename = substr($dsn, 7);
        if ($filename === ':memory:') {
            throw new ModuleException(__CLASS__, ':memory: database is not supported');
        }

        $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

class Oci extends Db
{
    public function setWaitLock(int $seconds): void
    {
        $this->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];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use PDO;

class SqlSrv extends Db
{
    public function getDb()
    {
        $matches = [];
        $matched = preg_match('#Database=(.*);?#s', $this->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];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Exception\ModuleException;
use PDO;
use PDOException;

class PostgreSql extends Db
{
    protected bool $putline = false;

    /**
     * @var null|resource|bool
     */
    protected $connection;

    /**
     * @var mixed|null
     */
    protected $searchPath;

    /**
     * Loads a SQL file.
     *
     * @param string[] $sql sql file
     */
    public function load(array $sql): void
    {
        $query = '';
        $delimiter = ';';
        $delimiterLength = 1;

        $dollarsOpen = false;
        foreach ($sql as $singleSql) {
            if (preg_match('#DELIMITER ([\;\$\|\\\]+)#i', $singleSql, $match)) {
                $delimiter = $match[1];
                $delimiterLength = strlen($delimiter);
                continue;
            }

            $parsed = trim($query) == '' && $this->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];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib;

/**
 * Populates a db using a parameterized command built from the Db module configuration.
 */
class DbPopulator
{
    protected array $config = [];

    protected array $commands = [];

    /**
     * Constructs a DbPopulator object for the given command and Db module.
     *
     * @internal param string $command The parameterized command to evaluate and execute later.
     * @internal param Codeception\Module\Db|null $dbModule The Db module used to build the populator command or null.
     */
    public function __construct(array $config)
    {
        $this->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
     * <?php
     *
     * $command = 'Hello $name';
     * $config = ['name' => '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;
    }
}
<?php

namespace Codeception\Lib\Interfaces;

interface ScreenshotSaver
{
    /**
     * Saves screenshot of current page to a file
     *
     * ```php
     * $this->getModule('{{MODULE_NAME}}')->_saveScreenshot(codecept_output_dir().'screenshot_1.png');
     * ```
     * @api
     */
    public function _saveScreenshot(string $filename);
}
<?php

namespace Codeception\Lib\Interfaces;

interface ElementLocator
{
    /**
     * Locates element using available Codeception locator types:
     *
     * * XPath
     * * CSS
     * * Strict Locator
     *
     * Use it in Helpers or GroupObject or Extension classes:
     *
     * ```php
     * <?php
     * $els = $this->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;
}
<?php

// phpcs:disable Generic.Files.LineLength.TooLong

namespace Codeception\Lib\Interfaces;

interface PageSourceSaver
{
    /**
     * Saves page source of to a file
     *
     * ```php
     * $this->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
     * <?php
     * $I->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;
}
<?php

// phpcs:disable Generic.Files.LineLength.TooLong

namespace Codeception\Lib\Interfaces;

interface Web
{
    /**
     * Opens the page for the given relative URI.
     *
     * ```php
     * <?php
     * // opens front page
     * $I->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
     * <?php
     * $I->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:
     *
     *   - `<p>I am Stronger than thou</p>`
     *   - `<script>document.createElement('strong');</script>`
     *
     * But will *not* be true for strings like:
     *
     *   - `<strong>Home</strong>`
     *   - `<div class="strong">Home</strong>`
     *   - `<!-- strong -->`
     *
     * 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
     * <?php
     * $I->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:
     *
     *   - `<p>I am Stronger than thou</p>`
     *   - `<script>document.createElement('strong');</script>`
     *
     * But will ignore strings like:
     *
     *   - `<strong>Home</strong>`
     *   - `<div class="strong">Home</strong>`
     *   - `<!-- strong -->`
     *
     * 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
     * <?php
     * $I->seeInSource('<h1>Green eggs &amp; ham</h1>');
     * ```
     */
    public function seeInSource(string $raw): void;

    /**
     * Checks that the current page contains the given string in its
     * raw source code.
     *
     * ```php
     * <?php
     * $I->dontSeeInSource('<h1>Green eggs &amp; ham</h1>');
     * ```
     */
    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
     * <?php
     * $I->submitForm('#login', [
     *     'login' => 'davert',
     *     'password' => '123456'
     * ]);
     * // or
     * $I->submitForm('#login', [
     *     'login' => 'davert',
     *     'password' => '123456'
     * ], 'submitButtonName');
     *
     * ```
     *
     * For example, given this sample "Sign Up" form:
     *
     * ``` html
     * <form id="userForm">
     *     Login:
     *     <input type="text" name="user[login]" /><br/>
     *     Password:
     *     <input type="password" name="user[password]" /><br/>
     *     Do you agree to our terms?
     *     <input type="checkbox" name="user[agree]" /><br/>
     *     Subscribe to our newsletter?
     *     <input type="checkbox" name="user[newsletter]" value="1" checked="checked" /><br/>
     *     Select pricing plan:
     *     <select name="plan">
     *         <option value="1">Free</option>
     *         <option value="2" selected="selected">Paid</option>
     *     </select>
     *     <input type="submit" name="submitButton" value="Submit" />
     * </form>
     * ```
     *
     * You could write the following to submit it:
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $form = [
     *      'field1' => '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
     * <?php
     * $I->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
     * <?php
     * // This will NOT work correctly
     * $I->submitForm('#my-form', [
     *     'field[]' => 'value',
     *     'field[]' => 'another value',  // 'field[]' is already a defined key
     * ]);
     * ```
     *
     * The solution is to pass an array value:
     *
     * ```php
     * <?php
     * // This way both values are submitted
     * $I->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
     * <?php
     * // simple link
     * $I->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
     * <?php
     * $I->seeLink('Logout'); // matches <a href="#">Logout</a>
     * $I->seeLink('Logout','/logout'); // matches <a href="/logout">Logout</a>
     * ```
     */
    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
     * <?php
     * $I->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
     * <?php
     * // to match: /home/dashboard
     * $I->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
     * <?php
     * // to match root url
     * $I->seeCurrentUrlEquals('/');
     * ```
     */
    public function seeCurrentUrlEquals(string $uri): void;

    /**
     * Checks that the current URL matches the given regular expression.
     *
     * ```php
     * <?php
     * // to match root url
     * $I->seeCurrentUrlMatches('~^/users/(\d+)~');
     * ```
     */
    public function seeCurrentUrlMatches(string $uri): void;

    /**
     * Checks that the current URI doesn't contain the given string.
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * // current url is not root
     * $I->dontSeeCurrentUrlEquals('/');
     * ```
     */
    public function dontSeeCurrentUrlEquals(string $uri): void;

    /**
     * Checks that current url doesn't match the given regular expression.
     *
     * ```php
     * <?php
     * // to match root url
     * $I->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
     * <?php
     * $user_id = $I->grabFromCurrentUrl('~^/user/(\d+)/~');
     * $uri = $I->grabFromCurrentUrl();
     * ```
     */
    public function grabFromCurrentUrl(string $uri = null): mixed;

    /**
     * Checks that the specified checkbox is checked.
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->seeInFormFields('.form-class', [
     *      'multiselect' => [
     *          'value1',
     *          'value2',
     *      ],
     *      'checkbox[]' => [
     *          'a checked value',
     *          'another checked value',
     *      ],
     * ]);
     * ```
     *
     * Additionally, checkbox values can be checked with a boolean.
     *
     * ```php
     * <?php
     * $I->seeInFormFields('#form-id', [
     *      'checkbox1' => true,        // passes if checked
     *      'checkbox2' => false,       // passes if unchecked
     * ]);
     * ```
     *
     * Pair this with submitForm for quick testing magic.
     *
     * ```php
     * <?php
     * $form = [
     *      'field1' => '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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->checkOption('#agree');
     * ```
     */
    public function checkOption($option): void;

    /**
     * Unticks a checkbox.
     *
     * ```php
     * <?php
     * $I->uncheckOption('#notify');
     * ```
     */
    public function uncheckOption($option): void;

    /**
     * Fills a text field or textarea with the given string.
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * // file is stored in 'tests/_data/prices.xls'
     * $I->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
     * <?php
     * $heading = $I->grabTextFrom('h1');
     * $heading = $I->grabTextFrom('descendant-or-self::h1');
     * $value = $I->grabTextFrom('~<input value=(.*?)]~sgi'); // match with a regex
     * ```
     */
    public function grabTextFrom($cssOrXPathOrRegex): mixed;

    /**
     * Finds the value for the given form field.
     * If a fuzzy locator is used, the field is found by field name, CSS, and XPath.
     *
     * ```php
     * <?php
     * $name = $I->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
     * <?php
     * $I->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
     * <a href="#first">First</a>
     * <a href="#second">Second</a>
     * <a href="#third">Third</a>
     * ```
     *
     * ```php
     * <?php
     * // would return ['First', 'Second', 'Third']
     * $aLinkText = $I->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 `<html>` tag is present.
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->seeOptionIsSelected('#form input[name=payment]', 'Visa');
     * ```
     *
     * @return mixed|void
     */
    public function seeOptionIsSelected($selector, string $optionText);

    /**
     * Checks that the given option is not selected.
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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;
}
<?php

namespace Codeception\Lib\Interfaces;

interface SessionSnapshot
{
    /**
     * Saves current cookies into named snapshot in order to restore them in other tests
     * This is useful to save session state between tests.
     * For example, if user needs log in to site for each test this scenario can be executed once
     * while other tests can just restore saved cookies.
     *
     * ``` php
     * <?php
     * // inside AcceptanceTester class:
     *
     * public function login()
     * {
     *      // if snapshot exists - skipping login
     *      if ($I->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);
}
<?php

namespace Codeception\Lib\Interfaces;

interface Remote
{
    /**
     * Changes the subdomain for the 'url' configuration parameter.
     * Does not open a page; use `amOnPage` for that.
     *
     * ``` php
     * <?php
     * // If config is: 'https://mysite.com'
     * // or config is: 'https://www.mysite.com'
     * // or config is: 'https://company.mysite.com'
     *
     * $I->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
     * <?php
     * $I->amOnUrl('https://codeception.com');
     * $I->amOnPage('/quickstart'); // moves to https://codeception.com/quickstart
     * ```
     */
    public function amOnUrl(string $url): void;

    public function _getUrl();
}
<?php

namespace Codeception\Lib\Interfaces;

interface MultiSession
{
    public function _initializeSession(): void;

    public function _loadSession($session): void;

    public function _backupSession();

    public function _closeSession($session = null): void;

    public function _getName(): string;
}
<?php

declare(strict_types=1);

namespace Codeception\Constraint;

use PHPUnit\Framework\Constraint\Constraint;

use function mb_stripos;
use function mb_strlen;
use function mb_substr;
use function preg_replace;
use function sprintf;
use function strtr;
use function trim;

class Page extends Constraint
{
    protected string $uri;

    protected string $string;

    public function __construct(string $string, string $uri = '')
    {
        $this->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}";
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use GuzzleHttp\Psr7\Uri as Psr7Uri;
use InvalidArgumentException;

use function dirname;
use function ltrim;
use function parse_url;
use function preg_match;
use function rtrim;

class Uri
{
    /**
     * Merges the passed $add argument onto $base.
     *
     * If a relative URL is passed as the 'path' part of the $add url
     * array, the relative URL is mapped using the base 'path' part as
     * its base.
     *
     * @param string $baseUri the base URL
     * @param string $uri the URL to merge
     * @return string the merged array
     */
    public static function mergeUrls(string $baseUri, string $uri): string
    {
        $base = new Psr7Uri($baseUri);
        $parts = parse_url($uri);

        //If the relative URL does not parse, attempt to parse the entire URL.
        //PHP Known bug ( https://bugs.php.net/bug.php?id=70942 )
        if ($parts === false) {
            $parts = parse_url($base . $uri);
        }

        if ($parts === false) {
            throw new InvalidArgumentException("Invalid URI {$uri}");
        }

        if (isset($parts['host']) && isset($parts['scheme'])) {
            // if this is an absolute url, replace with it
            return $uri;
        }

        if (isset($parts['host'])) {
            $base = $base->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, '/');
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use DOMDocument;
use DOMXPath;
use Exception;
use Facebook\WebDriver\WebDriverBy;
use InvalidArgumentException;
use Symfony\Component\CssSelector\CssSelectorConverter;
use Symfony\Component\CssSelector\Exception\ParseException;
use Symfony\Component\CssSelector\XPath\Translator;

use function abs;
use function class_exists;
use function func_get_args;
use function implode;
use function is_array;
use function is_int;
use function is_string;
use function key;
use function preg_match;
use function sprintf;
use function strtolower;

/**
 * Set of useful functions for using CSS and XPath locators.
 * Please check them before writing complex functional or acceptance tests.
 *
 */
class Locator
{
    /**
     * Applies OR operator to any number of CSS or XPath selectors.
     * You can mix up CSS and XPath selectors here.
     *
     * ```php
     * <?php
     * use \Codeception\Util\Locator;
     *
     * $I->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
     * <?php
     * use \Codeception\Util\Locator;
     *
     * $I->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
     * <?php
     * use \Codeception\Util\Locator;
     *
     * $I->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
     * <?php
     * use \Codeception\Util\Locator;
     *
     * $I->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
     * <?php
     * use Codeception\Util\Locator;
     *
     * $I->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
     * <?php
     * use \Codeception\Util\Locator;
     *
     * $I->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
     * <?php
     * Locator::isCSS('#user .hello') => 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
     * <?php
     * Locator::isXPath('#user .hello') => 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
     * <?php
     * Locator::isID('#user') => 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
     * <?php
     * Locator::isClass('.hello') => 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
     * <?php
     * use Codeception\Util\Locator;
     *
     * Locator::contains('label', 'Name'); // label containing name
     * Locator::contains('div[@contenteditable=true]', 'hello world');
     * ```
     */
    public static function contains(string $element, string $text): string
    {
        $text = Translator::getXpathLiteral($text);
        return sprintf('%s[%s]', self::toXPath($element), "contains(., {$text})");
    }

    /**
     * Locates element at position.
     * Either CSS or XPath locator can be passed as locator,
     * position is an integer. If a negative value is provided, counting starts from the last element.
     * First element has index 1
     *
     * ```php
     * <?php
     * use Codeception\Util\Locator;
     *
     * Locator::elementAt('//table/tr', 2); // second row
     * Locator::elementAt('//table/tr', -1); // last row
     * Locator::elementAt('table#grind>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
     * <?php
     * use Codeception\Util\Locator;
     *
     * Locator::firstElement('//table/tr');
     * ```
     */
    public static function firstElement(string $element): string
    {
        return self::elementAt($element, 1);
    }

    /**
     * Locates last element of group elements.
     * Either CSS or XPath locator can be passed as locator,
     * Equal to `Locator::elementAt($locator, -1)`
     *
     * ```php
     * <?php
     * use Codeception\Util\Locator;
     *
     * Locator::lastElement('//table/tr');
     * ```
     */
    public static function lastElement(string $element): string
    {
        return self::elementAt($element, 'last()');
    }

    /**
     * Transforms strict locator, \Facebook\WebDriver\WebDriverBy into a string representation
     */
    public static function humanReadableString(WebDriverBy|array|string $selector): string
    {
        if (is_string($selector)) {
            return "'{$selector}'";
        }
        if (is_array($selector)) {
            $type = strtolower(key($selector));
            $locator = $selector[$type];
            return "{$type} '{$locator}'";
        }
        if (class_exists('\Facebook\WebDriver\WebDriverBy') && $selector instanceof WebDriverBy) {
            $type = $selector->getMechanism();
            $locator = $selector->getValue();
            return "{$type} '{$locator}'";
        }
        throw new InvalidArgumentException("Unrecognized selector");
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use function ucfirst;

class MalformedLocatorException extends \RuntimeException
{
    public function __construct(string $locator, string $type = 'CSS or XPath')
    {
        parent::__construct(ucfirst($type) . " locator is malformed: {$locator}");
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Codeception\Util\Locator;
use PHPUnit\Framework\AssertionFailedError;

use function is_string;

class ElementNotFound extends AssertionFailedError
{
    public function __construct($selector, string $message = '')
    {
        if (!is_string($selector) || !str_contains($selector, "'")) {
            $selector = Locator::humanReadableString($selector);
        }
        parent::__construct("{$message} element with {$selector} was not found.");
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\TestInterface;
use Codeception\Exception\ModuleException;

/**
 *
 * Works with SFTP/FTP servers.
 *
 * In order to test the contents of a specific file stored on any remote FTP/SFTP system
 * this module downloads a temporary file to the local system. The temporary directory is
 * defined by default as ```tests/_data``` to specify a different directory set the tmp config
 * option to your chosen path.
 *
 * Don't forget to create the folder and ensure its writable.
 *
 * Supported and tested FTP types are:
 *
 * * FTP
 * * SFTP
 *
 * Connection uses php build in FTP client for FTP,
 * connection to SFTP uses [phpseclib](http://phpseclib.sourceforge.net/) pulled in using composer.
 *
 * For SFTP, add [phpseclib](http://phpseclib.sourceforge.net/) to require list.
 * ```
 * "require": {
 *  "phpseclib/phpseclib": "^2.0.14"
 * }
 * ```
 *
 * ## Status
 *
 * * Stability:
 *     - FTP: **stable**
 *     - SFTP: **stable**
 *
 * ## Config
 *
 * * type: ftp - type of connection ftp/sftp (defaults to ftp).
 * * host *required* - hostname/ip address of the ftp server.
 * * port: 21 - port number for the ftp server
 * * timeout: 90 - timeout settings for connecting the ftp server.
 * * user: anonymous - user to access ftp server, defaults to anonymous authentication.
 * * password - password, defaults to empty for anonymous.
 * * key - path to RSA key for sftp.
 * * tmp - path to local directory for storing tmp files.
 * * passive: true - Turns on or off passive mode (FTP only)
 * * cleanup: true - remove tmp files from local directory on completion.
 *
 * ### Example
 * #### Example (FTP)
 *
 *     modules:
 *        enabled: [FTP]
 *        config:
 *           FTP:
 *              type: ftp
 *              host: '127.0.0.1'
 *              port: 21
 *              timeout: 120
 *              user: 'root'
 *              password: 'root'
 *              key: ~/.ssh/id_rsa
 *              tmp: 'tests/_data/ftp'
 *              passive: true
 *              cleanup: false
 *
 * #### Example (SFTP)
 *
 *     modules:
 *        enabled: [FTP]
 *        config:
 *           FTP:
 *              type: sftp
 *              host: '127.0.0.1'
 *              port: 22
 *              timeout: 120
 *              user: 'root'
 *              password: 'root'
 *              key: ''
 *              tmp: 'tests/_data/ftp'
 *              cleanup: false
 *
 *
 * This module extends the Filesystem module, file contents methods are inherited from this module.
 */
class FTP extends Filesystem
{
    /**
     * FTP/SFTP connection handler
     * @var null|bool|\Net_SFTP|\phpseclib\Net\SFTP|resource
     */
    protected $ftp = null;

    /**
     * Configuration options and default settings
     */
    protected array $config = [
        'type'     => '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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->deleteDir('vendor');
     * ```
     */
    public function deleteDir(string $dirname): void
    {
        $this->delete($this->absolutizePath($dirname));
    }

    /**
     * Erases directory contents on the FTP/SFTP server
     *
     * ``` php
     * <?php
     * $I->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
     * <?php
     * $files = $I->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: <empty>");
        return [];
    }

    /**
     * Grabber method for returning file/folders count in directory
     *
     * ```php
     * <?php
     * $count = $I->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
     * <?php
     * $size = $I->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
     * <?php
     * $time = $I->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
     * <?php
     * $pwd = $I->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';
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\Exception\ModuleException;
use Codeception\TestInterface;

/**
 * Sequence solves data cleanup issue in alternative way.
 * Instead cleaning up the database between tests,
 * you can use generated unique names, that should not conflict.
 * When you create article on a site, for instance, you can assign it a unique name and then check it.
 *
 * This module has no actions, but introduces a function `sq` for generating unique sequences within test and
 * `sqs` for generating unique sequences across suite.
 *
 * ### Usage
 *
 * Function `sq` generates sequence, the only parameter it takes, is id.
 * You can get back to previously generated sequence using that id:
 *
 * ``` php
 * <?php
 * sq('post1'); // post1_521fbc63021eb
 * sq('post2'); // post2_521fbc6302266
 * sq('post1'); // post1_521fbc63021eb
 * ```
 *
 * Example:
 *
 * ``` php
 * <?php
 * $I->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
 * <?php
 *
 * for ($i = 0; $i<10; $i++) {
 *      $I->haveInDatabase('users', array('login' => sq("user$i"), 'email' => sq("user$i").'@email.com');
 * }
 * ```
 *
 * Cest Suite tests:
 *
 * ``` php
 * <?php
 * class UserTest
 * {
 *     public function createUser(AcceptanceTester $I)
 *     {
 *         $I->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<int|string,string>
     */
    public static array $hash = [];

    /**
     * @var array<int|string,string>
     */
    public static array $suiteHash = [];

    public static string $prefix = '';

    /**
     * @var array<string, string>
     */
    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 = [];
    }
}
<?php

declare(strict_types=1);

use Codeception\Module\Sequence;

function sq(int|string $id = null): string
{
    if ($id && isset(Sequence::$hash[$id])) {
        return Sequence::$hash[$id];
    }

    $prefix = str_replace('{id}', (string)$id, Sequence::$prefix);
    $sequence = $prefix . uniqid((string)$id);
    if ($id) {
        Sequence::$hash[$id] = $sequence;
    }

    return $sequence;
}

function sqs(int|string $id = null): string
{
    if ($id && isset(Sequence::$suiteHash[$id])) {
        return Sequence::$suiteHash[$id];
    }

    $prefix = str_replace('{id}', (string)$id, Sequence::$prefix);
    $sequence = $prefix . uniqid((string)$id);
    if ($id) {
        Sequence::$suiteHash[$id] = $sequence;
    }

    return $sequence;
}
<?php

namespace Codeception\Step;

use Codeception\Lib\ModuleContainer;
use Codeception\Util\Template;

class AsJson extends Action implements GeneratedStep
{
    public function run(ModuleContainer $container = null)
    {
        $container->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');
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use ArrayAccess;
use Codeception\Exception\ConfigurationException;
use Codeception\Exception\ExternalUrlException;
use Codeception\Exception\ModuleConfigException;
use Codeception\Exception\ModuleException;
use Codeception\Lib\Framework;
use Codeception\Lib\InnerBrowser;
use Codeception\Lib\Interfaces\API;
use Codeception\Lib\Interfaces\ConflictsWithModule;
use Codeception\Lib\Interfaces\DependsOnModule;
use Codeception\Lib\Interfaces\PartedModule;
use Codeception\Module;
use Codeception\PHPUnit\Constraint\JsonContains;
use Codeception\PHPUnit\Constraint\JsonType as JsonTypeConstraint;
use Codeception\TestInterface;
use Codeception\Util\JsonArray;
use Codeception\Util\JsonType;
use Codeception\Util\Soap as XmlUtils;
use Codeception\Util\XmlStructure;
use Exception;
use JsonException;
use JsonSchema\Constraints\Constraint as JsonConstraint;
use JsonSchema\Validator as JsonSchemaValidator;
use JsonSerializable;
use PHPUnit\Framework\Assert;
use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\HttpKernel\HttpKernelBrowser;

/**
 * Module for testing REST WebService.
 *
 * This module requires either [PhpBrowser](https://codeception.com/docs/modules/PhpBrowser)
 * or a framework module (e.g. [Symfony](https://codeception.com/docs/modules/Symfony), [Laravel](https://codeception.com/docs/modules/Laravel5))
 * to send the actual HTTP request.
 *
 * ## Configuration
 *
 * * `url` *optional* - the url of api
 * * `shortDebugResponse` *optional* - number of chars to limit the API response length
 *
 * ### Example
 *
 * ```yaml
 * modules:
 *    enabled:
 *        - REST:
 *            depends: PhpBrowser
 *            url: 'https://example.com/api/v1/'
 *            shortDebugResponse: 300 # only the first 300 characters of the response
 * ```
 *
 * In case you need to configure low-level HTTP headers, that's done on the PhpBrowser level like so:
 *
 * ```yaml
 * modules:
 *    enabled:
 *        - REST:
 *            depends: PhpBrowser
 *            url: &url 'https://example.com/api/v1/'
 *    config:
 *        PhpBrowser:
 *            url: *url
 *            headers:
 *                Content-Type: application/json
 * ```
 *
 * ## JSONPath
 *
 * [JSONPath](https://goessner.net/articles/JsonPath/) is the equivalent to XPath, for querying JSON data structures.
 * Here's an [Online JSONPath Expressions Tester](https://jsonpath.curiousconcept.com/)
 *
 * ## Public Properties
 *
 * * headers - array of headers going to be sent.
 * * params - array of sent data
 * * response - last response (string)
 *
 * ## Parts
 *
 * * Json - actions for validating Json responses (no Xml responses)
 * * Xml - actions for validating XML responses (no Json responses)
 *
 * ## Conflicts
 *
 * Conflicts with SOAP module
 *
 */
class REST extends Module implements DependsOnModule, PartedModule, API, ConflictsWithModule
{
    /**
     * @var string[]
     */
    public const QUERY_PARAMS_AWARE_METHODS = ['GET', 'HEAD', 'OPTIONS'];

    /**
     * @var array<string, string>
     */
    protected array $config = [
        'url' => '',
        'aws' => ''
    ];

    protected string $dependencyMessage = <<<EOF
Example configuring PhpBrowser as backend for REST module.
--
modules:
    enabled:
        - REST:
            depends: PhpBrowser
            url: http://localhost/api/
            shortDebugResponse: 300
--
Framework modules can be used for testing of API as well.
EOF;

    protected int $DEFAULT_SHORTEN_VALUE = 150;

    /**
     * @var HttpKernelBrowser|AbstractBrowser
     */
    public $client;

    public bool $isFunctional = false;

    protected ?InnerBrowser $connectionModule = null;

    /** @var array */
    public $params = [];

    public ?string $response = null;

    public function _before(TestInterface $test): void
    {
        $this->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * //simple POST call
     * $response = $I->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. `<input type="text" name="form[task]">`),
     * // 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
     * <?php
     * $response = $I->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
     * <?php
     * $response = $I->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
     * <?php
     * $response = $I->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
     * <?php
     * $I->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
     * <?php
     * // response: {name: john, email: john@gmail.com}
     * $I->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
     * <?php
     * // response: {"name": "john", "age": 20}
     * $I->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
     * <?php
     * $user_id = $I->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
     * <?php
     * // match the first `user.id` in json
     * $firstUserId = $I->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
     * <?php
     * // at least one book in store has author
     * $I->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
     * <?php
     * // at least one book in store has author
     * $I->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
     * <?php
     * // at least one book in store has author
     * $I->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"
            . "- <info>" . var_export($json, true) . "</info>\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
     * <?php
     * // {'user_id': 1, 'name': 'davert', 'is_active': false}
     * $I->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
     * <?php
     * // {'user_id': 1, 'name': 'davert', 'company': {'name': 'Codegyre'}}
     * $I->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
     * <?php
     * // {'user_id': 1, 'email' => '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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->seeXmlResponseIncludes("<result>1</result>");
     * ```
     *
     * @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
     * <?php
     * $I->seeBinaryResponseEquals("df589122eac0f6a7bd8795436e692e3675cadc3b");
     * ```
     *
     * Example: Using sha1 for a file contents
     *
     * ```php
     * <?php
     * $fileData = file_get_contents("test_file.jpg");
     * $I->seeBinaryResponseEquals(md5($fileData));
     * ```
     * Example: Using sha256 hash
     *
     * ```php
     * <?php
     * $fileData = '/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k='; // very small jpeg
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->stopFollowingRedirects();
     * ```
     *
     * @part xml
     * @part json
     */
    public function stopFollowingRedirects(): void
    {
        $this->client->followRedirects(false);
    }

    /**
     * Enables automatic redirects to be followed by the client
     *
     * ```php
     * <?php
     * $I->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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\PHPUnit\Constraint;

use Codeception\Util\JsonArray;
use Codeception\Util\JsonType as JsonTypeUtil;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\ExpectationFailedException;

use function json_encode;

class JsonType extends Constraint
{
    /**
     * @var array
     */
    protected $jsonType;
    /**
     * @var bool
     */
    private $match;

    public function __construct(array $jsonType, bool $match = true)
    {
        $this->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 '';
    }
}
<?php

declare(strict_types=1);

namespace Codeception\PHPUnit\Constraint;

use Codeception\Util\JsonArray;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ArrayComparator;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory;

use function is_array;

class JsonContains extends Constraint
{
    /**
     * @var array
     */
    protected $expected;

    public function __construct(array $expected)
    {
        $this->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 '';
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

/**
 * JsonType matches JSON structures against templates.
 * You can specify the type of fields in JSON or add additional validation rules.
 *
 * JsonType is used by REST module in `seeResponseMatchesJsonType` and `dontSeeResponseMatchesJsonType` methods.
 *
 * Usage example:
 *
 * ```php
 * <?php
 * $jsonType = new JsonType(['name' => '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
     * <?php
     * JsonType::addCustomFilter('slug', function($value) {
     *     return strpos(' ', $value) !== false;
     * });
     * // => 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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use function array_intersect;
use function array_keys;
use function count;
use function is_array;
use function is_numeric;
use function min;
use function range;

class ArrayContainsComparator
{
    protected array $haystack;

    public function __construct(array $haystack)
    {
        $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use DOMDocument;
use DOMNode;
use DOMNodeList;
use DOMXPath;
use Exception;
use Flow\JSONPath\JSONPath;
use InvalidArgumentException;

class JsonArray
{
    protected array $jsonArray = [];

    protected ?DOMDocument $jsonXml = null;

    public function __construct(string $jsonString)
    {
        $jsonDecode = json_decode($jsonString, true);

        if (!is_array($jsonDecode)) {
            $jsonDecode = [$jsonDecode];
        }

        $this->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];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Exception\ModuleConfigException;
use Codeception\Lib\Driver\AmazonSQS;
use Codeception\Lib\Driver\Beanstalk;
use Codeception\Lib\Driver\Iron;
use Codeception\Lib\Driver\Pheanstalk4;
use Codeception\Module;
use Codeception\TestInterface;
use Pheanstalk\Contract\JobIdInterface as PheanstalkJobIdInterface;

/**
 * Works with Queue servers.
 *
 * Testing with a selection of remote/local queueing services, including Amazon's SQS service
 * Iron.io service and beanstalkd service.
 *
 * Supported and tested queue types are:
 *
 * * [Iron.io](https://www.iron.io/)
 * * [Beanstalkd](https://beanstalkd.github.io/)
 * * [Amazon SQS](https://aws.amazon.com/sqs/)
 *
 * The following dependencies are needed for the listed queue servers:
 *
 * * Beanstalkd: pda/pheanstalk ~3.0
 * * Amazon SQS: aws/aws-sdk-php
 * * IronMQ: iron-io/iron_mq
 *
 * ## Status
 *
 * * Stability:
 *     - Iron.io:    **stable**
 *     - Beanstalkd: **stable**
 *     - Amazon SQS: **stable**
 *
 * ## Config
 *
 * The configuration settings depending on which queueing service is being used, all the options are listed
 * here. Refer to the configuration examples below to identify the configuration options required for your chosen
 * service.
 *
 * * type - type of queueing server (defaults to beanstalkd).
 * * host - hostname/ip address of the queue server or the host for the iron.io when using iron.io service.
 * * port: 11300 - port number for the queue server.
 * * timeout: 90 - timeout settings for connecting the queue server.
 * * token - Iron.io access token.
 * * project - Iron.io project ID.
 * * key - AWS access key ID.
 * * version - AWS version (e.g. latest)
 * * endpoint - The full URI of the webservice. This is only required when connecting to a custom endpoint (e.g., a local version of SQS).
 * * secret - AWS secret access key.
 *      Warning:
 *          Hard-coding your credentials can be dangerous, because it is easy to accidentally commit your credentials
 *          into an SCM repository, potentially exposing your credentials to more people than intended.
 *          It can also make it difficult to rotate credentials in the future.
 * * profile - AWS credential profile
 *           - it should be located in ~/.aws/credentials file
 *           - eg:  [default]
 *                  aws_access_key_id = YOUR_AWS_ACCESS_KEY_ID
 *                  aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY
 *                  [project1]
 *                  aws_access_key_id = YOUR_AWS_ACCESS_KEY_ID
 *                  aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY
 *          - Note: Using IAM roles is the preferred technique for providing credentials
 *                  to applications running on Amazon EC2
 *                  https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials.html?highlight=credentials
 *
 * * region - A region parameter is also required for AWS, refer to the AWS documentation for possible values list.
 *
 * ### Example
 * #### Example (beanstalkd)
 *
 *     modules:
 *        enabled: [Queue]
 *        config:
 *           Queue:
 *              type: 'beanstalkd'
 *              host: '127.0.0.1'
 *              port: 11300
 *              timeout: 120
 *
 * #### Example (Iron.io)
 *
 *     modules:
 *        enabled: [Queue]
 *        config:
 *           Queue:
 *              'type': 'iron',
 *              'host': 'mq-aws-us-east-1.iron.io',
 *              'token': 'your-token',
 *              'project': 'your-project-id'
 *
 * #### Example (AWS SQS)
 *
 *     modules:
 *        enabled: [Queue]
 *        config:
 *           Queue:
 *              'type': 'aws',
 *              'key': 'your-public-key',
 *              'secret': 'your-secret-key',
 *              'region': 'us-west-2'
 *
 * #### Example AWS SQS using profile credentials
 *
 *     modules:
 *        enabled: [Queue]
 *        config:
 *           Queue:
 *              'type': 'aws',
 *              'profile': 'project1', //see documentation
 *              'region': 'us-west-2'
 *
 * #### Example AWS SQS running on Amazon EC2 instance
 *
 *     modules:
 *        enabled: [Queue]
 *        config:
 *           Queue:
 *              'type': 'aws',
 *              'region': 'us-west-2'
 */
class Queue extends Module
{
    public ?\Codeception\Lib\Interfaces\Queue $queueDriver = null;

    /**
     * Setup connection and open/setup the connection with config settings
     */
    public function _before(TestInterface $test): void
    {
        $this->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $queues = $I->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
     * <?php
     *     $I->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
     * <?php
     *     $I->grabQueueTotalCount('default');
     * ```
     *
     * @param  string $queue Queue name
     * @return int Count
     */
    public function grabQueueTotalCount(string $queue)
    {
        return $this->queueDriver->getMessagesTotalCountOnQueue($queue);
    }
}
<?php

namespace Codeception\Lib\Interfaces;

interface Queue
{
    /**
     * Connect to the queueing server.
     *
     * @param array<string, mixed> $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<string, mixed>
     */
    public function getDefaultConfig(): array;
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Lib\Interfaces\Queue;
use Pheanstalk\Exception\ConnectionException;
use Pheanstalk\Pheanstalk;
use PHPUnit\Framework\Assert;

class Beanstalk implements Queue
{
    protected ?Pheanstalk $queue = null;

    /**
     * @param array<string, mixed> $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<string, mixed>
     */
    public function getDefaultConfig(): array
    {
        return ['port' => 11300, 'timeout' => 90];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Lib\Interfaces\Queue;
use Pheanstalk\Contract\ResponseInterface;
use Pheanstalk\Pheanstalk;

class Pheanstalk4 implements Queue
{
    protected ?Pheanstalk $queue = null;

    /**
     * Connect to the queueing server.
     */
    public function openConnection(array $config): void
    {
        $this->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<string, mixed>
     */
    public function getDefaultConfig(): array
    {
        return ['port' => 11300, 'timeout' => 90, 'host' => 'localhost'];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Lib\Interfaces\Queue;
use Http_Exception;
use IronMQ;
use PHPUnit\Framework\Assert;

class Iron implements Queue
{
    protected ?IronMQ $queue = null;

    /**
     * Connect to the queueing server. (AWS, Iron.io and Beanstalkd)
     */
    public function openConnection(array $config): void
    {
        $this->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<string, mixed>
     */
    public function getDefaultConfig(): array
    {
        return [];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Aws\Credentials\Credentials;
use Aws\Sqs\SqsClient;
use Codeception\Exception\TestRuntimeException;
use Codeception\Lib\Interfaces\Queue;

class  AmazonSQS implements Queue
{
    protected ?SqsClient $queue = null;

    /**
     * Connect to the queueing server.
     */
    public function openConnection(array $config): void
    {
        $params = [
            'region' => $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<string, mixed>
     */
    public function getDefaultConfig(): array
    {
        return [];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Closure;
use Codeception\Lib\Connector\Guzzle;
use Codeception\Lib\InnerBrowser;
use Codeception\Lib\Interfaces\MultiSession;
use Codeception\Lib\Interfaces\Remote;
use Codeception\TestInterface;
use Codeception\Util\Uri;
use GuzzleHttp\Client as GuzzleClient;
use Symfony\Component\BrowserKit\AbstractBrowser;

/**
 * Uses [Guzzle](https://docs.guzzlephp.org/en/stable/) to interact with your application over CURL.
 * Module works over CURL and requires **PHP CURL extension** to be enabled.
 *
 * Use to perform web acceptance tests with non-javascript browser.
 *
 * If test fails stores last shown page in 'output' dir.
 *
 * ## Status
 *
 * * Maintainer: **davert**
 * * Stability: **stable**
 * * Contact: codeception@codeception.com
 *
 *
 * ## Configuration
 *
 * * url *required* - start url of your app
 * * headers - default headers are set before each test.
 * * handler (default: curl) -  Guzzle handler to use. By default curl is used, also possible to pass `stream`, or any valid class name as [Handler](https://docs.guzzlephp.org/en/latest/handlers-and-middleware.html#handlers).
 * * middleware - Guzzle middlewares to add. An array of valid callables is required.
 * * curl - curl options
 * * cookies - ...
 * * auth - ...
 * * verify - ...
 * * .. those and other [Guzzle Request options](https://docs.guzzlephp.org/en/latest/request-options.html)
 *
 *
 * ### Example (`acceptance.suite.yml`)
 *
 *     modules:
 *        enabled:
 *            - PhpBrowser:
 *                url: 'http://localhost'
 *                auth: ['admin', '123345']
 *                curl:
 *                    CURLOPT_RETURNTRANSFER: true
 *                cookies:
 *                    cookie-1:
 *                        Name: userName
 *                        Value: john.doe
 *                    cookie-2:
 *                        Name: authToken
 *                        Value: 1abcd2345
 *                        Domain: subdomain.domain.com
 *                        Path: /admin/
 *                        Expires: 1292177455
 *                        Secure: true
 *                        HttpOnly: false
 *
 *
 * All SSL certification checks are disabled by default.
 * Use Guzzle request options to configure certifications and others.
 *
 * ## Public API
 *
 * Those properties and methods are expected to be used in Helper classes:
 *
 * Properties:
 *
 * * `guzzle` - contains [Guzzle](https://guzzlephp.org/) client instance: `\GuzzleHttp\Client`
 * * `client` - Symfony BrowserKit instance.
 *
 */
class PhpBrowser extends InnerBrowser implements Remote, MultiSession
{
    /**
     * @var string[]
     */
    protected array $requiredFields = ['url'];

    /**
     * @var array<string, mixed>
     */
    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
     * <?php
     * $I->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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Connector;

use Aws\Credentials\Credentials as AwsCredentials;
use Aws\Signature\SignatureV4 as AwsSignatureV4;
use Codeception\Util\Uri;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Cookie\CookieJar as GuzzleCookieJar;
use GuzzleHttp\Cookie\SetCookie;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Handler\StreamHandler;
use GuzzleHttp\HandlerStack as GuzzleHandlerStack;
use GuzzleHttp\Psr7\Request as Psr7Request;
use GuzzleHttp\Psr7\Response as Psr7Response;
use GuzzleHttp\Psr7\Uri as Psr7Uri;
use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;

class Guzzle extends AbstractBrowser
{
    protected array $requestOptions = [
        'allow_redirects' => 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('#<meta[^>]+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(
                '#<meta[^>]+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<string, mixed>
     */
    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']);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\TestInterface;
use Codeception\Exception\ModuleConfigException;
use Memcached;

/**
 * Connects to [memcached](http://www.memcached.org/) using either _Memcache_ or _Memcached_ extension.
 *
 * Performs a cleanup by flushing all values after each test run.
 *
 * ## Status
 *
 * * Maintainer: **davert**
 * * Stability: **beta**
 * * Contact: davert@codeception.com
 *
 * ## Configuration
 *
 * * **`host`** (`string`, default `'localhost'`) - The memcached host
 * * **`port`** (`int`, default `11211`) - The memcached port
 *
 * ### Example (`unit.suite.yml`)
 *
 * ```yaml
 *    modules:
 *        - Memcache:
 *            host: 'localhost'
 *            port: 11211
 * ```
 *
 * Be sure you don't use the production server to connect.
 *
 * ## Public Properties
 *
 * * **memcache** - instance of _Memcache_ or _Memcached_ object
 */
class Memcache extends Module
{
    public \Memcache|Memcached|null $memcache = null;

    /**
     * @var array<string, string|integer>
     */
    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
     * <?php
     * $users_count = $I->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
     * <?php
     * // With only one argument, only checks the key exists
     * $I->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
     * <?php
     * // With only one argument, only checks the key does not exist
     * $I->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();
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib;

/**
 * Abstract module for PHP frameworks connected via Symfony BrowserKit components
 * Each framework is connected with it's own connector defined in \Codeception\Lib\Connector
 * Each module for framework should extend this class.
 */
abstract class Framework extends InnerBrowser
{
    /**
     * Returns a list of recognized domain names
     */
    protected function getInternalDomains(): array
    {
        return [];
    }

    public function _beforeSuite($settings = [])
    {
        /**
         * reset internal domains before suite, because each suite can have a different configuration
         */
        $this->internalDomains = null;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib;

use Codeception\Constraint\Crawler as CrawlerConstraint;
use Codeception\Constraint\CrawlerNot as CrawlerNotConstraint;
use Codeception\Constraint\Page as PageConstraint;
use Codeception\Exception\ElementNotFound;
use Codeception\Exception\ExternalUrlException;
use Codeception\Exception\MalformedLocatorException;
use Codeception\Exception\ModuleException;
use Codeception\Exception\TestRuntimeException;
use Codeception\Lib\Interfaces\ConflictsWithModule;
use Codeception\Lib\Interfaces\ElementLocator;
use Codeception\Lib\Interfaces\PageSourceSaver;
use Codeception\Lib\Interfaces\Web;
use Codeception\Module;
use Codeception\Test\Descriptor;
use Codeception\TestInterface;
use Codeception\Util\HttpCode;
use Codeception\Util\Locator;
use Codeception\Util\ReflectionHelper;
use Codeception\Util\Uri;
use DOMDocument;
use DOMNode;
use InvalidArgumentException;
use LogicException;
use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\BrowserKit\Exception\BadMethodCallException;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\DomCrawler\Crawler as SymfonyCrawler;
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
use Symfony\Component\DomCrawler\Field\FileFormField;
use Symfony\Component\DomCrawler\Field\FormField;
use Symfony\Component\DomCrawler\Field\InputFormField;
use Symfony\Component\DomCrawler\Field\TextareaFormField;
use Symfony\Component\DomCrawler\Form as SymfonyForm;
use Symfony\Component\DomCrawler\Link;

class InnerBrowser extends Module implements Web, PageSourceSaver, ElementLocator, ConflictsWithModule
{
    protected ?SymfonyCrawler $crawler = null;

    /**
     * @api
     */
    public ?AbstractBrowser $client = null;

    /**
     * @var SymfonyForm[]
     */
    protected array $forms = [];

    /**
     * @var string[]
     */
    public array $headers = [];

    /**
     * @var array<string, string>|array<string, bool>|array<string, null>
     */
    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
     * <?php
     * // in Helper class
     * public function createUserByApi($name) {
     *     $userData = $this->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
     * <?php
     * // in Helper class
     * public function seeResponseContains($text)
     * {
     *    $this->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
     * <?php
     * // in Helper class
     * public function openCheckoutFormStep2($orderId) {
     *     $this->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
     * <?php
     * $I->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&#x0005F;Id' or 'Client&#95;Id'
     *
     * ```php
     * <?php
     * $I->haveHttpHeader('Client&#95;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
     * <?php
     * $I->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
     * <?php
     * $I->sendAjaxPostRequest('/add-task', ['task' => 'lorem ipsum']);
     * ```
     * Some frameworks (e.g. Symfony) create field names in the form of an "array":
     * `<input type="text" name="form[task]">`
     * In this case you need to pass the fields like this:
     * ``` php
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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("<title>", "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 href="https://codeception.com">Codeception</a> and <a href="https://phpunit.de/">PHPUnit %s</a>',
                    PHPUnitVersion::id()
                )
            );
        } else {
            $writer = new HtmlFacadeReport(
                sprintf(
                    ', <a href="https://codeception.com">Codeception</a> and <a href="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</title>
	<meta charset='utf-8'>
	<link href='https://fonts.googleapis.com/css?family=Varela+Round&v2' rel='stylesheet' type='text/css'>
     <meta name="viewport" content="width=device-width, initial-scale=1">
	 <style>
		 .layout {
		 margin: 0 auto;
		 max-width: 1000px;

		 }
		 body { font-family: arial, serif; margin: 0; padding: 0; background: #ecf0f1; font-size: 20px; }
		 h1,h2,h3 { font-family: arial, serif; color: #7f8c8d; }
		 h1 { font-size: 2.5em; }
		 h2 { font-size: 1.3em; }
         h3 { font-size: 1em; color: #84BBDD; margin: 0.5em 0; }

		 table { border: none; margin: 0; padding: 0; font-size: 0.9em;}
		 .scenarioStepsTable .stepName { padding: 5px; }

		 .scenarioStepsTable td {
            background: #fff;
		 }

         .quiet {
             color: #333;
             font-size: 0.8em;
         }

         .screenshot {
             max-height: 400px;
             overflow-y: scroll;
             display: block;
         }
         .screenshot img {
             zoom: 0.5;
         }

         @media (max-width: 1200px) {
           #toolbar-filter {
             display: none !important;
           }
         }

         .scenarioStepsTable .nostyle {
             background: none;
             border: none;
         }

		 p {
		    cursor: pointer;
		 }

         .scenarioRow>td>p {
             padding: 5px;
         }

         .scenarioStepsTable .failedStep {
             padding: 10px;
             background: #ecf0f1;
             border: 2px solid #e74c3c;
             border-radius: 0px;
             color:  #e74c3c;
         }

         .scenarioStepsTable .error {
             background:  #999;
             padding: 10px;
             color: #fff;
             border-radius: 0px;
         }

         .scenarioStepsTable .error a {
             color: #eef;
         }

         .scenarioStepsTable.substeps td {
             background: #bdc3c7;
         }

		 .header { font-size: large; font-weight: bold; }
		 p.scenarioSuccess {
    		 background: rgb(157,213,58); /* Old browsers */
		 }


		 .scenario { color: black; }
		 p.scenarioFailed, p.scenarioError { color: black;
		   background: #e74c3c
		 }

		 table.scenarioFailed tr:last-child { font-weight: bold; }

		 td.scenarioSuccess { color: green }
		 td.scenarioFailed { color: red }
		 .scenarioSkipped { color: teal; }
		 .scenarioIncomplete { color: gray; }
		 .scenarioUseless { color: orange; }
		 .scenarioStepsTable { margin-left: 10px; display: none; color: #333; }

		 #stepContainerSummary {
		 background: white;
		 -webkit-border-radius: 5px;
		 -moz-border-radius: 5px;
		 border-radius: 5px;
		 padding: 20px;
		 }

         .toggle {
             background: rgba(255,255,255,0.5);
             border-radius: 10px;
             display: inline-block;
             width: 20px;
             height: 20px;
             text-align: center;
             margin: auto;
             color: #666
         }

		 ul#toolbar-filter {
            display: block;
            position: fixed;
            top: 20px;
            left: 0px;
            padding: 0px;
		 }
		 ul#toolbar-filter li {
		    list-style: none;
		    text-align: center;
			padding: 20px;
			background-color: #3498db;
		 }
		 ul#toolbar-filter li a, ul#toolbar-filter li a:hover, ul#toolbar-filter li a:visited {
		    color: #34495e;
		    text-decoration: none;
		 }
		 ul#toolbar-filter li.disabled {
			background-color: #bdc3c7;
		 }
	 </style>

  <script type="text/javascript">
  var showAll = true;
  function showHide(nodeId, linkObj)
  {
    var subObj = document.getElementById('stepContainer' + nodeId);
    var toggle = linkObj.childNodes[0];
    if (toggle.innerHTML != '-') {
      toggle.innerHTML = '-';
      subObj.style.display='block';
      subObj.style.width = '100%';
    } else {
      toggle.innerHTML = '+';
      subObj.style.display='none';
    }
  }

  function showAllScenarios() {
    var toolbar = document.getElementById('toolbar-filter');
    for (var i = 0; i < toolbar.children.length; i++) {
      toolbar.children[i].className = '';
    }

    var trs = document.getElementsByTagName('tr');
    for(var z = 0; z < trs.length; z++) {
      trs[z].style.display = '';
    }
    showAll = true;
  }

  function toggleScenarios(name, linkObj) {
    var links = document.getElementById('toolbar-filter').children;
    var rows = document.getElementsByClassName('scenarioRow');
    if (showAll) {
        for (var i = 0; i < links.length; i++) {
            links[i].className = 'disabled';
        }

        for (var i = 0; i < rows.length; i++) {
            rows[i].style.display = 'none';
        }

    }
    showAll = false;

    if (linkObj.className == '') {
        linkObj.className = 'disabled';
        for (var i = 0; i < rows.length; i++) {
          if (rows[i].classList.contains(name)) {
                rows[i].style.display = 'none';
          }
        }
        return;
    }
    if (linkObj.className == 'disabled') {
        linkObj.className = '';
        for (var i = 0; i < rows.length; i++) {
          if (rows[i].classList.contains(name)) {
                rows[i].style.display = 'table-row';
          }
        }
        return;
    }

  }
  </script>
 </head>

 <body>
   <ul id="toolbar-filter">
     <li> <a href="#" title="Show all" onClick="showAllScenarios()">◯</a></li>
     <li> <a href="#" title="Successful" onClick="toggleScenarios('scenarioSuccess', this.parentElement)"><strong>✔</strong> {successfulScenarios}</a></li>
     <li> <a href="#" title="Failed" onClick="toggleScenarios('scenarioFailed', this.parentElement)"><strong>✗</strong> {failedScenarios}</a></li>
     <li> <a href="#" title="Skipped" onClick="toggleScenarios('scenarioSkipped', this.parentElement)"><strong>S</strong> {skippedScenarios}</a></li>
     <li> <a href="#" title="Incomplete" onClick="toggleScenarios('scenarioIncomplete', this.parentElement)"><strong>I</strong> {incompleteScenarios}</a></li>
     <li> <a href="#" title="Useless" onClick="toggleScenarios('scenarioUseless', this.parentElement)"><strong>U</strong> {uselessScenarios}</a></li>
   </ul>
 <div class="layout">
 {header}

	 <table border="0" style="width: 100%;">
		{scenarios}
   <tr>
    <td>
	    <h2>Summary</h2>
     <div id="stepContainerSummary">
      <table border="0">
       <tr>
        <td width="250" class="scenarioSuccess">Successful scenarios:</td>
        <td class="scenarioSuccessValue"><strong>{successfulScenarios}</strong></td>
       </tr>
       <tr>
        <td class="scenarioFailed">Failed scenarios:</td>
        <td class="scenarioFailedValue"><strong>{failedScenarios}</strong></td>
       </tr>
       <tr>
        <td class="scenarioSkipped">Skipped scenarios:</td>
        <td class="scenarioSkippedValue"><strong>{skippedScenarios}</strong></td>
       </tr>
       <tr>
        <td class="scenarioIncomplete">Incomplete scenarios:</td>
        <td class="scenarioIncompleteValue"><strong>{incompleteScenarios}</strong></td>
       </tr>
       <tr>
        <td class="scenarioUseless">Useless scenarios:</td>
        <td class="scenarioUselessValue"><strong>{uselessScenarios}</strong></td>
       </tr>
      </table>
     </div>
    </td>
   </tr>
  </table>
 </div>
 </body>
</html>
      <tr>
       <td class="stepName {error}">&nbsp;&nbsp;&nbsp;&nbsp;{action}</td>
      </tr>

<tr>
 <td>
     <h3>{suite} Tests</h3>
 </td>
</tr><h1>{name} <small>{status} ({time})</small></h1>

<?php

namespace Codeception\Reporter;

use Codeception\Event\FailEvent;
use Codeception\Event\PrintResultEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Lib\Console\Message;
use Codeception\Lib\Console\Output;
use Codeception\Lib\Interfaces\ConsolePrinter;
use Codeception\Subscriber\Shared\StaticEventsTrait;
use Codeception\Test\Descriptor;
use Codeception\Test\Test;

class ReportPrinter implements ConsolePrinter
{
    use StaticEventsTrait;

    /**
     * @var array<string, string>
     */
    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) . '.');
    }
}
<?php

namespace Codeception\Reporter;

use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Test\Interfaces\Reported;
use ReflectionClass;

class PhpUnitReporter extends JUnitReporter
{
    public const SUITE_LEVEL = 1;
    public const FILE_LEVEL  = 2;

    protected string $reportFileParam = 'phpunit-xml';
    protected string $reportName = 'PHPUNIT XML';

    private ?string $currentFile = null;

    public function startTest(TestEvent $event): void
    {
        $test = $event->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);
    }
}
<?php

namespace Codeception\Reporter;

use Codeception\Event\FailEvent;
use Codeception\Event\PrintResultEvent;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Lib\Console\Output;
use Codeception\Subscriber\Shared\StaticEventsTrait;
use Codeception\Test\Test;
use Codeception\Test\TestCaseWrapper;
use Codeception\Util\StackTraceFilter;
use DOMDocument;
use DOMElement;
use InvalidArgumentException;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Runner\Version as PHPUnitVersion;
use PHPUnit\Util\ThrowableToStringMapper;
use PHPUnit\Util\Xml;
use ReflectionException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Throwable;

use function sprintf;
use function str_replace;

class JUnitReporter 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::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(
            "- <bold>%s</bold> report generated in <comment>file://%s</comment>",
            $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);
    }
}
<?php

namespace Codeception\Reporter;

use Codeception\Event\FailEvent;
use Codeception\Event\PrintResultEvent;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Lib\Console\Output;
use Codeception\Step;
use Codeception\Step\Meta;
use Codeception\Subscriber\Shared\StaticEventsTrait;
use Codeception\Test\Descriptor;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\Test\Test;
use Codeception\TestInterface;
use Codeception\Util\PathResolver;
use SebastianBergmann\Template\Template;
use SebastianBergmann\Timer\Timer;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

use function trigger_error;

class HtmlReporter implements EventSubscriberInterface
{
    use StaticEventsTrait;

    /**
     * @var array<string, string>
     */
    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 = "<tr><td class='error'><div class='screenshot'><img src='$localPath' alt='failure screenshot'></div></td></tr>";
            }
            if (isset($reports['html'])) {
                $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir());
                $html = "<tr><td class='error'>See <a href='$localPath' target='_blank'>HTML snapshot</a> of a failed page</td></tr>";
            }
        }

        $toggle = $stepsBuffer ? '<span class="toggle">+</span>' : '';

        $testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test)), ENT_QUOTES | ENT_SUBSTITUTE);
        $testString = preg_replace('~^([\s\w\\\]+):\s~', '<span class="quiet">$1 &raquo;</span> ', $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 = "<tr><td class='error'><div class='screenshot'><img src='$localPath' alt='failure screenshot'></div></td></tr>";
            }
            if (isset($reports['html'])) {
                $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir());
                $html = "<tr><td class='error'>See <a href='$localPath' target='_blank'>HTML snapshot</a> of a failed page</td></tr>";
            }
        }

        $toggle = $stepsBuffer ? '<span class="toggle">+</span>' : '';

        $testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test)), ENT_QUOTES | ENT_SUBSTITUTE);
        $testString = preg_replace('~^([\s\w\\\]+):\s~', '<span class="quiet">$1 &raquo;</span> ', $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()
            ? '<span style="color: green">OK</span>'
            : '<span style="color: #e74c3c">FAILED</span>';

        $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(
            "- <bold>HTML</bold> report generated in <comment>file://%s</comment>",
            $this->reportFile
        )->writeln();
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Test\Metadata;
use PHPUnit\Framework\Test;

interface TestInterface extends Test
{
    public function getMetadata(): Metadata;

    public function getResultAggregator(): ResultAggregator;
}
<?php

declare(strict_types=1);

namespace Codeception;

use Closure;
use Codeception\Lib\Actor\Shared\Comment;
use Codeception\Lib\Actor\Shared\Pause;
use Codeception\Step\Executor;
use RuntimeException;

abstract class Actor
{
    use Comment;
    use Pause;

    public function __construct(protected Scenario $scenario)
    {
    }

    protected function getScenario(): Scenario
    {
        return $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Command\Shared\ActorTrait;
use Codeception\Lib\Di;
use Codeception\Lib\GroupManager;
use Codeception\Lib\ModuleContainer;
use Codeception\Lib\Notification;
use Codeception\Test\Descriptor;
use Codeception\Test\Filter;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\Test\Loader;
use Codeception\Test\Test;
use Codeception\Test\TestCaseWrapper;
use Codeception\Test\Unit;
use PHPUnit\Runner\Version as PHPUnitVersion;
use Symfony\Component\EventDispatcher\EventDispatcher;

class SuiteManager
{
    use ActorTrait;

    protected ?Suite $suite = null;

    protected ?EventDispatcher $dispatcher = null;

    protected GroupManager $groupManager;

    protected ModuleContainer $moduleContainer;

    protected Di $di;

    protected string $env = '';

    protected array $settings = [];

    private Filter $testFilter;

    public function __construct(EventDispatcher $dispatcher, string $name, array $settings, array $options)
    {
        $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();
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Event\FailEvent;
use Codeception\Event\StepEvent;
use Codeception\Exception\ConditionalAssertionFailed;
use Codeception\Exception\InjectionException;
use Codeception\Step\Comment;
use Codeception\Step\Meta;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\Test\Metadata;
use PHPUnit\Framework\IncompleteTestError;
use PHPUnit\Framework\SkippedTestError;
use PHPUnit\Framework\SkippedWithMessageException;
use PHPUnit\Runner\Version as PHPUnitVersion;

class Scenario
{
    protected TestInterface $test;

    protected Metadata $metadata;

    /**
     * @var Step[]
     */
    protected array $steps = [];

    protected string $feature;

    protected ?Meta $metaStep = null;

    public function __construct(TestInterface $test)
    {
        $this->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() . '<br/>';
            } else {
                $text .= trim($step->getHumanizedArguments(), '"') . '<br/>';
            }
        }
        $text = str_replace(['"\'', '\'"'], ["'", "'"], $text);
        return "<h3>" . mb_strtoupper('I want to ' . $this->getFeature(), 'utf-8') . "</h3>" . $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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Event\TestEvent;

abstract class GroupObject extends Extension
{
    public static $group;

    public function _before(TestEvent $event)
    {
    }

    public function _after(TestEvent $event)
    {
    }

    public static function getSubscribedEvents(): array
    {
        $inheritedEvents = parent::getSubscribedEvents();
        $events = [];
        if (static::$group) {
            $events = [
                Events::TEST_BEFORE . '.' . static::$group => '_before',
                Events::TEST_AFTER . '.' . static::$group => '_after',
            ];
        }
        return array_merge($events, $inheritedEvents);
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Exception\ConfigurationException;
use Exception;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Input\ArgvInput as SymfonyArgvInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;

class Application extends BaseApplication
{
    protected ?SymfonyArgvInput $coreArguments = null;

    /**
     * Register commands from config file
     *
     *  extensions:
     *      commands:
     *          - Project\Command\MyCustomCommand
     */
    public function registerCustomCommands(): void
    {
        try {
            $this->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;
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Exception\ConfigurationException;
use Codeception\Lib\ParamsLoader;
use Codeception\Step\ConditionalAssertion;
use Codeception\Util\Autoload;
use Codeception\Util\PathResolver;
use Codeception\Util\Template;
use Exception;
use InvalidArgumentException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;

use function array_unique;

class Configuration
{
    /**
     * @var string[]
     */
    protected static array $suites = [];

    /**
     * @var array<string, mixed>|null Current configuration
     */
    protected static ?array $config = null;

    /**
     * @var array<mixed> 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<string, mixed>
     */
    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<string, mixed>
     */
    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<string, mixed>|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<string, mixed>
     * @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<mixed> $config
     * @return array<string, string>
     * @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<string, mixed>
     */
    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<string, mixed>
     * @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<string, mixed> $nonExistentValue Value used if filename is not found
     * @return array<string, mixed>
     * @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<string, mixed> $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<string, mixed> $config
     * @return array<string, mixed>
     */
    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<mixed> $a1
     * @param array<mixed> $a2
     * @return array<mixed>
     */
    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<string ,mixed> $settings
     * @return array<string ,mixed>
     * @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<string, mixed> $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));
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\Snapshot as SnapshotGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function ucfirst;

/**
 * Generates Snapshot.
 * Snapshot can be used to test dynamical data.
 * If suite name is provided, an actor class will be included into placeholder
 *
 * * `codecept g:snapshot UserEmails`
 * * `codecept g:snapshot Products`
 * * `codecept g:snapshot acceptance UserEmails`
 */
class GenerateSnapshot extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>Snapshot {$filename} already exists</error>");
            return 1;
        }
        $output->writeln("<info>Snapshot was created in {$filename}</info>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Exception\ConfigurationException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Generates empty environment configuration file into envs dir:
 *
 *  * `codecept g:env firefox`
 *
 * Required to have `envs` path to be specified in `codeception.yml`
 */
class GenerateEnvironment extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<info>{$env} config was created in {$relativePath}/{$file}</info>");
            return 0;
        } else {
            $output->writeln("<error>File {$relativePath}/{$file} already exists</error>");
            return 1;
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Lib\Generator\Actions;
use Codeception\Lib\ModuleContainer;
use Codeception\Stub;
use Codeception\Subscriber\Bootstrap as BootstrapLoader;
use Codeception\Subscriber\Console as ConsolePrinter;
use Codeception\SuiteManager;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\Test\Test;
use Exception;
use InvalidArgumentException;
use ReflectionIntersectionType;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionUnionType;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

use function ini_set;
use function preg_match;
use function str_replace;

/**
 * Shows step by step execution process for scenario driven tests without actually running them.
 *
 * * `codecept dry-run acceptance`
 * * `codecept dry-run acceptance MyCest`
 * * `codecept dry-run acceptance checkout.feature`
 * * `codecept dry-run tests/acceptance/MyCest.php`
 *
 */
class DryRun extends Command
{
    use Shared\ConfigTrait;
    use Shared\StyleTrait;

    protected function configure(): void
    {
        $this->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("<warning> SKIPPED </warning>" . $skip);
            }
            if ($incomplete = $test->getMetadata()->getIncomplete()) {
                $output->writeln("<warning> INCOMPLETE </warning>" . $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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Template\Bootstrap as BootstrapTemplate;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Creates default config, tests directory and sample suites for current project.
 * Use this command to start building a test suite.
 *
 * By default it will create 3 suites **Acceptance**, **Functional**, and **Unit**.
 *
 * * `codecept bootstrap` - creates `tests` dir and `codeception.yml` in current dir.
 * * `codecept bootstrap --empty` - creates `tests` dir without suites
 * * `codecept bootstrap --namespace Frontend` - creates tests, and use `Frontend` namespace for actor classes and helpers.
 * * `codecept bootstrap --actor Wizard` - sets actor as Wizard, to have `TestWizard` actor in tests.
 * * `codecept bootstrap path/to/the/project` - provide different path to a project, where tests should be placed
 *
 */
class Bootstrap extends Command
{
    protected function configure(): void
    {
        $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Lib\Generator\GherkinSnippets as GherkinSnippetsGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function count;

/**
 * Generates code snippets for matched feature files in a suite.
 * Code snippets are expected to be implemented in Actor or PageObjects
 *
 * Usage:
 *
 * * `codecept gherkin:snippets acceptance` - snippets from all feature of acceptance tests
 * * `codecept gherkin:snippets acceptance/feature/users` - snippets from `feature/users` dir of acceptance tests
 * * `codecept gherkin:snippets acceptance user_account.feature` - snippets from a single feature file
 * * `codecept gherkin:snippets acceptance/feature/users/user_accout.feature` - snippets from feature file in a dir
 */
class GherkinSnippets extends Command
{
    use Shared\ConfigTrait;
    use Shared\StyleTrait;

    protected function configure(): void
    {
        $this->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("<notice> All Gherkin steps are defined. Exiting... </notice>");
            return 0;
        }
        $output->writeln("<comment> Snippets found in: </comment>");

        $features = $generator->getFeatures();
        foreach ($features as $feature) {
            $output->writeln("<info>  - {$feature} </info>");
        }
        $output->writeln("<comment> Generated Snippets: </comment>");
        $output->writeln("<info> ----------------------------------------- </info>");
        foreach ($snippets as $snippet) {
            $output->writeln($snippet);
        }
        $output->writeln("<info> ----------------------------------------- </info>");
        $output->writeln(sprintf(' <bold>%d</bold> snippets proposed', count($snippets)));
        $output->writeln("<notice> Copy generated snippets to {$config['actor']} or a specific Gherkin context </notice>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\PageObject as PageObjectGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function ucfirst;

/**
 * Generates PageObject. Can be generated either globally, or just for one suite.
 * If PageObject is generated globally it will act as UIMap, without any logic in it.
 *
 * * `codecept g:page Login`
 * * `codecept g:page Registration`
 * * `codecept g:page acceptance Login`
 */
class GeneratePageObject extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>PageObject {$filename} already exists</error>");
            return 1;
        }
        $output->writeln("<info>PageObject was created in {$filename}</info>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Util\FileSystem;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Recursively cleans `output` directory and generated code.
 *
 * * `codecept clean`
 *
 */
class Clean extends Command
{
    use Shared\ConfigTrait;

    public function getDescription(): string
    {
        return 'Recursively cleans log and generated code';
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $projectDir = Configuration::projectDir();
        $this->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("<info>Cleaning up output " . $logDir . "...</info>");
        FileSystem::doEmptyDir($logDir);

        $subProjects = $config['include'];
        foreach ($subProjects as $subProject) {
            $subProjectDir = $projectDir . $subProject;
            $this->cleanProjectsRecursively($output, $subProjectDir);
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Codecept;
use Codeception\Configuration;
use Codeception\Exception\ConfigurationException;
use Codeception\Exception\ParseException;
use Exception;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException as SymfonyConsoleInvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function array_flip;
use function array_intersect_key;
use function array_merge;
use function count;
use function explode;
use function extension_loaded;
use function getcwd;
use function implode;
use function in_array;
use function preg_match;
use function preg_replace;
use function rtrim;
use function sprintf;
use function str_contains;
use function str_replace;
use function str_starts_with;
use function strpos;
use function strtolower;
use function substr;
use function substr_replace;

/**
 * Executes tests.
 *
 * Usage:
 *
 * * `codecept run acceptance`: run all acceptance tests
 * * `codecept run tests/acceptance/MyCest.php`: run only MyCest
 * * `codecept run acceptance MyCest`: same as above
 * * `codecept run acceptance MyCest:myTestInIt`: run one test from a Cest
 * * `codecept run acceptance MyCest:myTestInIt#1`: run one example or data provider item by number
 * * `codecept run acceptance MyCest:myTestInIt#1-3`: run a range of examples or data provider items
 * * `codecept run acceptance MyCest:myTestInIt@name.*`: run data provider items with matching names
 * * `codecept run acceptance checkout.feature`: run feature-file
 * * `codecept run acceptance -g slow`: run tests from *slow* group
 * * `codecept run unit,functional`: run only unit and functional suites
 *
 * Verbosity modes:
 *
 * * `codecept run -v`:
 * * `codecept run --steps`: print step-by-step execution
 * * `codecept run -vv`: print steps and debug information
 * * `codecept run --debug`: alias for `-vv`
 * * `codecept run -vvv`: print Codeception-internal debug information
 *
 * Load config:
 *
 * * `codecept run -c path/to/another/config`: from another dir
 * * `codecept run -c another_config.yml`: from another config file
 *
 * Override config values:
 *
 * * `codecept run -o "settings: shuffle: true"`: enable shuffle
 * * `codecept run -o "settings: lint: false"`: disable linting
 *
 * Run with specific extension
 *
 * * `codecept run --ext Recorder` run with Recorder extension enabled
 * * `codecept run --ext DotReporter` run with DotReporter printer
 * * `codecept run --ext "My\Custom\Extension"` run with an extension loaded by class name
 *
 * Full reference:
 * ```
 * Arguments:
 *  suite                 suite to be tested
 *  test                  test to be run
 *
 * Options:
 *  -o, --override=OVERRIDE Override config values (multiple values allowed)
 *  --config (-c)         Use custom path for config
 *  --report              Show output in compact style
 *  --html                Generate html with results (default: "report.html")
 *  --xml                 Generate JUnit XML Log (default: "report.xml")
 *  --phpunit-xml         Generate PhpUnit XML Log (default: "phpunit-report.xml")
 *  --no-redirect         Do not redirect to Composer-installed version in vendor/codeception
 *  --colors              Use colors in output
 *  --no-colors           Force no colors in output (useful to override config file)
 *  --silent              Only outputs suite names and final results. Almost the same as `--quiet`
 *  --steps               Show steps in output
 *  --debug (-d)          Alias for `-vv`
 *  --bootstrap           Execute bootstrap script before the test
 *  --coverage            Run with code coverage (default: "coverage.serialized")
 *  --coverage-html       Generate CodeCoverage HTML report in path (default: "coverage")
 *  --coverage-xml        Generate CodeCoverage XML report in file (default: "coverage.xml")
 *  --coverage-text       Generate CodeCoverage text report in file (default: "coverage.txt")
 *  --coverage-phpunit    Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit")
 *  --coverage-cobertura  Generate CodeCoverage Cobertura report in file (default: "coverage-cobertura")
 *  --no-exit             Don't finish with exit code
 *  --group (-g)          Groups of tests to be executed (multiple values allowed)
 *  --skip (-s)           Skip selected suites (multiple values allowed)
 *  --skip-group (-x)     Skip selected groups (multiple values allowed)
 *  --env                 Run tests in selected environments. (multiple values allowed, environments can be merged with ',')
 *  --fail-fast (-f)      Stop after nth failure (defaults to 1)
 *  --no-rebuild          Do not rebuild actor classes on start
 *  --help (-h)           Display this help message.
 *  --quiet (-q)          Do not output any message. Almost the same as `--silent`
 *  --verbose (-v|vv|vvv) Increase the verbosity of messages: `v` for normal output, `vv` for steps and debug, `vvv` for Codeception-internal debug
 *  --version (-V)        Display this application version.
 *  --ansi                Force ANSI output.
 *  --no-ansi             Disable ANSI output.
 *  --no-interaction (-n) Do not ask any interactive question.
 *  --seed                Use the given seed for shuffling tests
 * ```
 *
 */
class Run extends Command
{
    use Shared\ConfigTrait;

    protected ?Codecept $codecept = null;

    /**
     * @var int Executed suites
     */
    protected int $executed = 0;

    protected array $options = [];

    protected ?OutputInterface $output = null;

    /**
     * Sets Run arguments
     *
     * @throws SymfonyConsoleInvalidArgumentException
     */
    protected function configure(): void
    {
        $this->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: <info>" . $this->options['seed'] . "</info>\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] <info>%s</info> ", 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']}] <info>Running subset of tests</info>"
            );
        }

        if (!$this->options['silent'] && $config['settings']['shuffle']) {
            $this->output->writeln(
                "[Seed] <info>" . $userOptions['seed'] . "</info>"
            );
        }

        $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<string,string> $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('<error>Wildcard options can not be combined with specific suites of included apps.</error>');
                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']}] <info>Merge this result with other shards to see the complete report</info>"
            );
        }

        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<string,string[]> $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<fg=white;bg=magenta>\n[{$namespace}]: tests from {$currentDir}\n</fg=white;bg=magenta>"
            );

            $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<string, bool>
     */
    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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\Group as GroupGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function ucfirst;

/**
 * Creates empty GroupObject - extension which handles all group events.
 *
 * * `codecept g:group Admin`
 */
class GenerateGroup extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>Group {$filename} already exists</error>");
            return 1;
        }

        $output->writeln("<info>Group extension was created in {$filename}</info>");
        $output->writeln(
            'To use this group extension, include it to "extensions" option of global Codeception config.'
        );
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Lib\Generator\Cest as CestGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function file_exists;

/**
 * Generates Cest (scenario-driven object-oriented test) file:
 *
 * * `codecept generate:cest suite Login`
 * * `codecept g:cest suite subdir/subdir/testnameCest.php`
 * * `codecept g:cest suite LoginCest -c path/to/project`
 * * `codecept g:cest "App\Login"`
 *
 */
class GenerateCest extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>Test {$filename} already exists</error>");
            return 1;
        }
        $cest = new CestGenerator($class, $config);
        $res = $this->createFile($filename, $cest->produce());
        if (!$res) {
            $output->writeln("<error>Test {$filename} already exists</error>");
            return 1;
        }

        $output->writeln("<info>Test was created in {$filename}</info>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Test\Loader\Gherkin as GherkinLoader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function count;

/**
 * Prints all steps from all Gherkin contexts for a specific suite
 *
 * ```
 * codecept gherkin:steps acceptance
 * ```
 *
 */
class GherkinSteps extends Command
{
    use Shared\ConfigTrait;
    use Shared\StyleTrait;

    protected function configure(): void
    {
        $this->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 <bold>{$name}</bold> 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 <bold>gherkin:snippets</bold>");
        }
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Exception\ConfigurationException;
use Codeception\SuiteManager;
use Codeception\Test\Cest;
use Codeception\Test\Interfaces\Descriptive;
use Codeception\Test\Interfaces\ScenarioDriven;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

use function basename;
use function file_exists;
use function is_writable;
use function mkdir;
use function preg_replace;

/**
 * Generates user-friendly text scenarios from scenario-driven tests (Cest).
 *
 * * `codecept g:scenarios acceptance` - for all acceptance tests
 * * `codecept g:scenarios acceptance --format html` - in html format
 * * `codecept g:scenarios acceptance --path doc` - generate scenarios to `doc` dir
 */
class GenerateScenarios extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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 "<html><body>$text</body></html>";
        }
        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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\StepObject as StepObjectGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;

use function ucfirst;

/**
 * Generates StepObject class. You will be asked for steps you want to implement.
 *
 * * `codecept g:stepobject acceptance AdminSteps`
 * * `codecept g:stepobject acceptance UserSteps --silent` - skip action questions
 *
 */
class GenerateStepObject extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>StepObject {$filename} already exists</error>");
            return 1;
        }
        $output->writeln("<info>StepObject was created in {$filename}</info>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Lib\Generator\Feature;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function basename;
use function preg_match;
use function rtrim;

/**
 * Generates Feature file (in Gherkin):
 *
 * * `codecept generate:feature suite Login`
 * * `codecept g:feature suite subdir/subdir/login.feature`
 * * `codecept g:feature suite login.feature -c path/to/project`
 *
 */
class GenerateFeature extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>Feature {$filename} already exists</error>");
            return 1;
        }
        $output->writeln("<info>Feature was created in {$fullPath}</info>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Codecept;
use Exception;
use Humbug\SelfUpdate\Updater;
use Phar;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function sprintf;

/**
 * Auto-updates phar archive from official site: 'https://codeception.com/codecept.phar' .
 *
 * * `php codecept.phar self-update`
 *
 * @author Franck Cassedanne <franck@cassedanne.com>
 */
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 <comment>%s</comment> 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(
                '<info>%s</info> version <comment>%s</comment>',
                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("\n<info>Updating...</info>");
                $updater->update();

                $output->writeln(
                    sprintf("\n<comment>%s</comment> has been updated.\n", $this->filename)
                );
            } else {
                $output->writeln('You are already using the latest version.');
            }
        } catch (Exception $e) {
            $output->writeln(
                sprintf(
                    "<error>\n%s\n</error>",
                    $e->getMessage()
                )
            );
            return 1;
        }

        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command\Shared;

use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Output\OutputInterface;

trait StyleTrait
{
    public function addStyles(OutputInterface $output): void
    {
        $output->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'));
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command\Shared;

use Codeception\Util\Shared\Namespaces;

use function file_exists;
use function file_put_contents;
use function mkdir;
use function pathinfo;
use function preg_replace;
use function rtrim;
use function str_replace;
use function strrev;

trait FileSystemTrait
{
    use Namespaces;

    protected function createDirectoryFor(string $basePath, string $className = ''): string
    {
        $basePath = rtrim($basePath, DIRECTORY_SEPARATOR);
        if ($className) {
            $className = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $className);
            $path = $basePath . DIRECTORY_SEPARATOR . $className;
            $basePath = pathinfo($path, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR;
        }
        if (!file_exists($basePath)) {
            // Second argument should be mode. Well, umask() doesn't seem to return any if not set. Config may fix this.
            mkdir($basePath, 0775, true); // Third parameter commands to create directories recursively
        }
        return $basePath;
    }

    protected function completeSuffix(string $filename, string $suffix): string
    {
        if (str_starts_with(strrev($filename), strrev($suffix))) {
            $filename .= '.php';
        }
        if (!str_starts_with(strrev($filename), strrev($suffix . '.php'))) {
            $filename .= $suffix . '.php';
        }
        if (!str_starts_with(strrev($filename), strrev('.php'))) {
            $filename .= '.php';
        }

        return $filename;
    }

    protected function removeSuffix(string $classname, string $suffix): string
    {
        $classname = preg_replace('#\.php$#', '', $classname);
        return preg_replace("#{$suffix}$#", '', $classname);
    }

    protected function createFile(string $filename, string $contents, bool $force = false, int $flags = 0): bool
    {
        if (file_exists($filename) && !$force) {
            return false;
        }
        file_put_contents($filename, $contents, $flags);
        return true;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command\Shared;

use Codeception\Scenario;

trait ActorTrait
{
    protected function getActorClassName(): ?string
    {
        if (empty($this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command\Shared;

use Codeception\Configuration;
use InvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;

use function array_merge_recursive;
use function array_pop;
use function array_shift;
use function class_exists;
use function count;
use function explode;
use function str_repeat;
use function ucfirst;

trait ConfigTrait
{
    protected function getSuiteConfig(string $suite): array
    {
        return Configuration::suiteSettings($suite, $this->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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\Helper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function ucfirst;

/**
 * Creates empty Helper class.
 *
 * * `codecept g:helper MyHelper`
 * * `codecept g:helper "My\Helper"`
 *
 */
class GenerateHelper extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<info>Helper {$filename} created</info>");
            return 0;
        } else {
            $output->writeln("<error>Error creating helper {$filename}</error>");
            return 1;
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\Actor as ActorGenerator;
use Codeception\Util\Template;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;

use function file_exists;
use function preg_match;
use function ucfirst;

/**
 * Create new test suite. Requires suite name and actor name
 *
 * * ``
 * * `codecept g:suite api` -> 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("<error>Suite name '{$suite}' contains invalid characters. ([A-Za-z0-9_]).</error>");
            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'],
                "<?php\n",
                true
            );
        }

        $yamlSuiteConfigTemplate = <<<EOF
actor: {{actor}}
suite_namespace: {{suite_namespace}}
modules:
    # enable helpers as array
    enabled: []
EOF;

        $this->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 <info>" . $actor . "</info> was created in {$file}");

        $output->writeln("Suite config <info>{$suite}.suite.yml</info> was created.");
        $output->writeln(' ');
        $output->writeln("Next steps:");
        $output->writeln("1. Edit <bold>{$suite}.suite.yml</bold> to enable modules for this suite");
        $output->writeln("2. Create first test with <bold>generate:cest testName</bold> ( or test|cept) command");
        $output->writeln("3. Run tests of this suite with <bold>codecept run {$suite}</bold> command");

        $output->writeln("<info>Suite {$suite} generated</info>");
        return 0;
    }

    private function containsInvalidCharacters(string $suite): bool
    {
        return (bool)preg_match('#[^A-Za-z0-9_]#', $suite);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Lib\Generator\Test as TestGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Generates skeleton for Unit Test that extends `Codeception\TestCase\Test`.
 *
 * * `codecept g:test unit User`
 * * `codecept g:test unit "App\User"`
 */
class GenerateTest extends Command
{
    use Shared\FileSystemTrait;
    use Shared\ConfigTrait;

    protected function configure(): void
    {
        $this->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("<error>Test {$filename} already exists</error>");
            return 1;
        }
        $output->writeln("<info>Test was created in {$filename}</info>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Codecept;
use Codeception\Configuration;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Exception\ConfigurationException;
use Codeception\Lib\Console\Output;
use Codeception\Scenario;
use Codeception\Suite;
use Codeception\SuiteManager;
use Codeception\Test\Cept;
use Codeception\Util\Debug;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function array_keys;
use function file_exists;
use function function_exists;
use function pcntl_signal;

/**
 * Try to execute test commands in run-time. You may try commands before writing the test.
 *
 * * `codecept console acceptance` - starts acceptance suite environment. If you use WebDriver you can manipulate browser with Codeception commands.
 */
class Console extends Command
{
    protected ?Cept $test = null;

    protected ?Codecept $codecept = null;

    protected ?Suite $suite = null;

    protected ?OutputInterface $output = null;

    /**
     * @var string[]
     */
    protected array $actions = [];

    protected function configure(): void
    {
        $this->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("<info>Interactive console started for suite {$suiteName}</info>");
        $output->writeln("<info>Try Codeception commands without writing a test</info>");

        $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("<info>Bye-bye!</info>");
        return 0;
    }

    protected function listenToSignals(): void
    {
        if (function_exists('pcntl_signal')) {
            declare(ticks=1);
            pcntl_signal(SIGINT, SIG_IGN);
            pcntl_signal(SIGTERM, SIG_IGN);
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Stecman\Component\Symfony\Console\BashCompletion\Completion as ConsoleCompletion;
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionInterface as ConsoleCompletionInterface;
use Stecman\Component\Symfony\Console\BashCompletion\Completion\ShellPathCompletion;
use Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand;
use Stecman\Component\Symfony\Console\BashCompletion\CompletionHandler;
use Symfony\Component\Console\Input\InputDefinition as SymfonyInputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

// phpcs:ignoreFile PSR1.Files.SideEffects.FoundWithSymbols
if (!class_exists(ConsoleCompletion::class)) {
    echo "Please install `stecman/symfony-console-completion\n` to enable auto completion";
    return;
}

class Completion extends CompletionCommand
{
    protected function configureCompletion(CompletionHandler $handler): void
    {
        // Can't set for all commands, because it wouldn't work well with generate:suite
        $suiteCommands = [
            'run',
            'config:validate',
            'console',
            'dry-run',
            'generate:cest',
            'generate:feature',
            'generate:phpunit',
            'generate:scenarios',
            'generate:stepobject',
            'generate:test',
            'gherkin:snippets',
            'gherkin:steps'
        ];

        foreach ($suiteCommands as $suiteCommand) {
            $handler->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CompletionFallback extends Command
{
    public function __construct()
    {
        parent::__construct('_completion');
    }

    protected function configure(): void
    {
        $this
            ->setDescription('BASH completion hook.')
            ->setHidden(true) // Hide from listing
            ->setHelp(<<<END
To enable BASH completion, install optional stecman/symfony-console-completion first:

    <comment>composer require stecman/symfony-console-completion</comment>

END);
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln("Install optional <comment>stecman/symfony-console-completion</comment>");
        return 0;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\InitTemplate;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function class_exists;
use function ucfirst;

class Init extends Command
{
    protected function configure(): void
    {
        $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use function codecept_data_dir;
use function codecept_output_dir;
use function codecept_root_dir;
use function implode;
use function preg_replace;
use function print_r;

/**
 * Validates and prints Codeception config.
 * Use it do debug Yaml configs
 *
 * Check config:
 *
 * * `codecept config`: check global config
 * * `codecept config unit`: check suite config
 *
 * Load config:
 *
 * * `codecept config:validate -c path/to/another/config`: from another dir
 * * `codecept config:validate -c another_config.yml`: from another config file
 *
 * Check overriding config values (like in `run` command)
 *
 * * `codecept config:validate -o "settings: shuffle: true"`: enable shuffle
 * * `codecept config:validate -o "settings: lint: false"`: disable linting
 * * `codecept config:validate -o "reporters: report: \Custom\Reporter" --report`: use custom reporter
 *
 */
class ConfigValidate extends Command
{
    use Shared\ConfigTrait;
    use Shared\StyleTrait;

    protected function configure(): void
    {
        $this->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 <bold>{$suite}</bold> config... ");
            $config = $this->getSuiteConfig($suite);
            $output->writeln("Ok");
            $output->writeln("------------------------------\n");
            $output->writeln("<info>{$suite} Suite Config</info>:\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("<info>Codeception Config</info>:\n");
        $output->writeln($this->formatOutput($config));

        $output->writeln('<info>Directories</info>:');
        $output->writeln("<comment>codecept_root_dir()</comment>   " . codecept_root_dir());
        $output->writeln("<comment>codecept_output_dir()</comment> " . codecept_output_dir());
        $output->writeln("<comment>codecept_data_dir()</comment>   " . codecept_data_dir());
        $output->writeln('');

        $output->writeln("<info>Available suites</info>: " . implode(', ', $suites));

        foreach ($suites as $suite) {
            $output->write("Validating suite <bold>{$suite}</bold>... ");
            $this->getSuiteConfig($suite);
            $output->writeln('Ok');
        }

        $output->writeln("Execute <info>codecept config:validate [<suite>]</info> to see config for a suite");
        return 0;
    }

    protected function formatOutput($config): ?string
    {
        $output = print_r($config, true);
        return preg_replace('#\[(.*?)] =>#', "<fg=yellow>$1</fg=yellow> =>", $output);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\Actions as ActionsGenerator;
use Codeception\Lib\Generator\Actor as ActorGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface as SymfonyOutputInterface;

use function implode;

/**
 * Generates Actor classes (initially Guy classes) from suite configs.
 * Starting from Codeception 2.0 actor classes are auto-generated. Use this command to generate them manually.
 *
 * * `codecept build`
 * * `codecept build path/to/project`
 *
 */
class Build extends Command
{
    use Shared\ConfigTrait;
    use Shared\FileSystemTrait;

    protected string $inheritedMethodTemplate = ' * @method void %s(%s)';

    protected ?SymfonyOutputInterface $output = null;

    public function getDescription(): string
    {
        return 'Generates base classes for all suites';
    }

    protected function execute(InputInterface $input, SymfonyOutputInterface $output): int
    {
        $this->output = $output;
        $this->buildActorsForConfig();
        return 0;
    }

    private function buildActor(array $settings): bool
    {
        $actorGenerator = new ActorGenerator($settings);
        $this->output->writeln(
            '<info>' . Configuration::config()['namespace'] . '\\' . $actorGenerator->getActorName()
            . "</info> 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("<info>Building Actor classes for suites: " . implode(', ', $suites) . '</info>');
        }
        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("\n<comment>Included Configuration: {$subConfig}</comment>");
            $this->buildActorsForConfig($dir . DIRECTORY_SEPARATOR . $subConfig);
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use RuntimeException;

/**
 * Really basic class to store data in global array and use it in Cests/Tests.
 *
 * ```php
 * <?php
 * Fixtures::add('user1', ['name' => '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]);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use ReflectionException;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionProperty;

use function array_filter;
use function array_keys;
use function array_map;
use function array_pop;
use function count;
use function explode;
use function get_class;
use function implode;
use function in_array;
use function is_array;
use function is_null;
use function is_string;
use function json_encode;
use function method_exists;
use function range;
use function var_export;

/**
 * This class contains helper methods to help with common Reflection tasks.
 */
class ReflectionHelper
{
    /**
     * Read a private property of an object.
     *
     * @throws ReflectionException
     */
    public static function readPrivateProperty(object $object, string $property, string $class = null): mixed
    {
        if (is_null($class)) {
            $class = $object;
        }

        $property = new ReflectionProperty($class, $property);
        $property->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) . ']';
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use Codeception\Command\Console;
use Codeception\Lib\Console\Output;
use Codeception\Lib\PauseShell;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Question\ConfirmationQuestion;

/**
 * This class is used only when Codeception is executed in `--debug` mode.
 * In other cases method of this class won't be seen.
 */
class Debug
{
    protected static ?Output $output = null;

    public static function setOutput(Output $output): void
    {
        self::$output = $output;
    }

    /**
     * Prints data to screen. Message can be any time of data
     */
    public static function debug(mixed $message): void
    {
        if (!self::$output) {
            return;
        }
        self::$output->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));
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use Codeception\Exception\ConfigurationException;

use function array_fill;
use function array_filter;
use function array_merge;
use function array_shift;
use function count;
use function explode;
use function implode;
use function preg_match;
use function preg_quote;
use function preg_replace;
use function strlen;
use function substr;

class PathResolver
{
    /**
     * Returns path to a given directory relative to $projDir.
     */
    public static function getRelativeDir(string $path, string $projDir, string $dirSep = DIRECTORY_SEPARATOR): string
    {
        // ensure $projDir ends with a trailing $dirSep
        $projDir = preg_replace('/' . preg_quote($dirSep, '/') . '*$/', $dirSep, $projDir);
        // if $path is a within $projDir
        if (self::fsCaseStrCmp(substr($path, 0, strlen($projDir)), $projDir, $dirSep) == 0) {
            // simply chop it off the front
            return substr($path, strlen($projDir));
        }
        // Identify any absoluteness prefix (like '/' in Unix or "C:\\" in Windows)
        $pathAbsPrefix = self::getPathAbsolutenessPrefix($path, $dirSep);
        $projDirAbsPrefix = self::getPathAbsolutenessPrefix($projDir, $dirSep);
        $sameAbsoluteness = (self::fsCaseStrCmp($pathAbsPrefix['wholePrefix'], $projDirAbsPrefix['wholePrefix'], $dirSep) == 0);
        if (!$sameAbsoluteness) {
            // if the $projDir and $path aren't relative to the same
            // thing, we can't make a relative path.

            // if we're relative to the same device ...
            if (
                strlen($pathAbsPrefix['devicePrefix']) &&
                (self::fsCaseStrCmp($pathAbsPrefix['devicePrefix'], $projDirAbsPrefix['devicePrefix'], $dirSep) == 0)
            ) {
                // ... shave that off
                return substr($path, strlen($pathAbsPrefix['devicePrefix']));
            }
            // Return the input unaltered
            return $path;
        }
        // peel off optional absoluteness prefixes and convert
        // $path and $projDir to an subdirectory path array
        $relPathParts = array_filter(explode($dirSep, substr($path, strlen($pathAbsPrefix['wholePrefix']))), 'strlen');
        $relProjDirParts = array_filter(explode($dirSep, substr($projDir, strlen($projDirAbsPrefix['wholePrefix']))), 'strlen');
        // While there are any, peel off any common parent directories
        // from the beginning of the $projDir and $path
        while (
            ($relPathParts !== []) && ($relProjDirParts !== []) &&
            (self::fsCaseStrCmp($relPathParts[0], $relProjDirParts[0], $dirSep) == 0)
        ) {
            array_shift($relPathParts);
            array_shift($relProjDirParts);
        }
        if ($relProjDirParts !== []) {
            // prefix $relPath with '..' for all remaining unmatched $projDir
            // subdirectories
            $relPathParts = array_merge(array_fill(0, count($relProjDirParts), '..'), $relPathParts);
        }
        // only append a trailing seperator if one is already present
        $trailingSep = preg_match('/' . preg_quote($dirSep, '/') . '$/', $path) ? $dirSep : '';
        // convert array of dir paths back into a string path
        return implode($dirSep, $relPathParts) . $trailingSep;
    }

    /**
     * FileSystem Case String Compare
     * compare two strings with the filesystem's case-sensitiveness
     *
     * @return int -1 / 0 / 1 for < / = / > 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<string, string>
     */
    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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

use function basename;
use function rmdir;
use function unlink;

/**
 * Set of functions to work with Filesystem
 */
class FileSystem
{
    public static function doEmptyDir(string $path): void
    {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($path),
            RecursiveIteratorIterator::CHILD_FIRST
        );

        foreach ($iterator as $path) {
            $basename = basename((string)$path);
            if ($basename === '.' || $basename === '..' || $basename === '.gitignore' || $basename === '.gitkeep') {
                continue;
            }

            if ($path->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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use function array_unshift;
use function file_exists;
use function rtrim;
use function spl_autoload_register;
use function str_replace;
use function trim;

/**
 * Autoloader, which is fully compatible with PSR-4,
 * and can be used to autoload your `Helper`, `Page`, and `Step` classes.
 */
class Autoload
{
    protected static bool $registered = false;

    /**
     * An associative array where the key is a namespace prefix and the value
     * is an array of base directories for classes in that namespace.
     */
    protected static array $map = [];

    private function __construct()
    {
    }

    /**
     * Adds a base directory for a namespace prefix.
     *
     * Example:
     *
     * ```php
     * <?php
     * // app\Codeception\UserHelper will be loaded from '/path/to/helpers/UserHelper.php'
     * Autoload::addNamespace('app\Codeception', '/path/to/helpers');
     *
     * // LoginPage will be loaded from '/path/to/pageobjects/LoginPage.php'
     * Autoload::addNamespace('', '/path/to/pageobjects');
     *
     * Autoload::addNamespace('app\Codeception', '/path/to/controllers');
     * ```
     *
     * @param string $prefix The namespace prefix.
     * @param string $baseDir A base directory for class files in the namespace.
     * @param bool $prepend If true, prepend the base directory to the stack instead of appending it;
     *                      this causes it to be searched first rather than last.
     */
    public static function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void
    {
        if (!self::$registered) {
            spl_autoload_register([__CLASS__, 'load']);
            self::$registered = true;
        }

        // normalize namespace prefix
        $prefix = trim($prefix, '\\') . '\\';

        // normalize the base directory with a trailing separator
        $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
        $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';

        // initialize the namespace prefix array
        if (!isset(self::$map[$prefix])) {
            self::$map[$prefix] = [];
        }

        // retain the base directory for the namespace prefix
        if ($prepend) {
            array_unshift(self::$map[$prefix], $baseDir);
        } else {
            self::$map[$prefix][] = $baseDir;
        }
    }

    public static function load(string $class): string|false
    {
        // the current namespace prefix
        $prefix = $class;

        // work backwards through the namespace names of the fully-qualified class name to find a mapped file name
        while (false !== ($pos = strrpos($prefix, '\\'))) {
            // retain the trailing namespace separator in the prefix
            $prefix = substr($class, 0, $pos + 1);

            // the rest is the relative class name
            $relative_class = substr($class, $pos + 1);

            // try to load a mapped file for the prefix and relative class
            $mapped_file = self::loadMappedFile($prefix, $relative_class);
            if ($mapped_file) {
                return $mapped_file;
            }

            // remove the trailing namespace separator for the next iteration of strrpos()
            $prefix = rtrim($prefix, '\\');
        }

        // fix for empty prefix
        if (isset(self::$map['\\']) && ($class[0] != '\\')) {
            return self::load('\\' . $class);
        }

        if (str_contains($class, '\\')) {
            $relative_class = substr(strrchr($class, '\\'), 1); // Foo\Bar\ClassName -> 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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use ReflectionClass;
use ReflectionMethod;
use Reflector;

use function get_class;
use function in_array;
use function is_object;
use function json_decode;
use function preg_match;
use function preg_match_all;
use function sprintf;
use function substr;
use function trim;

/**
 * Simple annotation parser. Take only key-value annotations for methods or class.
 */
class Annotation
{
    /**
     * @var ReflectionClass[]
     */
    protected static array $reflectedClasses = [];

    protected static string $regex = '/@%s(?:[ \t]*(.*?))?[ \t]*(?:\*\/)?\r?$/m';

    protected ReflectionClass $reflectedClass;

    /**
     * @var ReflectionClass|ReflectionMethod
     */
    protected Reflector $currentReflectedItem;

    /**
     * Grabs annotation values.
     *
     * Usage example:
     *
     * ``` php
     * <?php
     * Annotation::forClass('MyTestCase')->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use Throwable;

class StackTraceFilter
{
    protected static array $filteredClassesPattern = [
        'Symfony\Component\Console',
        'Codeception\Command\\',
        'Codeception\TestCase\\',
    ];

    public static function getFilteredStackTrace(Throwable $e, bool $asString = true, bool $filter = true): array|string
    {
        $stackTrace = $asString ? '' : [];

        $trace = $e->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use ArrayAccess;
use Iterator;
use JsonSerializable;

use function array_keys;
use function call_user_func_array;
use function count;
use function is_array;
use function is_object;
use function property_exists;
use function range;

/**
 * Class to represent any type of content.
 * This class can act as an object, array, or string.
 * Method or property calls to this class won't cause any errors.
 *
 * Maybe was used in Codeception 1.x to represent data on parsing step.
 * Not widely used in 2.0 anymore, but left for compatibility.
 *
 * For instance, you may use `Codeception\Util\Maybe` as a test dummies.
 *
 * ```php
 * <?php
 * $user = new Maybe;
 * $user->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();
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util\Shared;

use function array_filter;
use function array_pop;
use function explode;
use function implode;
use function ltrim;
use function str_replace;

trait Namespaces
{
    /**
     * @return string[]
     */
    protected function breakParts(string $class): array
    {
        // removing leading slashes and dots first
        $class = str_replace('/', '\\', ltrim($class, './\\'));
        return explode('\\', $class);
    }

    protected function getShortClassName(string $class): string
    {
        $namespaces = $this->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');
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use function array_key_exists;
use function explode;
use function is_array;
use function preg_match_all;
use function sprintf;
use function str_replace;

/**
 * Basic template engine used for generating initial Cept/Cest/Test files.
 */
class Template
{
    private array $vars = [];

    public function __construct(
        private string $template,
        private string $placeholderStart = '{{',
        private string $placeholderEnd = '}}',
        private ?string $encoderFunction = null,
    ) {
    }

    /**
     * Replaces {{var}} string with provided value
     */
    public function place(string $var, $val): self
    {
        $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use Closure;
use Codeception\Step\Action;
use Exception;

use function call_user_func_array;
use function codecept_debug;
use function get_class;
use function implode;
use function is_array;
use function str_replace;

/**
 * Class for defining an array actions to be executed inside `performOn` of WebDriver
 *
 * ```php
 * <?php
 * (new ActionSequence)->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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Command\Shared\FileSystemTrait;
use Codeception\Command\Shared\StyleTrait;
use Codeception\Lib\Generator\Actions;
use Codeception\Lib\Generator\Actor;
use Codeception\Lib\Generator\Helper;
use Codeception\Lib\ModuleContainer;
use Exception;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;

/**
 * Codeception templates allow creating a customized setup and configuration for your project.
 * An abstract class for installation template. Each init template should extend it and implement a `setup` method.
 * Use it to build a custom setup class which can be started with `codecept init` command.
 *
 *
 * ```php
 * <?php
 * namespace Codeception\Template; // it is important to use this namespace so codecept init could locate this template
 * class CustomInstall extends \Codeception\InitTemplate
 * {
 *      public function setup()
 *      {
 *         // implement this
 *      }
 * }
 * ```
 * This class provides various helper methods for building customized setup
 */
abstract class InitTemplate
{
    use FileSystemTrait;
    use StyleTrait;

    /**
     * @var string
     */
    public const GIT_IGNORE = '.gitignore';

    protected string $namespace = 'Tests';

    protected string $actorSuffix = 'Tester';

    protected string $supportNamespace = 'Support';

    protected string $workDir = '.';

    protected OutputInterface $output;

    public function __construct(protected InputInterface $input, OutputInterface $output)
    {
        $this->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
     * <?php
     * // propose firefox as default browser
     * $this->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 .= " <info>(" . $answer[0] . ")</info> ";
            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 .= " <info>({$answer})</info>";
        }
        return $dialog->ask($this->input, $this->output, new Question("{$question} ", $answer));
    }

    /**
     * Print a message to console.
     *
     * ```php
     * <?php
     * $this->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("<notice> {$message} </notice>");
    }

    /**
     * Print error message
     */
    protected function sayError(string $message): void
    {
        $this->say("<error> {$message} </error>");
    }

    /**
     * Print warning message
     */
    protected function sayWarning(string $message): void
    {
        $this->say("<warning> {$message} </warning>");
    }

    /**
     * Print info message
     */
    protected function sayInfo(string $message): void
    {
        $this->say("<debug> {$message}</debug>");
    }

    /**
     * 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<string, mixed> $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);
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

interface CustomCommandInterface
{
    /**
     * returns the name of the command
     */
    public static function getCommandName(): string;
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final class Group
{
    public function __construct(string ...$groups)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Prepare
{
    public function __construct(string ...$methodNames)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Then
{
    public function __construct(string $description)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Depends
{
    public function __construct(string ...$testNames)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
final class BeforeClass
{
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Examples
{
    public function __construct(mixed ...$values)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class After
{
    public function __construct(string ...$methodNames)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class Incomplete
{
    public function __construct(string $reason = '')
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Before
{
    public function __construct(string ...$methodNames)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
final class AfterClass
{
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class DataProvider
{
    public function __construct(string $methodName)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
final class Skip
{
    public function __construct(string $reason = '')
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Env
{
    public function __construct(string ...$envValues)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Given
{
    public function __construct(string $description)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class When
{
    public function __construct(string $description)
    {
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Closure;
use Codeception\Lib\ModuleContainer;
use Codeception\Step\Argument\FormattedOutput;
use Codeception\Step\Meta as MetaStep;
use Codeception\Util\Locator;
use Exception;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\MockObject\MockObject;
use RuntimeException;
use Stringable;

abstract class Step implements Stringable
{
    /**
     * @var int
     */
    public const DEFAULT_MAX_LENGTH = 200;

    /**
     * @var int
     */
    public const STACK_POSITION = 3;

    public bool $executed = false;

    protected string|int|null $line = null;

    protected ?string $file = null;

    protected string $prefix = 'I';

    protected ?MetaStep $metaStep = null;

    protected bool $failed = false;

    protected bool $isTry = false;

    public function __construct(protected string $action, protected array $arguments = [])
    {
    }

    public function saveTrace(): void
    {
        $stack = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);

        if (count($stack) <= self::STACK_POSITION) {
            return;
        }

        $traceLine = $stack[self::STACK_POSITION - 1];

        if (!isset($traceLine['file'])) {
            return;
        }
        $this->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 <span style="color: %s">%s</span>', 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 . ' ';
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Codeception\Exception\ContentNotFound;
use Codeception\Util\Debug;
use Codeception\Util\Shared\Asserts;
use PHPUnit\Framework\AssertionFailedError;

abstract class Snapshot
{
    use Asserts;

    protected ?string $fileName = null;

    /**
     * @var string|false
     */
    protected $dataSet;

    protected ?bool $refresh = null;

    protected bool $showDiff = false;

    protected bool $saveAsJson = true;

    protected string $extension = 'json';

    /**
     * Should return data from current test run
     */
    abstract protected function fetchData(): array|string|false;

    /**
     * Performs assertion on saved data set against current dataset.
     * Can be overridden to implement custom assertion
     */
    protected function assertData(mixed $data): void
    {
        $this->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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class Error extends Exception
{
    public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);

        $this->file = $file;
        $this->line = $line;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class InjectionException extends Exception
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

use function is_object;

class ExtensionException extends Exception
{
    /**
     * ExtensionException constructor.
     *
     * @param object|string $extension
     */
    public function __construct($extension, string $message, Exception $previous = null)
    {
        parent::__construct($message, 0, $previous);
        if (is_object($extension)) {
            $extension = $extension::class;
        }
        $this->message = $extension . "\n\n" . $this->message;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class ParseException extends Exception
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Throwable;

class ThrowableWrapper extends Error
{
    public function __construct(Throwable $throwable)
    {
        parent::__construct(
            $throwable::class . ': ' . $throwable->getMessage(),
            $throwable->getCode(),
            $throwable->getFile(),
            $throwable->getLine()
        );
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class RemoteException extends Exception
{
    public function __construct(string $message)
    {
        parent::__construct($message);
        $this->message = "Remote Application Error:\n" . $this->message;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use PHPUnit\Framework\AssertionFailedError;

class ContentNotFound extends AssertionFailedError
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class InvalidTestException extends Exception
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class TestParseException extends Exception
{
    public function __construct(string $fileName, string $errors = null, int $line = null)
    {
        $this->message = "Couldn't parse test '{$fileName}'";
        if ($line !== null) {
            $this->message .= " on line {$line}";
        }
        if ($errors) {
            $this->message .= PHP_EOL . $errors;
        }
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use RuntimeException;

class TestRuntimeException extends RuntimeException
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

class Deprecation extends Error
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

class Notice extends Error
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

use function is_object;
use function ltrim;
use function str_replace;

class ModuleRequireException extends Exception
{
    /**
     * ModuleRequireException constructor.
     *
     * @param object|string $module
     */
    public function __construct($module, string $message)
    {
        if (is_object($module)) {
            $module = $module::class;
        }
        $module = str_replace('Codeception\\Module\\', '', ltrim($module, '\\'));
        parent::__construct($message);
        $this->message = "[{$module}] module requirements not met --\n \n" . $this->message;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

class ConfigurationException extends Exception
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

use function is_object;
use function ltrim;
use function str_replace;

class ModuleConflictException extends Exception
{
    /**
     * ModuleConflictException constructor.
     *
     * @param object|string $module
     * @param object|string $conflicted
     */
    public function __construct($module, $conflicted, string $additional = '')
    {
        if (is_object($module)) {
            $module = $module::class;
        }
        if (is_object($conflicted)) {
            $conflicted = $conflicted::class;
        }
        $module = ltrim(str_replace('Codeception\Module\\', '', $module), '\\');
        $conflicted = ltrim(str_replace('Codeception\Module\\', '', $conflicted), '\\');
        $this->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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

use function is_object;
use function ltrim;
use function str_replace;

class ModuleConfigException extends Exception
{
    /**
     * ModuleConfigException constructor.
     *
     * @param object|string $module
     */
    public function __construct($module, string $message, Exception $previous = null)
    {
        if (is_object($module)) {
            $module = $module::class;
        }
        $module = str_replace('Codeception\Module\\', '', ltrim($module, '\\'));
        parent::__construct($message, 0, $previous);
        $this->message = $module . " module is not configured!\n \n" . $this->message;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

class Warning extends Error
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use PHPUnit\Framework\AssertionFailedError;

class ConditionalAssertionFailed extends AssertionFailedError
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use PHPUnit\Framework\AssertionFailedError;

class UselessTestException extends AssertionFailedError
{
}
<?php

declare(strict_types=1);

namespace Codeception\Exception;

use Exception;

use function is_object;
use function ltrim;
use function str_replace;

class ModuleException extends Exception
{
    protected string $module;

    /**
     * ModuleException constructor.
     *
     * @param object|string $module
     */
    public function __construct($module, string $message)
    {
        if (is_object($module)) {
            $module = $module::class;
        }
        $module = ltrim(str_replace('Codeception\Module\\', '', $module), '\\');
        $this->module = $module;
        parent::__construct($message);
        $this->message = "{$module}: {$this->message}";
    }
}
<?php

namespace Codeception\PHPUnit;

abstract class TestCase extends \PHPUnit\Framework\TestCase
{
    protected function setUp(): void
    {
        if (method_exists($this, '_setUp')) {
            $this->_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);
    }
}
<?php

namespace Codeception\PHPUnit\Wrapper;

use PHPUnit\Framework\Test as PHPUnitTest;
use PHPUnit\Framework\TestResult;

abstract class Test implements PHPUnitTest
{
    public function run(?TestResult $result = null): TestResult
    {
        // does nothing
    }
}
<?php

namespace Codeception\PHPUnit\Wrapper;

use PHPUnit\Framework\Test as PHPUnitTest;
use PHPUnit\Framework\TestResult;

abstract class Test implements PHPUnitTest
{
    public function run(): void
    {
        // does nothing
    }
}
<?php

// function not autoloaded in PHP, thus its a good place for them
use Codeception\Extension\Logger;

function codecept_debug($data)
{
    \Codeception\Util\Debug::debug($data);
}

/**
 * Executes interactive pause in ths place
 *
 * @param array $vars
 * @return void
 */
function codecept_pause(array $vars = []): void
{
    \Codeception\Util\Debug::pause($vars);
}

function codecept_root_dir($appendPath = '')
{
    return \Codeception\Configuration::projectDir() . $appendPath;
}

function codecept_output_dir($appendPath = '')
{
    return \Codeception\Configuration::outputDir() . $appendPath;
}

function codecept_log_dir($appendPath = '')
{
    return \Codeception\Configuration::outputDir() . $appendPath;
}


function codecept_data_dir($appendPath = '')
{
    return \Codeception\Configuration::dataDir() . $appendPath;
}

function codecept_relative_path($path)
{
    return \Codeception\Util\PathResolver::getRelativeDir(
        $path,
        \Codeception\Configuration::projectDir(),
        DIRECTORY_SEPARATOR
    );
}

/**
 * If $path is absolute, it will be returned without changes.
 * If $path is relative, it will be passed to `codecept_root_dir()` function
 * to make it absolute.
 *
 * @param string $path
 * @return string the absolute path
 */
function codecept_absolute_path($path)
{
    return codecept_is_path_absolute($path) ? $path : codecept_root_dir($path);
}

/**
 * Check whether the given $path is absolute.
 *
 * @param string $path
 * @return bool
 * @since 2.4.4
 */
function codecept_is_path_absolute($path)
{
    return \Codeception\Util\PathResolver::isPathAbsolute($path);
}

function codecept_log(): \Monolog\Logger
{
    return Logger::getLogger();
}
<?php

require_once __DIR__ . '/autoload.php';

use Codeception\Application;

call_user_func(static function () {
    $app = new Application('Codeception', Codeception\Codecept::VERSION);
    $app->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();
});
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Events;
use Codeception\Exception\ExtensionException;
use Codeception\Extension;
use Symfony\Component\Process\Process;

use function array_shift;
use function class_exists;
use function count;
use function is_array;
use function sleep;

/**
 * Extension for execution of some processes before running tests.
 *
 * Processes can be independent and dependent.
 * Independent processes run independently of each other.
 * Dependent processes run sequentially one by one.
 *
 * Can be configured in suite config:
 *
 * ```yaml
 * # acceptance.suite.yml
 * extensions:
 *     enabled:
 *         - Codeception\Extension\RunBefore:
 *             - independent_process_1
 *             -
 *                 - dependent_process_1_1
 *                 - dependent_process_1_2
 *             - independent_process_2
 *             -
 *                 - dependent_process_2_1
 *                 - dependent_process_2_2
 * ```
 *
 * HINT: you can use different configurations per environment.
 */
class RunBefore extends Extension
{
    protected array $config = [];

    /**
     * @var array<string, string>
     */
    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('<error>' . $processInstance->getErrorOutput() . '</error>');
                    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();
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Event\StepEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Exception\ExtensionException;
use Codeception\Extension;
use Codeception\Lib\Interfaces\ScreenshotSaver;
use Codeception\Module\WebDriver;
use Codeception\Step;
use Codeception\Step\Comment as CommentStep;
use Codeception\Test\Descriptor;
use Codeception\Util\FileSystem;
use Codeception\Util\Template;
use DateTime;
use DirectoryIterator;
use Exception;
use Symfony\Contracts\EventDispatcher\Event;

use function array_diff;
use function array_key_exists;
use function array_keys;
use function array_merge;
use function array_unique;
use function basename;
use function codecept_output_dir;
use function codecept_relative_path;
use function dirname;
use function file_put_contents;
use function in_array;
use function is_array;
use function is_dir;
use function mkdir;
use function preg_match;
use function preg_replace;
use function sprintf;
use function str_pad;
use function str_replace;
use function strcasecmp;
use function strlen;
use function substr;
use function trim;
use function ucfirst;
use function uniqid;

/**
 * Saves a screenshot of each step in acceptance tests and shows them as a slideshow on one HTML page (here's an [example](https://codeception.com/images/recorder.gif))
 * Activated only for suites with WebDriver module enabled.
 *
 * The screenshots are saved to `tests/_output/record_*` directories, open `index.html` to see them as a slideshow.
 *
 * #### Installation
 *
 * Add this to the list of enabled extensions in `codeception.yml` or `acceptance.suite.yml`:
 *
 * ``` yaml
 * extensions:
 *     enabled:
 *         - Codeception\Extension\Recorder
 * ```
 *
 * #### Configuration
 *
 * * `delete_successful` (default: true) - delete screenshots for successfully passed tests  (i.e. log only failed and errored tests).
 * * `module` (default: WebDriver) - which module for screenshots to use. Set `AngularJS` if you want to use it with AngularJS module. Generally, the module should implement `Codeception\Lib\Interfaces\ScreenshotSaver` interface.
 * * `ignore_steps` (default: []) - array of step names that should not be recorded (given the step passed), * wildcards supported. Meta steps can also be ignored.
 * * `success_color` (default: success) - bootstrap values to be used for color representation for passed tests
 * * `failure_color` (default: danger) - bootstrap values to be used for color representation for failed tests
 * * `error_color` (default: dark) - bootstrap values to be used for color representation for scenarios where there's an issue occurred while generating a recording
 * * `delete_orphaned` (default: false) - delete recording folders created via previous runs
 * * `include_microseconds` (default: false) - enable microsecond precision for recorded step time details
 *
 * #### Examples:
 *
 * ``` yaml
 * extensions:
 *     enabled:
 *         - Codeception\Extension\Recorder:
 *             module: AngularJS # enable for Angular
 *             delete_successful: false # keep screenshots of successful tests
 *             ignore_steps: [have, grab*]
 * ```
 * #### Skipping recording of steps with annotations
 *
 * It is also possible to skip recording of steps for specified tests by using the @skipRecording annotation.
 *
 * ```php
 * /**
 * * @skipRecording login
 * * @skipRecording amOnUrl
 * *\/
 * public function testLogin(AcceptanceTester $I)
 * {
 *     $I->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 = <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Recorder Result</title>

    <!-- Bootstrap Core CSS -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">

    <style>
        html,
        body {
            height: 100%;
        }
        .active {
            height: 100%;
        }
        .carousel-caption {
            background: rgba(0,0,0,0.8);
        }
        .carousel-caption.error {
            background: #c0392b !important;
        }
        .carousel-item {
            min-height: 100vh;
        }
        .fill {
            width: 100%;
            height: 100%;
            text-align: center;
            overflow-y: scroll;
            background-position: top;
            -webkit-background-size: cover;
            -moz-background-size: cover;
            background-size: cover;
            -o-background-size: cover;
        }
        .gradient-right {
             background:
                linear-gradient(to left, rgba(0,0,0,.4), rgba(0,0,0,.0))
        }
        .gradient-left {
            background:
                linear-gradient(to right, rgba(0,0,0,.4), rgba(0,0,0,.0))
        }
    </style>
</head>
<body>
    <!-- Navigation -->
        <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="navbar-header">
            <a class="navbar-brand" href="../records.html"></span>Recorded Tests</a>
        </div>
        <div class="collapse navbar-collapse" id="navbarText">
            <ul class="navbar-nav mr-auto">
                <span class="navbar-text">{{feature}}</span>
            </ul>
            <span class="navbar-text">{{test}}</span>
        </div>
    </nav>
    <header id="steps" class="carousel slide" data-ride="carousel">
        <!-- Indicators -->
        <ol class="carousel-indicators">
            {{indicators}}
        </ol>

        <!-- Wrapper for Slides -->
        <div class="carousel-inner">
            {{slides}}
        </div>

        <!-- Controls -->
        <a class="carousel-control-prev gradient-left" href="#steps" role="button" data-slide="prev">
            <span class="carousel-control-prev-icon" aria-hidden="false"></span>
            <span class="sr-only">Previous</span>
        </a>
        <a class="carousel-control-next gradient-right" href="#steps" role="button" data-slide="next">
            <span class="carousel-control-next-icon" aria-hidden="false"></span>
            <span class="sr-only">Next</span>
        </a>
    </header>

    <!-- jQuery -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

    <!-- Script to Activate the Carousel -->
    <script>
    $('.carousel').carousel({
        wrap: true,
        interval: false
    })

    $(document).bind('keyup', function(e) {
      if(e.keyCode==39){
      jQuery('a.carousel-control.right').trigger('click');
      }

      else if(e.keyCode==37){
      jQuery('a.carousel-control.left').trigger('click');
      }

    });

    </script>

</body>

</html>
EOF;

    protected string $indicatorTemplate = <<<EOF
<li data-target="#steps" data-slide-to="{{step}}" class="{{isActive}}"></li>
EOF;

    protected string $indexTemplate = <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Recorder Results Index</title>

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <!-- Navigation -->
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Recorded Tests
            </a>
        </div>
    </nav>
    <div class="container py-4">
        <h1>Record #{{seed}}</h1>
        <ul>
            {{records}}
        </ul>
    </div>
</body>

</html>

EOF;

    protected string $slidesTemplate = <<<EOF
<div class="carousel-item {{isActive}}">
    <img class="mx-auto d-block mh-100" src="{{image}}">
    <div class="carousel-caption {{isError}}">
        <h5>{{caption}}</h5>
        <p>Step finished at <span style="color: #3498db">"{{timeStamp}}"</span></p>
    </div>
</div>
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(
                '⏺ <bold>Recording</bold> ⏺ step-by-step screenshots will be saved to <info>%s</info>',
                codecept_output_dir()
            )
        );
        $this->writeln("Directory Format: <debug>record_{$this->seed}_{filename}_{testname}</debug> ----");
    }

    public function afterSuite(): void
    {
        if (!$this->webDriverModule) {
            return;
        }
        $links = '';

        if (!empty($this->slides)) {
            foreach ($this->recordedTests as $suiteName => $suite) {
                $links .= "<ul><li><b>{$suiteName}</b></li><ul>";
                foreach ($suite as $fileName => $tests) {
                    $links .= "<li>{$fileName}</li><ul>";

                    foreach ($tests as $test) {
                        $links .= in_array($test['path'], $this->skipRecording, true)
                            ? "<li class=\"text{$this->config['error_color']}\">{$test['name']}</li>\n"
                            : '<li class="text-' . $this->config[$test['status'] . '_color']
                            . "\"><a href='{$test['index']}'>{$test['name']}</a></li>\n";
                    }

                    $links .= '</ul>';
                }
                $links .= '</ul></ul>';
            }

            $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: <info>{$exception->getMessage()}</info>"
                );
            }

            $this->writeln('⏺ Records saved into: <info>file://' . codecept_output_dir() . 'records.html</info>');
        }

        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: <info>{$this->dir}</info>"
            );
        }
    }

    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: <info>{$dir}</info>"
                );
            }

            $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 <info>{$testPath}/before</info>"
                );
            }
        }

        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 <info>{$testPath}: "
                    . "{$exception->getMessage()}</info>"
                );
            }

            $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 <info>{$testPath}/{$event->getStep()->getAction()}</info>"
            );
        }

        ++$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]
        );
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Events;
use Codeception\Exception\ExtensionException;
use Codeception\Extension;
use Symfony\Component\Process\Process;

use function array_reverse;
use function class_exists;
use function is_int;
use function sleep;

/**
 * Extension to start and stop processes per suite.
 * Can be used to start/stop selenium server, chromedriver, mailcatcher, etc.
 *
 * Can be configured in suite config:
 *
 * ```yaml
 * # acceptance.suite.yml
 * extensions:
 *     enabled:
 *         - Codeception\Extension\RunProcess:
 *             - chromedriver
 * ```
 *
 * Multiple parameters can be passed as array:
 *
 * ```yaml
 * # acceptance.suite.yml
 *
 * extensions:
 *     enabled:
 *         - Codeception\Extension\RunProcess:
 *             - php -S 127.0.0.1:8000 -t tests/data/app
 *             - java -jar ~/selenium-server.jar
 * ```
 *
 * In the end of a suite all launched processes will be stopped.
 *
 * To wait for the process to be launched use `sleep` option.
 * In this case you need configuration to be specified as object:
 *
 * ```yaml
 * extensions:
 *     enabled:
 *         - Codeception\Extension\RunProcess:
 *             0: java -jar ~/selenium-server.jar
 *             1: mailcatcher
 *             sleep: 5 # wait 5 seconds for processes to boot
 * ```
 *
 * HINT: you can use different configurations per environment.
 */
class RunProcess extends Extension
{
    /**
     * @var array<int|string, mixed>
     */
    protected array $config = ['sleep' => 0];

    /**
     * @var array<string, string>
     */
    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__);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Event\PrintResultEvent;
use Codeception\Events;
use Codeception\Extension;
use Codeception\Test\Descriptor;

use function array_key_exists;
use function file_put_contents;
use function implode;
use function is_file;
use function realpath;
use function str_replace;
use function strlen;
use function substr;
use function unlink;

/**
 * Saves failed tests into tests/_output/failed in order to rerun failed tests.
 *
 * To rerun failed tests just run the `failed` group:
 *
 * ```
 * php codecept run -g failed
 * ```
 *
 * To change failed group name add:
 * ```
 * --override "extensions: config: Codeception\Extension\RunFailed: fail-group: another_group1"
 * ```
 * Remember: if you run tests and they generated custom-named fail group, to run this group, you should add override too
 *
 * Starting from Codeception 2.1 **this extension is enabled by default**.
 *
 * ``` yaml
 * extensions:
 *     enabled: [Codeception\Extension\RunFailed]
 * ```
 *
 * On each execution failed tests are logged and saved into `tests/_output/failed` file.
 */
class RunFailed extends Extension
{
    /**
     * @var array<string, string>
     */
    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;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Event\FailEvent;
use Codeception\Event\PrintResultEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Extension;
use Codeception\Subscriber\Console as CodeceptConsole;

/**
 * DotReporter provides less verbose output for test execution.
 * Like PHPUnit printer it prints dots "." for successful testes and "F" for failures.
 *
 * ![](https://cloud.githubusercontent.com/assets/220264/26132800/4d23f336-3aab-11e7-81ba-2896a4c623d2.png)
 *
 * ```bash
 *  ..........
 *  ..........
 *  ..........
 *  ..........
 *  ..........
 *  ..........
 *  ..........
 *  ..........
 *
 * Time: 2.07 seconds, Memory: 20.00MB
 *
 * OK (80 tests, 124 assertions)
 * ```
 *
 *
 * Enable this reporter with `--ext option`
 *
 * ```
 * codecept run --ext DotReporter
 * ```
 *
 * Failures and Errors are printed by a standard Codeception reporter.
 * Use this extension as an example for building custom reporters.
 */
class DotReporter extends Extension
{
    protected ?CodeceptConsole $standardReporter = null;

    protected array $errors = [];

    protected array $failures = [];

    protected int $width = 10;

    protected int $currentPos = 0;

    public function _initialize(): void
    {
        $this->_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<string, string>
     */
    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('<error>F</error>');
    }

    public function error(FailEvent $event): void
    {
        $this->printChar('<error>E</error>');
    }

    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);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Extension;
use Codeception\Test\Descriptor;

/**
 * This extension demonstrates how you can implement console output of your own.
 * Recommended to be used for development purposes only.
 */
class SimpleReporter extends Extension
{
    public function _initialize(): void
    {
        $this->_reconfigure(['settings' => ['silent' => true]]); // turn off printing for everything else
    }

    /**
     * We are listening for events
     *
     * @var array<string, string>
     */
    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)');
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Extension;

use Codeception\Event\FailEvent;
use Codeception\Event\StepEvent;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Exception\ConfigurationException;
use Codeception\Exception\ExtensionException;
use Codeception\Extension;
use Codeception\Test\Descriptor;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\RotatingFileHandler;

use function class_exists;
use function function_exists;
use function str_replace;
use function ucfirst;

/**
 * Log suites/tests/steps using Monolog library.
 * Monolog should be installed additionally by Composer.
 *
 * ```
 * composer require monolog/monolog
 * ```
 *
 * Codeception's core/internal stuff is logged into `tests/_output/codeception.log`.
 * Test suites' steps are logged into `tests/_output/<test_full_name>-<rotation_date>.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<string, string>
     */
    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<string, int>
     */
    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());
    }
}
<?php

$autoloadFile = './vendor/codeception/codeception/autoload.php';
if ((!isset($argv) || (isset($argv) && !in_array('--no-redirect', $argv))) && file_exists('./vendor/autoload.php') && file_exists($autoloadFile) && __FILE__ != realpath($autoloadFile)) {
    //for global installation or phar file
    fwrite(
        STDERR,
        "\n==== Redirecting to Composer-installed version in vendor/codeception. You can skip this using --no-redirect ====\n"
    );

    if (file_exists('./vendor/codeception/codeception/app.php')) {
        //codeception v4+
        require './vendor/codeception/codeception/app.php';
    } else {
        //older version
        require $autoloadFile;
        //require package/bin instead of codecept to avoid printing hashbang line
        require './vendor/codeception/codeception/package/bin';
    }

    die;
} elseif (file_exists(__DIR__ . '/vendor/autoload.php')) {
    // for phar
    require_once __DIR__ . '/vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/../../autoload.php')) {
    //for composer
    require_once __DIR__ . '/../../autoload.php';
}
unset($autoloadFile);
if (isset($argv)) {
    $argv = array_values(array_diff($argv, ['--no-redirect']));
}
if (isset($_SERVER['argv'])) {
    $_SERVER['argv'] = array_values(array_diff($_SERVER['argv'], ['--no-redirect']));
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\Util\Shared\Asserts;

abstract class AbstractAsserts extends Module
{
    use Asserts {
        assertArrayHasKey as public;
        assertArrayNotHasKey as public;
        assertClassHasAttribute as public;
        assertClassHasStaticAttribute as public;
        assertClassNotHasAttribute as public;
        assertClassNotHasStaticAttribute as public;
        assertContains as public;
        assertContainsEquals as public;
        assertContainsOnly as public;
        assertContainsOnlyInstancesOf as public;
        assertCount as public;
        assertDirectoryDoesNotExist as public;
        assertDirectoryExists as public;
        assertDirectoryIsNotReadable as public;
        assertDirectoryIsNotWritable as public;
        assertDirectoryIsReadable as public;
        assertDirectoryIsWritable as public;
        assertDoesNotMatchRegularExpression as public;
        assertEmpty as public;
        assertEquals as public;
        assertEqualsCanonicalizing as public;
        assertEqualsIgnoringCase as public;
        assertEqualsWithDelta as public;
        assertFalse as public;
        assertFileDoesNotExist as public;
        assertFileEquals as public;
        assertFileEqualsCanonicalizing as public;
        assertFileEqualsIgnoringCase as public;
        assertFileExists as public;
        assertFileIsNotReadable as public;
        assertFileIsNotWritable as public;
        assertFileIsReadable as public;
        assertFileIsWritable as public;
        assertFileNotEquals as public;
        assertFileNotEqualsCanonicalizing as public;
        assertFileNotEqualsIgnoringCase as public;
        assertFileNotExists as public;
        assertFinite as public;
        assertGreaterOrEquals as public;
        assertGreaterThan as public;
        assertGreaterThanOrEqual as public;
        assertInfinite as public;
        assertInstanceOf as public;
        assertIsArray as public;
        assertIsBool as public;
        assertIsCallable as public;
        assertIsClosedResource as public;
        assertIsEmpty as public;
        assertIsFloat as public;
        assertIsInt as public;
        assertIsIterable as public;
        assertIsNotArray as public;
        assertIsNotBool as public;
        assertIsNotCallable as public;
        assertIsNotClosedResource as public;
        assertIsNotFloat as public;
        assertIsNotInt as public;
        assertIsNotIterable as public;
        assertIsNotNumeric as public;
        assertIsNotObject as public;
        assertIsNotReadable as public;
        assertIsNotResource as public;
        assertIsNotScalar as public;
        assertIsNotString as public;
        assertIsNotWritable as public;
        assertIsNumeric as public;
        assertIsObject as public;
        assertIsReadable as public;
        assertIsResource as public;
        assertIsScalar as public;
        assertIsString as public;
        assertIsWritable as public;
        assertJson as public;
        assertJsonFileEqualsJsonFile as public;
        assertJsonFileNotEqualsJsonFile as public;
        assertJsonStringEqualsJsonFile as public;
        assertJsonStringEqualsJsonString as public;
        assertJsonStringNotEqualsJsonFile as public;
        assertJsonStringNotEqualsJsonString as public;
        assertLessOrEquals as public;
        assertLessThan as public;
        assertLessThanOrEqual as public;
        assertMatchesRegularExpression as public;
        assertNan as public;
        assertNotContains as public;
        assertNotContainsEquals as public;
        assertNotContainsOnly as public;
        assertNotCount as public;
        assertNotEmpty as public;
        assertNotEquals as public;
        assertNotEqualsCanonicalizing as public;
        assertNotEqualsIgnoringCase as public;
        assertNotEqualsWithDelta as public;
        assertNotFalse as public;
        assertNotInstanceOf as public;
        assertNotNull as public;
        assertNotRegExp as public;
        assertNotSame as public;
        assertNotSameSize as public;
        assertNotTrue as public;
        assertNull as public;
        assertObjectHasAttribute as public;
        assertObjectNotHasAttribute as public;
        assertRegExp as public;
        assertSame as public;
        assertSameSize as public;
        assertStringContainsString as public;
        assertStringContainsStringIgnoringCase as public;
        assertStringEndsNotWith as public;
        assertStringEndsWith as public;
        assertStringEqualsFile as public;
        assertStringEqualsFileCanonicalizing as public;
        assertStringEqualsFileIgnoringCase as public;
        assertStringMatchesFormat as public;
        assertStringMatchesFormatFile as public;
        assertStringNotContainsString as public;
        assertStringNotContainsStringIgnoringCase as public;
        assertStringNotEqualsFile as public;
        assertStringNotEqualsFileCanonicalizing as public;
        assertStringNotEqualsFileIgnoringCase as public;
        assertStringNotMatchesFormat as public;
        assertStringNotMatchesFormatFile as public;
        assertStringStartsNotWith as public;
        assertStringStartsWith as public;
        assertThat as public;
        assertThatItsNot as public;
        assertTrue as public;
        assertXmlFileEqualsXmlFile as public;
        assertXmlFileNotEqualsXmlFile as public;
        assertXmlStringEqualsXmlFile as public;
        assertXmlStringEqualsXmlString as public;
        assertXmlStringNotEqualsXmlFile as public;
        assertXmlStringNotEqualsXmlString as public;
        fail as public;
        markTestIncomplete as public;
        markTestSkipped as public;
    }
}<?php

declare(strict_types=1);

namespace Codeception\Module;

/**
 * Special module for using asserts in your tests.
 */
class Asserts extends AbstractAsserts
{
    /**
     * Handles and checks throwables (Exceptions/Errors) called inside the callback function.
     * Either throwable class name or throwable instance should be provided.
     *
     * ```php
     * <?php
     * $I->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
     * <?php
     * // will check that throwable MyError is thrown with "Don't do bad things" message
     * $I->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
    }
}
<?php

//phpcs:disable Generic.Files.LineLength.TooLong
declare(strict_types=1);

namespace Codeception\Module;

use Closure;
use Codeception\Coverage\Subscriber\LocalServer;
use Codeception\Exception\ConnectionException;
use Codeception\Exception\ElementNotFound;
use Codeception\Exception\MalformedLocatorException;
use Codeception\Exception\ModuleConfigException;
use Codeception\Exception\ModuleException;
use Codeception\Exception\TestRuntimeException;
use Codeception\Lib\Interfaces\ConflictsWithModule;
use Codeception\Lib\Interfaces\ElementLocator;
use Codeception\Lib\Interfaces\MultiSession as MultiSessionInterface;
use Codeception\Lib\Interfaces\PageSourceSaver;
use Codeception\Lib\Interfaces\Remote as RemoteInterface;
use Codeception\Lib\Interfaces\RequiresPackage;
use Codeception\Lib\Interfaces\ScreenshotSaver;
use Codeception\Lib\Interfaces\SessionSnapshot;
use Codeception\Lib\Interfaces\Web as WebInterface;
use Codeception\Module as CodeceptionModule;
use Codeception\Constraint\Page as PageConstraint;
use Codeception\Constraint\WebDriver as WebDriverConstraint;
use Codeception\Constraint\WebDriverNot as WebDriverConstraintNot;
use Codeception\Test\Descriptor;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\TestInterface;
use Codeception\Util\ActionSequence;
use Codeception\Util\Locator;
use Codeception\Util\Uri;
use Exception;
use Facebook\WebDriver\Cookie;
use Facebook\WebDriver\Cookie as WebDriverCookie;
use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\InvalidElementStateException;
use Facebook\WebDriver\Exception\InvalidSelectorException;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnknownErrorException;
use Facebook\WebDriver\Interactions\WebDriverActions;
use Facebook\WebDriver\Remote\LocalFileDetector;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\RemoteWebElement;
use Facebook\WebDriver\Remote\UselessFileDetector;
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
use Facebook\WebDriver\WebDriver as WebDriverInterface;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverDimension;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverExpectedCondition;
use Facebook\WebDriver\WebDriverKeys;
use Facebook\WebDriver\WebDriverSearchContext;
use Facebook\WebDriver\WebDriverSelect;
use InvalidArgumentException;
use PHPUnit\Framework\AssertionFailedError as PHPUnitAssertionFailedError;
use PHPUnit\Framework\SelfDescribing;

/**
 * Run tests in real browsers using the W3C [WebDriver protocol](https://www.w3.org/TR/webdriver/).
 * There are multiple ways of running browser tests using WebDriver:
 *
 * ## Selenium (Recommended)
 *
 * * Java is required
 * * NodeJS is required
 *
 * The fastest way to get started is to [Install and launch Selenium using selenium-standalone NodeJS package](https://www.npmjs.com/package/selenium-standalone).
 *
 * Launch selenium standalone in separate console window:
 *
 * ```
 * selenium-standalone start
 * ```
 *
 * Update configuration in `acceptance.suite.yml`:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - WebDriver:
 *          url: 'http://localhost/'
 *          browser: chrome # 'chrome' or 'firefox'
 * ```
 *
 * ## Headless Chrome Browser
 *
 * To enable headless mode (launch tests without showing a window) for Chrome browser using Selenium use this config in `acceptance.suite.yml`:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - WebDriver:
 *          url: 'http://localhost/'
 *          browser: chrome
 *          capabilities:
 *             chromeOptions:
 *                args: ["--headless", "--disable-gpu"]
 * ```
 *
 * ## Headless Selenium in Docker
 *
 * Docker can ship Selenium Server with all its dependencies and browsers inside a single container.
 * Running tests inside Docker is as easy as pulling [official selenium image](https://github.com/SeleniumHQ/docker-selenium) and starting a container with Chrome:
 *
 * ```
 * docker run --net=host --shm-size 2g selenium/standalone-chrome
 * ```
 *
 * By using `--net=host` allow Selenium to access local websites.
 *
 * ## Local Chrome and/or Firefox
 *
 * Tests can be executed directly through ChromeDriver or GeckoDriver (for Firefox). Consider using this option if you don't plan to use Selenium.
 *
 * ### ChromeDriver
 *
 * * Download and install [ChromeDriver](https://sites.google.com/chromium.org/driver/downloads?authuser=0)
 * * Launch ChromeDriver in a separate console window: `chromedriver --url-base=/wd/hub`.
 *
 * Configuration in `acceptance.suite.yml`:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - WebDriver:
 *          browser: chrome
 *          url: 'http://localhost/'
 *          window_size: 2000x1000
 *          port: 9515
 *          capabilities:
 *              chromeOptions:
 *                  args: ["--headless", "--disable-gpu"] # Run Chrome in headless mode
 *                  prefs:
 *                      download.default_directory: "..."
 * ```
 * See here for additional [Chrome options](https://sites.google.com/a/chromium.org/chromedriver/capabilities)
 *
 *
 * ### GeckoDriver
 *
 * * [GeckoDriver](https://github.com/mozilla/geckodriver/releases) must be installed
 * * Start GeckoDriver in a separate console window: `geckodriver`.
 *
 * Configuration in `acceptance.suite.yml`:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - WebDriver:
 *          browser: firefox
 *          url: 'http://localhost/'
 *          window_size: 2000x1000
 *          path: ''
 *          capabilities:
 *              acceptInsecureCerts: true # allow self-signed certificates
 *              moz:firefoxOptions:
 *                  args: ["-headless"] # Run Firefox in headless mode
 *                  prefs:
 *                      intl.accept_languages: "de-AT" # Set HTTP-Header `Accept-Language: de-AT` for requests
 * ```
 * See here for [Firefox capabilities](https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities#List_of_capabilities)
 *
 * ## Cloud Testing
 *
 * Cloud Testing services can run your WebDriver tests in the cloud.
 * In case you want to test a local site or site behind a firewall
 * you should use a tunnel application provided by a service.
 *
 * ### SauceLabs
 *
 * 1. Create an account at [SauceLabs.com](https://saucelabs.com/) to get your username and access key
 * 2. In the module configuration use the format `username`:`access_key`@ondemand.saucelabs.com' for `host`
 * 3. Configure `platform` under `capabilities` to define the [Operating System](https://docs.saucelabs.com/basics/platform-configurator/)
 * 4. run a tunnel app if your site can't be accessed from Internet
 *
 * ```yaml
 *     modules:
 *        enabled:
 *           - WebDriver:
 *              url: http://mysite.com
 *              host: '<username>:<access key>@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: '<username>:<access key>@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: '<key>:<secret>@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 `<div id="foo">`
 * * `['name' => 'foo']` matches `<div name="foo">`
 * * `['css' => 'input[type=input][value=foo]']` matches `<input type="input" value="foo">`
 * * `['xpath' => "//input[@type='submit'][contains(@value, 'foo')]"]` matches `<input type="submit" value="foobar">`
 * * `['link' => 'Click here']` matches `<a href="google.com">Click here</a>`
 * * `['class' => 'foo']` matches `<div class="foo">`
 *
 * 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
     * <?php // in helper
     * public function _before(TestInterface $test)
     * {
     *     $this->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
     * <?php // inside Helper\Acceptance
     * public function _before(TestInterface $test)
     * {
     *      $name = $test->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
     * <?php
     * // inside a Helper
     * $this->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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<string, string> $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
     * <?php
     * $I->seeInPageSource('<link rel="apple-touch-icon"');
     * ```
     */
    public function seeInPageSource(string $text): void
    {
        $this->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
     * <?php
     * $module = $this->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
     * <?php
     * $this->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
     * <?php
     * $this->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
     * <?php
     * $I->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
     * <?php
     * // activate input element
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->submitForm('#login', [
     *     'login' => 'davert',
     *     'password' => '123456'
     * ]);
     * // or
     * $I->submitForm('#login', [
     *     'login' => 'davert',
     *     'password' => '123456'
     * ], 'submitButtonName');
     *
     * ```
     *
     * For example, given this sample "Sign Up" form:
     *
     * ``` html
     * <form action="/sign_up">
     *     Login:
     *     <input type="text" name="user[login]" /><br/>
     *     Password:
     *     <input type="password" name="user[password]" /><br/>
     *     Do you agree to our terms?
     *     <input type="checkbox" name="user[agree]" /><br/>
     *     Select pricing plan:
     *     <select name="plan">
     *         <option value="1">Free</option>
     *         <option value="2" selected="selected">Paid</option>
     *     </select>
     *     <input type="submit" name="submitButton" value="Submit" />
     * </form>
     * ```
     *
     * You could write the following to submit it:
     *
     * ``` php
     * <?php
     * $I->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
     * <?php
     * $form = [
     *      'field1' => '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
     * <?php
     * $I->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
     * <input type="submit" name="submitButton" value="Submit" />
     * ```
     *
     * `$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
     * <?php
     * use \Facebook\WebDriver\WebDriverElement
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <input type="button" value="Open window" onclick="window.open('https://example.com', 'another_window')">
     * ```
     *
     * ``` php
     * <?php
     * $I->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
     * <?php
     * $I->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
     * <iframe name="another_frame" id="fr1" src="https://example.com">
     *
     * ```
     *
     * ``` php
     * <?php
     * # switch to iframe by name
     * $I->switchToIFrame("another_frame");
     * # switch to iframe by CSS or XPath
     * $I->switchToIFrame("#fr1");
     * # switch to parent page
     * $I->switchToIFrame();
     *
     * ```
     *
     * @param string|null $locator (name, CSS or XPath)
     */
    public function switchToIFrame(string $locator = null): void
    {
        $this->findAndSwitchToFrame($locator, 'iframe');
    }

    /**
     * Switch to another frame on the page.
     *
     * Example:
     * ``` html
     * <frame name="another_frame" id="fr1" src="https://example.com">
     *
     * ```
     *
     * ``` php
     * <?php
     * # switch to frame by name
     * $I->switchToFrame("another_frame");
     * # switch to frame by CSS or XPath
     * $I->switchToFrame("#fr1");
     * # switch to parent page
     * $I->switchToFrame();
     *
     * ```
     *
     * @param string|null $locator (name, CSS or XPath)
     */
    public function switchToFrame(string $locator = null): void
    {
        $this->findAndSwitchToFrame($locator);
    }

    private function findAndSwitchToFrame(string $locator = null, string $tag = 'frame'): void
    {
        if ($locator === null) {
            $this->webDriver->switchTo()->defaultContent();
            return;
        }

        $els = null;
        try {
            $els = $this->_findElements("{$tag}[name='{$locator}']");
        } catch (Exception $e) {
            $this->debug('Failed to find locator by name: ' . $e->getMessage());
        }

        if (!isset($els) || !is_array($els) || $els === []) {
            $this->debug(ucfirst($tag) . ' was not found by name, locating ' . $tag . ' by CSS or XPath');
            $els = $this->_findElements($locator);
        }

        if ($els === []) {
            throw new ElementNotFound($locator, ucfirst($tag));
        }

        $this->webDriver->switchTo()->frame($els[0]);
    }

    /**
     * Executes JavaScript and waits up to $timeout seconds for it to return true.
     *
     * In this example we will wait up to 60 seconds for all jQuery AJAX requests to finish.
     *
     * ``` php
     * <?php
     * $I->waitForJS("return $.active == 0;", 60);
     * ```
     *
     * @param int $timeout seconds
     */
    public function waitForJS(string $script, int $timeout = 5): void
    {
        $condition = fn($wd) => $wd->executeScript($script);
        $message = sprintf(
            "Waited for %d secs but script %s still doesn't evaluate to true",
            $timeout,
            Locator::humanReadableString($script)
        );
        $this->webDriver->wait($timeout)->until($condition, $message);
    }

    /**
     * Executes custom JavaScript.
     *
     * This example uses jQuery to get a value and assigns that value to a PHP variable:
     *
     * ```php
     * <?php
     * $myVar = $I->executeJS('return $("#myField").val()');
     *
     * // additional arguments can be passed as array
     * // Example shows `Hello World` alert:
     * $I->executeJS("window.alert(arguments[0])", ['Hello world']);
     * ```
     *
     * @param array $arguments
     * @return mixed
     */
    public function executeJS(string $script, array $arguments = [])
    {
        return $this->webDriver->executeScript($script, $arguments);
    }

    /**
     * Executes asynchronous JavaScript.
     * A callback should be executed by JavaScript to exit from a script.
     * Callback is passed as a last element in `arguments` array.
     * Additional arguments can be passed as array in second parameter.
     *
     * ```js
     * // wait for 1200 milliseconds my running `setTimeout`
     * * $I->executeAsyncJS('setTimeout(arguments[0], 1200)');
     *
     * $seconds = 1200; // or seconds are passed as argument
     * $I->executeAsyncJS('setTimeout(arguments[1], arguments[0])', [$seconds]);
     * ```
     *
     * @param array $arguments
     * @return mixed
     */
    public function executeAsyncJS(string $script, array $arguments = [])
    {
        return $this->webDriver->executeAsyncScript($script, $arguments);
    }

    /**
     * Maximizes the current window.
     */
    public function maximizeWindow(): void
    {
        $this->webDriver->manage()->window()->maximize();
    }

    /**
     * Performs a simple mouse drag-and-drop operation.
     *
     * ``` php
     * <?php
     * $I->dragAndDrop('#drag', '#drop');
     * ```
     *
     * @param string|array|WebDriverBy $source (CSS ID or XPath)
     * @param string|array|WebDriverBy $target (CSS ID or XPath)
     */
    public function dragAndDrop($source, $target): void
    {
        $sourceNodes = $this->matchFirstOrFail($this->getBaseElement(), $source);
        $targetNodes = $this->matchFirstOrFail($this->getBaseElement(), $target);

        $action = new WebDriverActions($this->webDriver);
        $action->dragAndDrop($sourceNodes, $targetNodes)->perform();
    }

    /**
     * Move mouse over the first element matched by the given locator.
     * If the first parameter null then the page is used.
     * If the second and third parameters are given,
     * then the mouse is moved to an offset of the element's top-left corner.
     * Otherwise, the mouse is moved to the center of the element.
     *
     * ``` php
     * <?php
     * $I->moveMouseOver(['css' => '.checkout']);
     * $I->moveMouseOver(null, 20, 50);
     * $I->moveMouseOver(['css' => '.checkout'], 20, 50);
     * ```
     *
     * @param null|string|array|WebDriverBy $cssOrXPath css or xpath of the web element
     * @throws ElementNotFound
     */
    public function moveMouseOver($cssOrXPath = null, int $offsetX = null, int $offsetY = null): void
    {
        $where = null;
        if (null !== $cssOrXPath) {
            $el = $this->matchFirstOrFail($this->getBaseElement(), $cssOrXPath);
            $where = $el->getCoordinates();
        }

        $this->webDriver->getMouse()->mouseMove($where, $offsetX, $offsetY);
    }

    /**
     * Performs click with the left mouse button on an element.
     * If the first parameter `null` then the offset is relative to the actual mouse position.
     * If the second and third parameters are given,
     * then the mouse is moved to an offset of the element's top-left corner.
     * Otherwise, the mouse is moved to the center of the element.
     *
     * ``` php
     * <?php
     * $I->clickWithLeftButton(['css' => '.checkout']);
     * $I->clickWithLeftButton(null, 20, 50);
     * $I->clickWithLeftButton(['css' => '.checkout'], 20, 50);
     * ```
     *
     * @param null|string|array|WebDriverBy $cssOrXPath css or xpath of the web element (body by default).
     *
     * @throws ElementNotFound
     */
    public function clickWithLeftButton($cssOrXPath = null, int $offsetX = null, int $offsetY = null): void
    {
        $this->moveMouseOver($cssOrXPath, $offsetX, $offsetY);
        $this->webDriver->getMouse()->click();
    }

    /**
     * Performs contextual click with the right mouse button on an element.
     * If the first parameter `null` then the offset is relative to the actual mouse position.
     * If the second and third parameters are given,
     * then the mouse is moved to an offset of the element's top-left corner.
     * Otherwise, the mouse is moved to the center of the element.
     *
     * ``` php
     * <?php
     * $I->clickWithRightButton(['css' => '.checkout']);
     * $I->clickWithRightButton(null, 20, 50);
     * $I->clickWithRightButton(['css' => '.checkout'], 20, 50);
     * ```
     *
     * @param null|string|array|WebDriverBy $cssOrXPath css or xpath of the web element (body by default).
     * @throws ElementNotFound
     */
    public function clickWithRightButton($cssOrXPath = null, int $offsetX = null, int $offsetY = null): void
    {
        $this->moveMouseOver($cssOrXPath, $offsetX, $offsetY);
        $this->webDriver->getMouse()->contextClick();
    }


    /**
     * Performs a double click on an element matched by CSS or XPath.
     *
     * @param string|array|WebDriverBy $cssOrXPath
     * @throws ElementNotFound
     */
    public function doubleClick($cssOrXPath): void
    {
        $el = $this->matchFirstOrFail($this->getBaseElement(), $cssOrXPath);
        $this->webDriver->getMouse()->doubleClick($el->getCoordinates());
    }

    /**
     * @param string|array|WebDriverBy $selector
     * @return WebDriverElement[]
     */
    protected function match(WebDriverSearchContext $page, $selector, bool $throwMalformed = true): array
    {
        if (is_array($selector)) {
            try {
                return $page->findElements($this->getStrictLocator($selector));
            } catch (InvalidSelectorException $exception) {
                throw new MalformedLocatorException(key($selector) . ' => ' . reset($selector), "Strict locator");
            } catch (InvalidElementStateException $exception) {
                if ($this->isPhantom() && $exception->getResults()['status'] == 12) {
                    throw new MalformedLocatorException(
                        key($selector) . ' => ' . reset($selector),
                        "Strict locator " . $exception->getCode()
                    );
                }
            }
        }

        if ($selector instanceof WebDriverBy) {
            try {
                return $page->findElements($selector);
            } catch (InvalidSelectorException $exception) {
                throw new MalformedLocatorException(
                    sprintf(
                        "WebDriverBy::%s('%s')",
                        $selector->getMechanism(),
                        $selector->getValue()
                    ),
                    'WebDriver'
                );
            }
        }

        $isValidLocator = false;
        $nodes = [];
        try {
            if (Locator::isID($selector)) {
                $isValidLocator = true;
                $nodes = $page->findElements(WebDriverBy::id(substr($selector, 1)));
            }

            if (Locator::isClass($selector)) {
                $isValidLocator = true;
                $nodes = $page->findElements(WebDriverBy::className(substr($selector, 1)));
            }

            if (empty($nodes) && Locator::isCSS($selector)) {
                $isValidLocator = true;
                try {
                    $nodes = $page->findElements(WebDriverBy::cssSelector($selector));
                } catch (InvalidElementStateException $exception) {
                    $nodes = $page->findElements(WebDriverBy::linkText($selector));
                }
            }

            if (empty($nodes) && Locator::isXPath($selector)) {
                $isValidLocator = true;
                $nodes = $page->findElements(WebDriverBy::xpath($selector));
            }
        } catch (InvalidSelectorException $exception) {
            throw new MalformedLocatorException($selector);
        }

        if (!$isValidLocator && $throwMalformed) {
            throw new MalformedLocatorException($selector);
        }

        return $nodes;
    }

    protected function getStrictLocator(array $by): WebDriverBy
    {
        $type = key($by);
        $locator = $by[$type];
        switch ($type) {
            case 'id':
                return WebDriverBy::id($locator);
            case 'name':
                return WebDriverBy::name($locator);
            case 'css':
                return WebDriverBy::cssSelector($locator);
            case 'xpath':
                return WebDriverBy::xpath($locator);
            case 'link':
                return WebDriverBy::linkText($locator);
            case 'class':
                return WebDriverBy::className($locator);
            default:
                throw new MalformedLocatorException(
                    "{$type} => {$locator}",
                    "Strict locator can be either xpath, css, id, link, class, name: "
                );
        }
    }

    /**
     * @param string|array|WebDriverBy $selector
     * @throws ElementNotFound
     */
    protected function matchFirstOrFail(WebDriverSearchContext $page, $selector): WebDriverElement
    {
        $this->enableImplicitWait();
        $els = $this->match($page, $selector);
        $this->disableImplicitWait();
        if ($els === []) {
            throw new ElementNotFound($selector, "CSS or XPath");
        }

        return reset($els);
    }

    /**
     * Presses the given key on the given element.
     * To specify a character and modifier (e.g. <kbd>Ctrl</kbd>, Alt, Shift, Meta), pass an array for `$char` with
     * the modifier as the first element and the character as the second.
     * For special keys, use the constants from [`Facebook\WebDriver\WebDriverKeys`](https://github.com/php-webdriver/php-webdriver/blob/main/lib/WebDriverKeys.php).
     *
     * ``` php
     * <?php
     * // <input id="page" value="old" />
     * $I->pressKey('#page','a'); // => olda
     * $I->pressKey('#page',array('ctrl','a'),'new'); //=> new
     * $I->pressKey('#page',array('shift','111'),'1','x'); //=> old!!!1x
     * $I->pressKey('descendant-or-self::*[@id='page']','u'); //=> oldu
     * $I->pressKey('#name', array('ctrl', 'a'), \Facebook\WebDriver\WebDriverKeys::DELETE); //=>''
     * ```
     *
     * @param string|array|WebDriverBy $element
     * @param array<string|string[]>$chars Can be char or array with modifier. You can provide several chars.
     * @throws ElementNotFound
     */
    public function pressKey($element, ...$chars): void
    {
        $el = $this->matchFirstOrFail($this->getBaseElement(), $element);
        $keys = [];
        foreach ($chars as $char) {
            $keys[] = $this->convertKeyModifier($char);
        }

        $el->sendKeys($keys);
    }

    /**
     * @param string|string[] $char
     * @return string|string[]
     */
    protected function convertKeyModifier($char)
    {
        if (is_string($char)) {
            return $char;
        }

        if (!isset($char[1])) {
            return $char;
        }

        [$modifier, $key] = $char;

        switch ($modifier) {
            case 'ctrl':
            case 'control':
                return [WebDriverKeys::CONTROL, $key];
            case 'alt':
                return [WebDriverKeys::ALT, $key];
            case 'shift':
                return [WebDriverKeys::SHIFT, $key];
            case 'meta':
                return [WebDriverKeys::META, $key];
        }

        return $char;
    }

    protected function assertNodesContain($text, $nodes, $selector = null): void
    {
        $this->assertNodeConstraint($nodes, new WebDriverConstraint($text, $this->_getCurrentUri()), $selector);
    }

    protected function assertNodesNotContain($text, $nodes, $selector = null): void
    {
        $this->assertNodeConstraint($nodes, new WebDriverConstraintNot($text, $this->_getCurrentUri()), $selector);
    }

    protected function assertNodeConstraint($nodes, WebDriverConstraint $constraint, $selector = null): void
    {
        $message = $selector;
        if (is_array($selector)) {
            $type = key($selector);
            $locator = $selector[$type];
            $message = $type . ':' . $locator;
        }

        $this->assertThat($nodes, $constraint, $message);
    }

    protected function assertPageContains($needle, string $message = ''): void
    {
        $this->assertThat(
            htmlspecialchars_decode($this->getVisibleText()),
            new PageConstraint($needle, $this->_getCurrentUri()),
            $message
        );
    }

    protected function assertPageNotContains($needle, string $message = ''): void
    {
        $this->assertThatItsNot(
            htmlspecialchars_decode($this->getVisibleText()),
            new PageConstraint($needle, $this->_getCurrentUri()),
            $message
        );
    }

    protected function assertPageSourceContains($needle, string $message = ''): void
    {
        $this->assertThat(
            $this->webDriver->getPageSource(),
            new PageConstraint($needle, $this->_getCurrentUri()),
            $message
        );
    }

    protected function assertPageSourceNotContains($needle, string $message = ''): void
    {
        $this->assertThatItsNot(
            $this->webDriver->getPageSource(),
            new PageConstraint($needle, $this->_getCurrentUri()),
            $message
        );
    }

    /**
     * Append the given text to the given element.
     * Can also add a selection to a select box.
     *
     * ``` php
     * <?php
     * $I->appendField('#mySelectbox', 'SelectValue');
     * $I->appendField('#myTextField', 'appended');
     * ```
     *
     * @param string|array|WebDriverBy $field
     * @param string $value
     * @throws ElementNotFound
     */
    public function appendField($field, string $value): void
    {
        $el = $this->findField($field);

        switch ($el->getTagName()) {
            //Multiple select
            case "select":
                $matched = false;
                $wdSelect = new WebDriverSelect($el);
                try {
                    $wdSelect->selectByVisibleText($value);
                    $matched = true;
                } catch (NoSuchElementException $e) {
                    // exception treated at the end
                }

                try {
                    $wdSelect->selectByValue($value);
                    $matched = true;
                } catch (NoSuchElementException $e) {
                    // exception treated at the end
                }

                if ($matched) {
                    return;
                }

                throw new ElementNotFound(
                    json_encode($value, JSON_THROW_ON_ERROR),
                    "Option inside {$field} matched by name or value"
                );
            case "textarea":
                $el->sendKeys($value);
                return;
            case "div": //allows for content editable divs
                $el->sendKeys(WebDriverKeys::END);
                $el->sendKeys($value);
                return;
            //Text, Checkbox, Radio
            case "input":
                $type = $el->getAttribute('type');

                if ($type == 'checkbox') {
                    //Find by value or css,id,xpath
                    $field = $this->findCheckable($this->getBaseElement(), $value, true);
                    if (!$field) {
                        throw new ElementNotFound($value, "Checkbox or Radio by Label or CSS or XPath");
                    }

                    if ($field->isSelected()) {
                        return;
                    }

                    $field->click();
                    return;
                } elseif ($type == 'radio') {
                    $this->selectOption($field, $value);
                    return;
                }

                $el->sendKeys($value);
                return;
        }

        throw new ElementNotFound($field, "Field by name, label, CSS or XPath");
    }

    /**
     * @param string|array|WebDriverBy $selector
     */
    protected function matchVisible($selector): array
    {
        $els = $this->match($this->getBaseElement(), $selector);
        return array_filter(
            $els,
            fn(WebDriverElement $el): bool => $el->isDisplayed()
        );
    }

    /**
     * @param string|array|WebDriverBy $selector
     * @throws InvalidArgumentException
     */
    protected function getLocator($selector): WebDriverBy
    {
        if ($selector instanceof WebDriverBy) {
            return $selector;
        }

        if (is_array($selector)) {
            return $this->getStrictLocator($selector);
        }

        if (Locator::isID($selector)) {
            return WebDriverBy::id(substr($selector, 1));
        }

        if (Locator::isCSS($selector)) {
            return WebDriverBy::cssSelector($selector);
        }

        if (Locator::isXPath($selector)) {
            return WebDriverBy::xpath($selector);
        }

        throw new InvalidArgumentException("Only CSS or XPath allowed");
    }

    public function saveSessionSnapshot($name)
    {
        $this->sessionSnapshots[$name] = [];

        foreach ($this->webDriver->manage()->getCookies() as $cookie) {
            if (in_array(trim($cookie['name']), [LocalServer::COVERAGE_COOKIE, LocalServer::COVERAGE_COOKIE_ERROR])) {
                continue;
            }

            if ($this->cookieDomainMatchesConfigUrl($cookie)) {
                $this->sessionSnapshots[$name][] = $cookie;
            }
        }

        $this->debugSection('Snapshot', sprintf('Saved "%s" session snapshot', $name));
    }

    public function loadSessionSnapshot($name, bool $showDebug = true): bool
    {
        if (!isset($this->sessionSnapshots[$name])) {
            return false;
        }

        foreach ($this->webDriver->manage()->getCookies() as $cookie) {
            if (in_array(trim($cookie['name']), [LocalServer::COVERAGE_COOKIE, LocalServer::COVERAGE_COOKIE_ERROR])) {
                continue;
            }

            $this->webDriver->manage()->deleteCookieNamed($cookie['name']);
        }

        foreach ($this->sessionSnapshots[$name] as $cookie) {
            $this->setCookie($cookie['name'], $cookie['value'], (array)$cookie, false);
        }

        if ($showDebug) {
            $this->debugCookies();
        }
        $this->debugSection('Snapshot', sprintf('Restored "%s" session snapshot', $name));
        return true;
    }

    public function deleteSessionSnapshot($name)
    {
        if (isset($this->sessionSnapshots[$name])) {
            unset($this->sessionSnapshots[$name]);
        }

        $this->debugSection('Snapshot', sprintf('Deleted "%s" session snapshot', $name));
    }

    /**
     * Check if the cookie domain matches the config URL.
     *
     * Taken from Guzzle\Cookie\SetCookie
     */
    private function cookieDomainMatchesConfigUrl(array|WebDriverCookie $cookie): bool
    {
        if (!isset($cookie['domain'])) {
            return true;
        }

        $domain = parse_url($this->config['url'], PHP_URL_HOST);

        // Remove the leading '.' as per spec in RFC 6265.
        // https://tools.ietf.org/html/rfc6265#section-5.2.3
        $cookieDomain = ltrim($cookie['domain'], '.');
        // Domain not set or exact match.
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
            return true;
        }

        // Matching the subdomain according to RFC 6265.
        // https://tools.ietf.org/html/rfc6265#section-5.1.3
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
            return false;
        }

        return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain);
    }

    protected function isPhantom(): bool
    {
        return strpos($this->config['browser'], 'phantom') === 0;
    }

    /**
     * Move to the middle of the given element matched by the given locator.
     * Extra shift, calculated from the top-left corner of the element,
     * can be set by passing $offsetX and $offsetY parameters.
     *
     * ``` php
     * <?php
     * $I->scrollTo(['css' => '.checkout'], 20, 50);
     * ```
     *
     * @param string|array|WebDriverBy $selector
     */
    public function scrollTo($selector, int $offsetX = null, int $offsetY = null): void
    {
        $el = $this->matchFirstOrFail($this->getBaseElement(), $selector);
        $x = $el->getLocation()->getX() + $offsetX;
        $y = $el->getLocation()->getY() + $offsetY;
        $this->webDriver->executeScript(sprintf('window.scrollTo(%d, %d)', $x, $y));
    }

    /**
     * Opens a new browser tab and switches to it.
     *
     * ```php
     * <?php
     * $I->openNewTab();
     * ```
     * The tab is opened with JavaScript's `window.open()`, which means:
     * * Some ad-blockers might restrict it.
     * * The sessionStorage is copied to the new tab (contrary to a tab that was manually opened by the user)
     */
    public function openNewTab(): void
    {
        $this->executeJS("window.open('about:blank','_blank');");
        $this->switchToNextTab();
    }

    /**
     * Checks current number of opened tabs
     *
     * ```php
     * <?php
     * $I->seeNumberOfTabs(2);
     * ```
     */
    public function seeNumberOfTabs(int $number): void
    {
        $this->assertCount($number, $this->webDriver->getWindowHandles());
    }

    /**
     * Closes current browser tab and switches to previous active tab.
     *
     * ```php
     * <?php
     * $I->closeTab();
     * ```
     */
    public function closeTab(): void
    {
        $currentTab = $this->webDriver->getWindowHandle();
        $prevTab = $this->getRelativeTabHandle(-1);
        if ($prevTab === $currentTab) {
            throw new ModuleException($this, 'Will not close the last open tab');
        }
        $this->webDriver->close();
        $this->webDriver->switchTo()->window($prevTab);
    }

    /**
     * Switches to next browser tab.
     * An offset can be specified.
     *
     * ```php
     * <?php
     * // switch to next tab
     * $I->switchToNextTab();
     * // switch to 2nd next tab
     * $I->switchToNextTab(2);
     * ```
     */
    public function switchToNextTab(int $offset = 1): void
    {
        $tab = $this->getRelativeTabHandle($offset);
        $this->webDriver->switchTo()->window($tab);
    }

    /**
     * Switches to previous browser tab.
     * An offset can be specified.
     *
     * ```php
     * <?php
     * // switch to previous tab
     * $I->switchToPreviousTab();
     * // switch to 2nd previous tab
     * $I->switchToPreviousTab(2);
     * ```
     */
    public function switchToPreviousTab(int $offset = 1): void
    {
        $this->switchToNextTab(0 - $offset);
    }

    protected function getRelativeTabHandle($offset)
    {
        if ($this->isPhantom()) {
            throw new ModuleException($this, "PhantomJS doesn't support tab actions");
        }

        $handle = $this->webDriver->getWindowHandle();
        $handles = $this->webDriver->getWindowHandles();
        $currentHandleIdx = array_search($handle, $handles);
        $newHandleIdx = ($currentHandleIdx + $offset) % count($handles);
        if ($newHandleIdx < 0) {
            $newHandleIdx = count($handles) + $newHandleIdx;
        }
        return $handles[$newHandleIdx];
    }

    /**
     * Waits for element and runs a sequence of actions inside its context.
     * Actions can be defined with array, callback, or `Codeception\Util\ActionSequence` instance.
     *
     * Actions as array are recommended for simple to combine "waitForElement" with assertions;
     * `waitForElement($el)` and `see('text', $el)` can be simplified to:
     *
     * ```php
     * <?php
     * $I->performOn($el, ['see' => 'text']);
     * ```
     *
     * List of actions can be pragmatically build using `Codeception\Util\ActionSequence`:
     *
     * ```php
     * <?php
     * $I->performOn('.model', ActionSequence::build()
     *     ->see('Warning')
     *     ->see('Are you sure you want to delete this?')
     *     ->click('Yes')
     * );
     * ```
     *
     * Actions executed from array or ActionSequence will print debug output for actions, and adds an action name to
     * exception on failure.
     *
     * Whenever you need to define more actions a callback can be used. A WebDriver module is passed for argument:
     *
     * ```php
     * <?php
     * $I->performOn('.rememberMe', function (WebDriver $I) {
     *      $I->see('Remember me next time');
     *      $I->seeElement('#LoginForm_rememberMe');
     *      $I->dontSee('Login');
     * });
     * ```
     *
     * In 3rd argument you can set number a seconds to wait for element to appear
     *
     * @param string|array|WebDriverBy $element
     * @param callable|array|\Codeception\Util\ActionSequence $actions
     */
    public function performOn($element, $actions, int $timeout = 10): void
    {
        $this->waitForElement($element, $timeout);
        $this->setBaseElement($element);
        $this->debugSection('InnerText', $this->getBaseElement()->getText());

        if (is_callable($actions)) {
            $actions($this);
            $this->setBaseElement();
            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->setBaseElement();
    }

    /**
     * @param string|array|WebDriverBy $element
     */
    protected function setBaseElement($element = null): void
    {
        if ($element === null) {
            $this->baseElement = $this->webDriver;
            return;
        }

        $this->baseElement = $this->matchFirstOrFail($this->webDriver, $element);
    }

    protected function enableImplicitWait(): void
    {
        if (!$this->config['wait']) {
            return;
        }

        $this->webDriver->manage()->timeouts()->implicitlyWait($this->config['wait']);
    }

    protected function disableImplicitWait(): void
    {
        if (!$this->config['wait']) {
            return;
        }

        $this->webDriver->manage()->timeouts()->implicitlyWait(0);
    }

    /**
     * From symfony/dom-crawler
     *
     * Converts string for XPath expressions.
     *
     * Escaped characters are: quotes (") and apostrophe (').
     *
     *  Examples:
     *
     *     echo self::xPathLiteral('foo " bar');
     *     //prints 'foo " bar'
     *
     *     echo self::xPathLiteral("foo ' bar");
     *     //prints "foo ' bar"
     *
     *     echo self::xPathLiteral('a\'b"c');
     *     //prints concat('a', "'", 'b"c')
     *
     * @return string Converted string
     */
    private static function xPathLiteral($s): string
    {
        if (false === strpos($s, "'")) {
            return sprintf("'%s'", $s);
        }

        if (false === strpos($s, '"')) {
            return sprintf('"%s"', $s);
        }

        $string = $s;
        $parts = [];
        while (true) {
            if (false !== $pos = strpos($string, "'")) {
                $parts[] = sprintf("'%s'", substr($string, 0, $pos));
                $parts[] = "\"'\"";
                $string = substr($string, $pos + 1);
            } else {
                $parts[] = "'{$string}'";
                break;
            }
        }

        return sprintf('concat(%s)', implode(', ', $parts));
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Constraint;

use Codeception\Exception\ElementNotFound;
use Codeception\Lib\Console\Message;
use Codeception\Util\Locator;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverElement;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;

use function count;
use function htmlspecialchars_decode;
use function strpos;

class WebDriver extends Page
{
    /**
     * @param WebDriverElement[] $nodes
     * @return bool
     */
    protected function matches($nodes): bool
    {
        if (count($nodes) === 0) {
            return false;
        }
        if ($this->string === '') {
            return true;
        }

        foreach ($nodes as $node) {
            if (!$node->isDisplayed()) {
                continue;
            }
            if (parent::matches(htmlspecialchars_decode($node->getText(), ENT_QUOTES | ENT_SUBSTITUTE))) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param WebDriverElement[] $nodes
     * @param string|array|WebDriverBy $selector
     * @param ComparisonFailure|null $comparisonFailure
     */
    protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null): void
    {
        if (count($nodes) === 0) {
            throw new ElementNotFound($selector, 'Element located either by name, CSS or XPath');
        }

        $output = "Failed asserting that any element by " . Locator::humanReadableString($selector);
        $output .= ' ' . $this->uriMessage('on page');

        if (count($nodes) < 5) {
            $output .= "\nElements: ";
            $output .= $this->nodesList($nodes);
        } else {
            $message = new Message("[total %s elements]");
            $output .= $message->with(count($nodes));
        }
        $output .= "\ncontains text '" . $this->string . "'";

        throw new ExpectationFailedException(
            $output,
            $comparisonFailure
        );
    }

    /**
     * @param WebDriverElement[] $nodes
     * @return string
     */
    protected function failureDescription($nodes): string
    {
        $desc = '';
        foreach ($nodes as $node) {
            $desc .= parent::failureDescription($node->getText());
        }
        return $desc;
    }

    /**
     * @param WebDriverElement[] $nodes
     * @param string|null $contains
     * @return string
     */
    protected function nodesList(array $nodes, string $contains = null): string
    {
        $output = "";
        foreach ($nodes as $node) {
            if ($contains && strpos($node->getText(), $contains) === false) {
                continue;
            }
            $message = new Message("\n+ <%s> %s");
            $output .= $message->with($node->getTagName(), $node->getText());
        }
        return $output;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Constraint;

use Codeception\Util\Locator;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverElement;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;

use function is_string;
use function strpos;

class WebDriverNot extends WebDriver
{
    protected function matches($nodes): bool
    {
        return !parent::matches($nodes);
    }

    /**
     * @param WebDriverElement[] $nodes
     * @param string|array|WebDriverBy $selector
     * @param ComparisonFailure|null $comparisonFailure
     */
    protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null): void
    {
        if (!is_string($selector) || strpos($selector, "'") === false) {
            $selector = Locator::humanReadableString($selector);
        }
        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\Exception;

use RuntimeException;

class ConnectionException extends RuntimeException
{
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Configuration;
use Codeception\Module;
use Codeception\PHPUnit\TestCase;
use Codeception\TestInterface;
use Codeception\Util\FileSystem as Util;
use PHPUnit\Framework\AssertionFailedError;
use Symfony\Component\Finder\Finder;

/**
 * Module for testing local filesystem.
 * Fork it to extend the module for FTP, Amazon S3, others.
 *
 * ## Status
 *
 * * Maintainer: **davert**
 * * Stability: **stable**
 * * Contact: codecept@davert.mail.ua
 *
 * Module was developed to test Codeception itself.
 */
class Filesystem extends Module
{
    protected string $file = '';

    protected string $filePath = '';

    protected string $path = '';

    public function _before(TestInterface $test): void
    {
        $this->path = Configuration::projectDir();
    }

    /**
     * Enters a directory In local filesystem.
     * Project root directory is used by default
     */
    public function amInPath(string $path): void
    {
        $this->path = $this->absolutizePath($path) . DIRECTORY_SEPARATOR;
        if (!file_exists($this->path)) {
            TestCase::fail('directory not found');
        }
        chdir($this->path);
        $this->debug('Moved to ' . getcwd());
    }

    protected function absolutizePath(string $path): string
    {
        // *nix way
        if (str_starts_with($path, '/')) {
            return $path;
        }

        // windows
        if (strpos($path, ':\\') === 1) {
            return $path;
        }

        return $this->path . $path;
    }

    /**
     * Opens a file and stores it's content.
     *
     * Usage:
     *
     * ``` php
     * <?php
     * $I->openFile('composer.json');
     * $I->seeInThisFile('codeception/codeception');
     * ```
     */
    public function openFile(string $filename): void
    {
        $absolutePath = $this->absolutizePath($filename);
        if (!file_exists($absolutePath)) {
            TestCase::fail('file not found');
        }
        $this->file = file_get_contents($absolutePath);
        $this->filePath = $filename;
    }

    /**
     * Deletes a file
     *
     * ``` php
     * <?php
     * $I->deleteFile('composer.lock');
     * ```
     */
    public function deleteFile(string $filename): void
    {
        $absolutePath = $this->absolutizePath($filename);
        if (!file_exists($absolutePath)) {
            TestCase::fail('file not found');
        }

        unlink($absolutePath);
    }

    /**
     * Deletes directory with all subdirectories
     *
     * ``` php
     * <?php
     * $I->deleteDir('vendor');
     * ```
     */
    public function deleteDir(string $dirname): void
    {
        $dir = $this->absolutizePath($dirname);
        Util::deleteDir($dir);
    }

    /**
     * Copies directory with all contents
     *
     * ``` php
     * <?php
     * $I->copyDir('vendor','old_vendor');
     * ```
     */
    public function copyDir(string $src, string $dst): void
    {
        Util::copyDir($src, $dst);
    }

    /**
     * Checks If opened file has `text` in it.
     *
     * Usage:
     *
     * ``` php
     * <?php
     * $I->openFile('composer.json');
     * $I->seeInThisFile('codeception/codeception');
     * ```
     */
    public function seeInThisFile(string $text): void
    {
        $this->assertStringContainsString($text, $this->file, "No text '{$text}' in currently opened file");
    }

    /**
     * Checks If opened file has the `number` of new lines.
     *
     * Usage:
     *
     * ``` php
     * <?php
     * $I->openFile('composer.json');
     * $I->seeNumberNewLines(5);
     * ```
     *
     * @param int $number New lines
     */
    public function seeNumberNewLines(int $number): void
    {
        $lines = preg_split('#[\n\r]#', $this->file);

        $this->assertTrue(
            $number === count($lines),
            "The number of new lines does not match with {$number}"
        );
    }

    /**
     * Checks that contents of currently opened file matches $regex
     */
    public function seeThisFileMatches(string $regex): void
    {
        $this->assertRegExp($regex, $this->file, "Contents of currently opened file does not match '{$regex}'");
    }

    /**
     * Checks the strict matching of file contents.
     * Unlike `seeInThisFile` will fail if file has something more than expected lines.
     * Better to use with HEREDOC strings.
     * Matching is done after removing "\r" chars from file content.
     *
     * ``` php
     * <?php
     * $I->openFile('process.pid');
     * $I->seeFileContentsEqual('3192');
     * ```
     */
    public function seeFileContentsEqual(string $text): void
    {
        $file = str_replace("\r", '', $this->file);
        TestCase::assertEquals($text, $file);
    }

    /**
     * Checks If opened file doesn't contain `text` in it
     *
     * ``` php
     * <?php
     * $I->openFile('composer.json');
     * $I->dontSeeInThisFile('codeception/codeception');
     * ```
     */
    public function dontSeeInThisFile(string $text): void
    {
        $this->assertStringNotContainsString($text, $this->file, "Found text '{$text}' in currently opened file");
    }

    /**
     * Deletes a file
     */
    public function deleteThisFile(): void
    {
        $this->deleteFile($this->filePath);
    }

    /**
     * Checks if file exists in path.
     * Opens a file when it's exists
     *
     * ``` php
     * <?php
     * $I->seeFileFound('UserModel.php','app/models');
     * ```
     */
    public function seeFileFound(string $filename, string $path = ''): void
    {
        if ($path === '' && file_exists($filename)) {
            $this->openFile($filename);
            TestCase::assertFileExists($filename);
            return;
        }

        $found = $this->findFileInPath($filename, $path);

        if ($found === false) {
            $this->fail(sprintf('File "%s" not found at "%s"', $filename, $path));
        }

        $this->openFile($found);
        TestCase::assertFileExists($found);
    }

    /**
     * Checks if file does not exist in path
     */
    public function dontSeeFileFound(string $filename, string $path = ''): void
    {
        if ($path === '') {
            TestCase::assertFileDoesNotExist($filename);
            return;
        }

        $found = $this->findFileInPath($filename, $path);

        if ($found === false) {
            //this line keeps a count of assertions correct
            TestCase::assertTrue(true);
            return;
        }

        TestCase::assertFileDoesNotExist($found);
    }

    /**
     * Finds the first matching file
     *
     * @throws AssertionFailedError When path does not exist
     * @return string|false Path to the first matching file
     */
    private function findFileInPath(string $filename, string $path): string|false
    {
        $path = $this->absolutizePath($path);
        if (!file_exists($path)) {
            $this->fail(sprintf('Directory does not exist: %s', $path));
        }

        $files = Finder::create()->files()->name($filename)->in($path);
        if ($files->count() === 0) {
            return false;
        }

        foreach ($files as $file) {
            return $file->getRealPath();
        }
    }


    /**
     * Erases directory contents
     *
     * ``` php
     * <?php
     * $I->cleanDir('logs');
     * ```
     */
    public function cleanDir(string $dirname): void
    {
        $path = $this->absolutizePath($dirname);
        Util::doEmptyDir($path);
    }

    /**
     * Saves contents to file
     */
    public function writeToFile(string $filename, string $contents): void
    {
        file_put_contents($filename, $contents);
    }
}
<?php

declare(strict_types=1);

namespace Codeception;

use Closure;
use Codeception\Stub\ConsecutiveMap;
use Codeception\Stub\StubMarshaler;
use Exception;
use LogicException;
use PHPUnit\Framework\MockObject\Generator as LegacyGenerator;
use PHPUnit\Framework\MockObject\Generator\Generator;
use PHPUnit\Framework\MockObject\MockObject as PHPUnitMockObject;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
use PHPUnit\Runner\Version as PHPUnitVersion;
use ReflectionClass;
use ReflectionException;
use RuntimeException;

class Stub
{
    public static array $magicMethods = ['__isset', '__get', '__set'];

    /**
     * Instantiates a class without executing a constructor.
     * Properties and methods can be set as a second parameter.
     * Even protected and private properties can be set.
     *
     * ```php
     * <?php
     * Stub::make('User');
     * Stub::make('User', ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ```php
     * <?php
     * Stub::make(new User, ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in second parameter
     * and it's return value or callback function as parameter
     *
     * ```php
     * <?php
     * Stub::make('User', ['save' => function () { return true; }]);
     * Stub::make('User', ['save' => true]);
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * Stub::make('User', [
     *      'save' => \Codeception\Stub\Expected::once()
     * ], $this);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param array $params - properties and methods to set
     * @param bool|PHPUnitTestCase $testCase
     *
     * @return PHPUnitMockObject&RealInstanceType - mock
     * @throws RuntimeException when class does not exist
     * @throws Exception
     */
    public static function make($class, array $params = [], $testCase = false)
    {
        $class = self::getClassname($class);
        if (!class_exists($class)) {
            if (interface_exists($class)) {
                throw new RuntimeException("Stub::make can't mock interfaces, please use Stub::makeEmpty instead.");
            }
            throw new RuntimeException("Stubbed class $class doesn't exist.");
        }

        $reflection = new ReflectionClass($class);
        $callables = self::getMethodsToReplace($reflection, $params);
        if ($reflection->isAbstract()) {
            $arguments = empty($callables) ? [] : array_keys($callables);
            $mock = self::generateMockForAbstractClass($class, $arguments, '', false, $testCase);
        } else {
            $arguments = empty($callables) ? null : array_keys($callables);
            $mock = self::generateMock($class, $arguments, [], '', false, $testCase);
        }

        self::bindParameters($mock, $params);

        return $mock;
    }

    /**
     * Creates $num instances of class through `Stub::make`.
     *
     * @param mixed $class
     * @throws Exception
     */
    public static function factory($class, int $num = 1, array $params = []): array
    {
        $objects = [];
        for ($i = 0; $i < $num; $i++) {
            $objects[] = self::make($class, $params);
        }

        return $objects;
    }

    /**
     * Instantiates class having all methods replaced with dummies except one.
     * Constructor is not triggered.
     * Properties and methods can be replaced.
     * Even protected and private properties can be set.
     *
     * ```php
     * <?php
     * Stub::makeEmptyExcept('User', 'save');
     * Stub::makeEmptyExcept('User', 'save', ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ```php
     * <?php
     * * Stub::makeEmptyExcept(new User, 'save');
     * ```
     *
     * To replace method provide it's name as a key in second parameter
     * and it's return value or callback function as parameter
     *
     * ```php
     * <?php
     * Stub::makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
     * Stub::makeEmptyExcept('User', 'save', ['isValid' => true]);
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * Stub::makeEmptyExcept('User', 'validate', [
     *      'save' => \Codeception\Stub\Expected::once()
     * ], $this);
     * ```
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param string $method
     * @param array $params
     * @param bool|PHPUnitTestCase $testCase
     *
     * @return PHPUnitMockObject&RealInstanceType
     * @throws Exception
     */
    public static function makeEmptyExcept($class, string $method, array $params = [], $testCase = false)
    {
        [$class, $methods] = self::createEmpty($class, $method);
        $mock = self::generateMock($class, $methods, [], '', false, $testCase);
        self::bindParameters($mock, $params);

        return $mock;
    }

    /**
     * Instantiates class having all methods replaced with dummies.
     * Constructor is not triggered.
     * Properties and methods can be set as a second parameter.
     * Even protected and private properties can be set.
     *
     * ```php
     * <?php
     * Stub::makeEmpty('User');
     * Stub::makeEmpty('User', ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ```php
     * <?php
     * Stub::makeEmpty(new User, ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in second parameter
     * and it's return value or callback function as parameter
     *
     * ```php
     * <?php
     * Stub::makeEmpty('User', ['save' => function () { return true; }]);
     * Stub::makeEmpty('User', ['save' => true]);
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * Stub::makeEmpty('User', [
     *      'save' => \Codeception\Stub\Expected::once()
     * ], $this);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param bool|PHPUnitTestCase $testCase
     *
     * @return PHPUnitMockObject&RealInstanceType
     * @throws Exception
     */
    public static function makeEmpty($class, array $params = [], $testCase = false)
    {
        $class = self::getClassname($class);
        $methods = array_filter(
            get_class_methods($class),
            fn($i) => !in_array($i, Stub::$magicMethods)
        );
        $mock = self::generateMock($class, $methods, [], '', false, $testCase);
        self::bindParameters($mock, $params);

        return $mock;
    }

    /**
     * Clones an object and redefines it's properties (even protected and private)
     *
     * @param       $obj
     * @param array $params
     * @return mixed
     * @throws Exception
     */
    public static function copy($obj, array $params = [])
    {
        $copy = clone($obj);
        self::bindParameters($copy, $params);

        return $copy;
    }

    /**
     * Instantiates a class instance by running constructor.
     * Parameters for constructor passed as second argument
     * Properties and methods can be set in third argument.
     * Even protected and private properties can be set.
     *
     * ```php
     * <?php
     * Stub::construct('User', ['autosave' => false]);
     * Stub::construct('User', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ```php
     * <?php
     * Stub::construct(new User, ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in third parameter
     * and it's return value or callback function as parameter
     *
     * ```php
     * <?php
     * Stub::construct('User', [], ['save' => function () { return true; }]);
     * Stub::construct('User', [], ['save' => true]);
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * Stub::construct('User', [], [
     *      'save' => \Codeception\Stub\Expected::once()
     * ], $this);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param bool|PHPUnitTestCase $testCase
     *
     * @return PHPUnitMockObject&RealInstanceType
     * @throws Exception
     */
    public static function construct($class, array $constructorParams = [], array $params = [], $testCase = false)
    {
        $class = self::getClassname($class);
        $reflection = new ReflectionClass($class);

        $callables = self::getMethodsToReplace($reflection, $params);

        $arguments = empty($callables) ? null : array_keys($callables);
        $mock = self::generateMock($class, $arguments, $constructorParams, $testCase);
        self::bindParameters($mock, $params);

        return $mock;
    }

    /**
     * Instantiates a class instance by running constructor with all methods replaced with dummies.
     * Parameters for constructor passed as second argument
     * Properties and methods can be set in third argument.
     * Even protected and private properties can be set.
     *
     * ```php
     * <?php
     * Stub::constructEmpty('User', ['autosave' => false]);
     * Stub::constructEmpty('User', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ```php
     * <?php
     * Stub::constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in third parameter
     * and it's return value or callback function as parameter
     *
     * ```php
     * <?php
     * Stub::constructEmpty('User', [], ['save' => function () { return true; }]);
     * Stub::constructEmpty('User', [], ['save' => true]);
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * Stub::constructEmpty('User', [], [
     *      'save' => \Codeception\Stub\Expected::once()
     * ], $this);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param array $constructorParams
     * @param array $params
     * @param bool|PHPUnitTestCase $testCase
     *
     * @return PHPUnitMockObject&RealInstanceType
     */
    public static function constructEmpty($class, array $constructorParams = [], array $params = [], $testCase = false)
    {
        $class = self::getClassname($class);
        $methods = array_filter(
            get_class_methods($class),
            fn($i) => !in_array($i, Stub::$magicMethods)
        );
        $mock = self::generateMock($class, $methods, $constructorParams, $testCase);
        self::bindParameters($mock, $params);

        return $mock;
    }

    /**
     * Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
     * Parameters for constructor passed as second argument
     * Properties and methods can be set in third argument.
     * Even protected and private properties can be set.
     *
     * ```php
     * <?php
     * Stub::constructEmptyExcept('User', 'save');
     * Stub::constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ```php
     * <?php
     * Stub::constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in third parameter
     * and it's return value or callback function as parameter
     *
     * ```php
     * <?php
     * Stub::constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
     * Stub::constructEmptyExcept('User', 'save', [], ['save' => true]);
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * Stub::constructEmptyExcept('User', 'save', [], [
     *      'save' => \Codeception\Stub\Expected::once()
     * ], $this);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param bool|PHPUnitTestCase $testCase
     *
     * @return PHPUnitMockObject&RealInstanceType
     * @throws ReflectionException
     */
    public static function constructEmptyExcept(
        $class,
        string $method,
        array $constructorParams = [],
        array $params = [],
        $testCase = false
    ) {
        [$class, $methods] = self::createEmpty($class, $method);
        $mock = self::generateMock($class, $methods, $constructorParams, $testCase);
        self::bindParameters($mock, $params);

        return $mock;
    }

    private static function generateMock()
    {
        $args = func_get_args();
        if (version_compare(PHPUnitVersion::series(), '11', '>=')) {
            if (!is_bool($args[1]) || !is_bool($args[2])) {
                $additionalParameters = [];
                if (!is_bool($args[1])) {
                    $additionalParameters[] = true;
                }
                if (!is_bool($args[2])) {
                    $additionalParameters[] = true;
                }

                array_splice($args, 1, 0, $additionalParameters);
            }
        } elseif (version_compare(PHPUnitVersion::series(), '10.4', '>=') && !is_bool($args[1])) {
            array_splice($args, 1, 0, [true]);
        }

        return self::doGenerateMock($args);
    }

    /**
     * Returns a mock object for the specified abstract class with all abstract
     * methods of the class mocked. Concrete methods to mock can be specified with
     * the last parameter
     *
     * @since  Method available since Release 1.0.0
     */
    private static function generateMockForAbstractClass(): object
    {
        return self::doGenerateMock(func_get_args(), true);
    }

    private static function doGenerateMock($args, $isAbstract = false)
    {
        $testCase = self::extractTestCaseFromArgs($args);

        // PHPUnit 10.4 changed method names
        if (version_compare(PHPUnitVersion::series(), '10.4', '>=')) {
            $methodName = $isAbstract ? 'mockObjectForAbstractClass' : 'testDouble';
        } else {
            $methodName = $isAbstract ? 'getMockForAbstractClass' : 'getMock';
        }

        // PHPUnit 10.3 changed the namespace
        if (version_compare(PHPUnitVersion::series(), '10.3', '>=')) {
            $generatorClass = new Generator();
        } else {
            $generatorClass = new LegacyGenerator();
        }

        $mock = call_user_func_array([$generatorClass, $methodName], $args);

        if ($testCase instanceof PHPUnitTestCase) {
            $testCase->registerMockObject($mock);
        }

        return $mock;
    }

    private static function extractTestCaseFromArgs(&$args)
    {
        $argsLength = count($args) - 1;
        $testCase = $args[$argsLength];

        unset($args[$argsLength]);

        return $testCase;
    }

    /**
     * Replaces properties of current stub
     *
     * @param PHPUnitMockObject|object $mock
     * @param array $params
     * @return object
     *@throws LogicException
     */
    public static function update(object $mock, array $params): object
    {
        self::bindParameters($mock, $params);

        return $mock;
    }

    /**
     * @param PHPUnitMockObject|object $mock
     * @param array $params
     * @throws LogicException
     */
    protected static function bindParameters($mock, array $params)
    {
        $reflectionClass = new ReflectionClass($mock);
        if ($mock instanceof PHPUnitMockObject) {
            $parentClass = $reflectionClass->getParentClass();
            if ($parentClass !== false) {
                $reflectionClass = $reflectionClass->getParentClass();
            }
        }

        foreach ($params as $param => $value) {
            // redefine method
            if ($reflectionClass->hasMethod($param)) {
                if ($value instanceof StubMarshaler) {
                    $marshaler = $value;
                    $mock
                        ->expects($marshaler->getMatcher())
                        ->method($param)
                        ->will(new ReturnCallback($marshaler->getValue()));
                } elseif ($value instanceof Closure) {
                    $mock
                        ->expects(new AnyInvokedCount)
                        ->method($param)
                        ->will(new ReturnCallback($value));
                } elseif ($value instanceof ConsecutiveMap) {
                    $consecutiveMap = $value;
                    $mock
                        ->expects(new AnyInvokedCount)
                        ->method($param)
                        ->will(new ConsecutiveCalls($consecutiveMap->getMap()));
                } else {
                    $mock
                        ->expects(new AnyInvokedCount)
                        ->method($param)
                        ->will(new ReturnStub($value));
                }
            } elseif ($reflectionClass->hasProperty($param)) {
                $reflectionProperty = $reflectionClass->getProperty($param);
                $reflectionProperty->setAccessible(true);
                $reflectionProperty->setValue($mock, $value);
            } else {
                if ($reflectionClass->hasMethod('__set')) {
                    try {
                        $mock->{$param} = $value;
                    } catch (Exception $exception) {
                        throw new LogicException(
                            sprintf(
                                'Could not add property %1$s, class %2$s implements __set method, '
                                . 'and no %1$s property exists',
                                $param,
                                $reflectionClass->getName()
                            ),
                            $exception->getCode(),
                            $exception
                        );
                    }
                } else {
                    $mock->{$param} = $value;
                }
            }
        }
    }

    /**
     * @TO-DO Should be simplified
     */
    protected static function getClassname($object)
    {
        if (is_object($object)) {
            return get_class($object);
        }

        if (is_callable($object)) {
            return call_user_func($object);
        }

        return $object;
    }

    protected static function getMethodsToReplace(ReflectionClass $reflection, array $params): array
    {
        $callables = [];
        foreach ($params as $method => $value) {
            if ($reflection->hasMethod($method)) {
                $callables[$method] = $value;
            }
        }

        return $callables;
    }


    /**
     * Stubbing a method call to return a list of values in the specified order.
     *
     * ```php
     * <?php
     * $user = Stub::make('User', ['getName' => Stub::consecutive('david', 'emma', 'sam', 'amy')]);
     * $user->getName(); //david
     * $user->getName(); //emma
     * $user->getName(); //sam
     * $user->getName(); //amy
     * ```
     */
    public static function consecutive(): ConsecutiveMap
    {
        return new ConsecutiveMap(func_get_args());
    }

    /**
     * @param mixed $class
     * @throws ReflectionException
     */
    private static function createEmpty($class, string $method): array
    {
        $class = self::getClassname($class);
        $reflectionClass = new ReflectionClass($class);

        $methods = $reflectionClass->getMethods();

        $methods = array_filter(
            $methods,
            fn($m) => !in_array($m->name, Stub::$magicMethods)
        );

        $methods = array_filter(
            $methods,
            fn($m) => $method != $m->name
        );

        $methods = array_map(
            fn($m) => $m->name,
            $methods
        );

        $methods = count($methods) ? $methods : null;
        return [$class, $methods];
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Test\Feature;

use Exception;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use RuntimeException;

/**
 * ### Usage in Codeception
 *
 * Since Codeception 2.3.8 this trait is enabled in `\Codeception\Test\Unit` class.
 *
 * ### Usage in PHPUnit
 *
 * Include this trait into a TestCase to be able to use Stubs and Mocks:
 *
 * ```php
 * <?php
 * class MyTest extends \PHPUnit\Framework\TestCase
 * {
 *      use Codeception\Test\Feature\Stub;
 * }
 * ```
 */
trait Stub
{
    private ?array $mocks = null;

    protected function stubStart()
    {
        if ($this instanceof TestCase) {
            return;
        }
        
        $this->mocks = [];
    }

    protected function stubEnd($status, $time)
    {
        if ($this instanceof TestCase) {
            return;
        }
        
        if ($status !== 'ok') { // Codeception status
            return;
        }

        foreach ($this->mocks as $mockObject) {
            if ($mockObject->__phpunit_hasMatchers()) {
                $this->assertTrue(true); // incrementing assertions
            }

            $mockObject->__phpunit_verify(true);
        }
    }

    /**
     * Instantiates a class without executing a constructor.
     * Properties and methods can be set as a second parameter.
     * Even protected and private properties can be set.
     *
     * ``` php
     * <?php
     * $this->make('User');
     * $this->make('User', ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ``` php
     * <?php
     * $this->make(new User, ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in second parameter
     * and it's return value or callback function as parameter
     *
     * ``` php
     * <?php
     * $this->make('User', ['save' => function () { return true; }]);
     * $this->make('User', ['save' => true]);
     * ```
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param array $params - properties and methods to set
     *
     * @return MockObject&RealInstanceType - mock
     * @throws RuntimeException when class does not exist
     * @throws Exception
     */
    public function make($class, array $params = [])
    {
        return $this->mocks[] = \Codeception\Stub::make($class, $params, $this);
    }

    /**
     * Instantiates class having all methods replaced with dummies.
     * Constructor is not triggered.
     * Properties and methods can be set as a second parameter.
     * Even protected and private properties can be set.
     *
     * ``` php
     * <?php
     * $this->makeEmpty('User');
     * $this->makeEmpty('User', ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ``` php
     * <?php
     * $this->makeEmpty(new User, ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in second parameter
     * and it's return value or callback function as parameter
     *
     * ``` php
     * <?php
     * $this->makeEmpty('User', ['save' => function () { return true; }]);
     * $this->makeEmpty('User', ['save' => true]);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @param array $params
     * @return MockObject&RealInstanceType
     * @throws Exception
     */
    public function makeEmpty($class, array $params = [])
    {
        return $this->mocks[] = \Codeception\Stub::makeEmpty($class, $params, $this);
    }

    /**
     * Instantiates class having all methods replaced with dummies except one.
     * Constructor is not triggered.
     * Properties and methods can be replaced.
     * Even protected and private properties can be set.
     *
     * ``` php
     * <?php
     * $this->makeEmptyExcept('User', 'save');
     * $this->makeEmptyExcept('User', 'save', ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ``` php
     * <?php
     * * $this->makeEmptyExcept(new User, 'save');
     * ```
     *
     * To replace method provide it's name as a key in second parameter
     * and it's return value or callback function as parameter
     *
     * ``` php
     * <?php
     * $this->makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
     * $this->makeEmptyExcept('User', 'save', ['isValid' => true]);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     *
     * @return \PHPUnit\Framework\MockObject\MockObject&RealInstanceType
     * @throws Exception
     */
    public function makeEmptyExcept($class, string $method, array $params = [])
    {
        return $this->mocks[] = \Codeception\Stub::makeEmptyExcept($class, $method, $params, $this);
    }

    /**
     * Instantiates a class instance by running constructor.
     * Parameters for constructor passed as second argument
     * Properties and methods can be set in third argument.
     * Even protected and private properties can be set.
     *
     * ``` php
     * <?php
     * $this->construct('User', ['autosave' => false]);
     * $this->construct('User', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ``` php
     * <?php
     * $this->construct(new User, ['autosave' => false), ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in third parameter
     * and it's return value or callback function as parameter
     *
     * ``` php
     * <?php
     * $this->construct('User', [], ['save' => function () { return true; }]);
     * $this->construct('User', [], ['save' => true]);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @return MockObject&RealInstanceType
     * @throws Exception
     */
    public function construct($class, array $constructorParams = [], array $params = [])
    {
        return $this->mocks[] = \Codeception\Stub::construct($class, $constructorParams, $params, $this);
    }

    /**
     * Instantiates a class instance by running constructor with all methods replaced with dummies.
     * Parameters for constructor passed as second argument
     * Properties and methods can be set in third argument.
     * Even protected and private properties can be set.
     *
     * ``` php
     * <?php
     * $this->constructEmpty('User', ['autosave' => false]);
     * $this->constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ``` php
     * <?php
     * $this->constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in third parameter
     * and it's return value or callback function as parameter
     *
     * ``` php
     * <?php
     * $this->constructEmpty('User', array(), array('save' => function () { return true; }));
     * $this->constructEmpty('User', array(), array('save' => true));
     * ```
     *
     * **To create a mock, pass current testcase name as last argument:**
     *
     * ```php
     * <?php
     * $this->constructEmpty('User', [], [
     *      'save' => \Codeception\Stub\Expected::once()
     * ]);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @return MockObject&RealInstanceType
     */
    public function constructEmpty($class, array $constructorParams = [], array $params = [])
    {
        return $this->mocks[] = \Codeception\Stub::constructEmpty($class, $constructorParams, $params, $this);
    }

    /**
     * Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
     * Parameters for constructor passed as second argument
     * Properties and methods can be set in third argument.
     * Even protected and private properties can be set.
     *
     * ``` php
     * <?php
     * $this->constructEmptyExcept('User', 'save');
     * $this->constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * Accepts either name of class or object of that class
     *
     * ``` php
     * <?php
     * $this->constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
     * ```
     *
     * To replace method provide it's name as a key in third parameter
     * and it's return value or callback function as parameter
     *
     * ``` php
     * <?php
     * $this->constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
     * $this->constructEmptyExcept('User', 'save', [], ['save' => true]);
     * ```
     *
     * @template RealInstanceType of object
     * @param class-string<RealInstanceType>|RealInstanceType|callable(): class-string<RealInstanceType> $class - A class to be mocked
     * @return MockObject&RealInstanceType
     * @throws \ReflectionException
     */
    public function constructEmptyExcept($class, string $method, array $constructorParams = [], array $params = [])
    {
        return $this->mocks[] = \Codeception\Stub::constructEmptyExcept($class, $method, $constructorParams, $params, $this);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Stub;

use Closure;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce;
use PHPUnit\Framework\MockObject\Rule\InvokedCount;

class Expected
{
    /**
     * Checks if a method never has been invoked
     *
     * If method invoked, it will immediately throw an
     * exception.
     *
     * ```php
     * <?php
     * use \Codeception\Stub\Expected;
     *
     * $user = $this->make('User', [
     *      'getName' => Expected::never(),
     *      'someMethod' => function() {}
     * ]);
     * $user->someMethod();
     * ```
     *
     * @param mixed $params
     */
    public static function never($params = null): StubMarshaler
    {
        return new StubMarshaler(
            new InvokedCount(0),
            self::closureIfNull($params)
        );
    }

    /**
     * Checks if a method has been invoked exactly one
     * time.
     *
     * If the number is less or greater it will later be checked in verify() and also throw an
     * exception.
     *
     * ```php
     * <?php
     * use \Codeception\Stub\Expected;
     *
     * $user = $this->make(
     *     'User',
     *     array(
     *         'getName' => Expected::once('Davert'),
     *         'someMethod' => function() {}
     *     )
     * );
     * $userName = $user->getName();
     * $this->assertEquals('Davert', $userName);
     * ```
     * Alternatively, a function can be passed as parameter:
     *
     * ```php
     * <?php
     * Expected::once(function() { return Faker::name(); });
     * ```
     *
     * @param mixed $params
     */
    public static function once($params = null): StubMarshaler
    {
        return new StubMarshaler(
            new InvokedCount(1),
            self::closureIfNull($params)
        );
    }

    /**
     * Checks if a method has been invoked at least one
     * time.
     *
     * If the number of invocations is 0 it will throw an exception in verify.
     *
     * ```php
     * <?php
     * use \Codeception\Stub\Expected;
     *
     * $user = $this->make(
     *     'User',
     *     array(
     *         'getName' => Expected::atLeastOnce('Davert')),
     *         'someMethod' => function() {}
     *     )
     * );
     * $user->getName();
     * $userName = $user->getName();
     * $this->assertEquals('Davert', $userName);
     * ```
     *
     * Alternatively, a function can be passed as parameter:
     *
     * ```php
     * <?php
     * Expected::atLeastOnce(function() { return Faker::name(); });
     * ```
     *
     * @param mixed $params
     */
    public static function atLeastOnce($params = null): StubMarshaler
    {
        return new StubMarshaler(
            new InvokedAtLeastOnce(),
            self::closureIfNull($params)
        );
    }

    /**
     * Checks if a method has been invoked a certain amount
     * of times.
     * If the number of invocations exceeds the value it will immediately throw an
     * exception,
     * If the number is less it will later be checked in verify() and also throw an
     * exception.
     *
     * ``` php
     * <?php
     * use \Codeception\Stub;
     * use \Codeception\Stub\Expected;
     *
     * $user = $this->make(
     *     'User',
     *     array(
     *         'getName' => Expected::exactly(3, 'Davert'),
     *         'someMethod' => function() {}
     *     )
     * );
     * $user->getName();
     * $user->getName();
     * $userName = $user->getName();
     * $this->assertEquals('Davert', $userName);
     * ```
     * Alternatively, a function can be passed as parameter:
     *
     * ```php
     * <?php
     * Expected::exactly(function() { return Faker::name() });
     * ```
     *
     * @param mixed $params
     */
    public static function exactly(int $count, $params = null): StubMarshaler
    {
        return new StubMarshaler(
            new InvokedCount($count),
            self::closureIfNull($params)
        );
    }

    private static function closureIfNull($params): Closure
    {
        if ($params instanceof Closure) {
            return $params;
        }

        return fn() => $params;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Stub;

/**
 * Holds matcher and value of mocked method
 */
class ConsecutiveMap
{
    private array $consecutiveMap = [];

    public function __construct(array $consecutiveMap)
    {
        $this->consecutiveMap = $consecutiveMap;
    }

    public function getMap(): array
    {
        return $this->consecutiveMap;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Stub;

use PHPUnit\Framework\MockObject\Rule\InvocationOrder;

/**
 * Holds matcher and value of mocked method
 */
class StubMarshaler
{
    private InvocationOrder $methodMatcher;

    private $methodValue;

    public function __construct(InvocationOrder $matcher, $value)
    {
        $this->methodMatcher = $matcher;
        $this->methodValue = $value;
    }

    public function getMatcher(): InvocationOrder
    {
        return $this->methodMatcher;
    }

    public function getValue()
    {
        return $this->methodValue;
    }
}
<?php

declare(strict_types=1);

use Robo\Tasks;

require_once 'vendor/autoload.php';
/**
 * This is project's console commands configuration for Robo task runner.
 *
 * @see https://robo.li/
 */
class RoboFile extends Tasks
{
    protected $docs = [
        'docs/Stub.md' => 'Codeception\Stub',
        'docs/Expected.md' => 'Codeception\Stub\Expected',
        'docs/StubTrait.md' => 'Codeception\Test\Feature\Stub',
    ];

    public function docs()
    {
        foreach ($this->docs as $file => $class) {
            if (!class_exists($class, true) && !trait_exists($class, true)) {
                throw new Exception('ups');
            }
            $this->say("Here goes, $class");
            $this->taskGenDoc($file)
                ->docClass($class)
                ->filterMethods(function(ReflectionMethod $method) {
                    if ($method->isConstructor() or $method->isDestructor()) return false;
                    if (!$method->isPublic()) return false;
                    if (strpos($method->name, '_') === 0) return false;
                    if (strpos($method->name, 'stub') === 0) return false;
                    return true;
                })
                ->processMethodDocBlock(
                    function (ReflectionMethod $m, $doc) {
                        $doc = str_replace(array('@since'), array(' * available since version'), $doc);
                        $doc = str_replace(array(' @', "\n@"), array("  * ", "\n * "), $doc);
                        return $doc;
                    })
                ->processProperty(false)
                ->run();
        }
    }
}<?php

declare(strict_types=1);

namespace Codeception\Util;

/**
 * This class is left for BC compatibility.
 * Most of its contents moved to parent
 *
 * Class Soap
 * @package Codeception\Util
 */
class Soap extends Xml
{
    public static function request(): XmlBuilder
    {
        return new XmlBuilder();
    }

    public static function response(): XmlBuilder
    {
        return new XmlBuilder();
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use DOMDocument;
use DOMElement;
use DOMNode;

use function is_array;

class Xml
{
    /**
     * @param array<mixed> $array
     */
    public static function arrayToXml(DOMDocument $xml, DOMNode $domNode, array $array = []): DOMDocument
    {
        foreach ($array as $el => $val) {
            /**
             * @var string|array<mixed> $val
             */
            if (is_array($val)) {
                self::arrayToXml($xml, $domNode->$el, $val);
            } else {
                $domNode->appendChild($xml->createElement($el, $val));
            }
        }
        return $xml;
    }

    /**
     * @param DOMNode|XmlBuilder|array<mixed>|string|null $xml
     */
    public static function toXml(DOMNode|XmlBuilder|array|string|null $xml): DOMDocument
    {
        if ($xml instanceof XmlBuilder) {
            return $xml->getDom();
        }
        if ($xml instanceof DOMDocument) {
            return $xml;
        }
        $dom = new DOMDocument();
        $dom->preserveWhiteSpace = false;
        if ($xml instanceof DOMNode) {
            $xml = $dom->importNode($xml, true);
            $dom->appendChild($xml);
            return $dom;
        }

        if (is_array($xml)) {
            return self::arrayToXml($dom, $dom, $xml);
        }
        if (!empty($xml)) {
            $dom->loadXML($xml);
        }
        return $dom;
    }

    public static function build(): XmlBuilder
    {
        return new XmlBuilder();
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use DOMDocument;
use DOMElement;
use DOMNode;
use Exception;

/**
 * That's a pretty simple yet powerful class to build XML structures in jQuery-like style.
 * With no XML line actually written!
 * Uses DOM extension to manipulate XML data.
 *
 * ```php
 * <?php
 * $xml = new \Codeception\Util\XmlBuilder();
 * $xml->users
 *    ->user
 *        ->val(1)
 *        ->email
 *            ->val('davert@mail.ua')
 *            ->attr('valid','true')
 *            ->parent()
 *        ->cart
 *            ->attr('empty','false')
 *            ->items
 *                ->item
 *                    ->val('useful item');
 *                ->parents('user')
 *        ->active
 *            ->val(1);
 * echo $xml;
 * ```
 *
 * This will produce this XML
 *
 * ```xml
 * <?xml version="1.0"?>
 * <users>
 *    <user>
 *        1
 *        <email valid="true">davert@mail.ua</email>
 *        <cart empty="false">
 *            <items>
 *                <item>useful item</item>
 *            </items>
 *        </cart>
 *        <active>1</active>
 *    </user>
 * </users>
 * ```
 *
 * ### Usage
 *
 * Builder uses chained calls. So each call to builder returns a builder object.
 * Except for `getDom` and `__toString` methods.
 *
 *  * `$xml->node` - create new xml node and go inside of it.
 *  * `$xml->node->val('value')` - sets the inner value of node
 *  * `$xml->attr('name','value')` - set the attribute of node
 *  * `$xml->parent()` - go back to parent node.
 *  * `$xml->parents('user')` - go back through all parents to `user` node.
 *
 * Export:
 *
 *  * `$xml->getDom` - get a DOMDocument object
 *  * `$xml->__toString` - get a string representation of XML.
 *
 * [Source code](https://github.com/Codeception/lib-xml/blob/main/src/Util/XmlBuilder.php)
 */
class XmlBuilder
{
    protected DOMDocument $dom;

    protected DOMNode $currentNode;

    public function __construct()
    {
        $this->dom = new DOMDocument();
        $this->currentNode = $this->dom;
    }

    /**
     * Appends child node
     */
    public function __get(string $tag): XmlBuilder
    {
        $domElement = $this->dom->createElement($tag);
        $this->currentNode->appendChild($domElement);
        $this->currentNode = $domElement;
        return $this;
    }

    public function val(string $val): self
    {
        $this->currentNode->nodeValue = $val;
        return $this;
    }

    /**
     * Sets attribute for current node
     */
    public function attr(string $attr, string $val): self
    {
        if (!$this->currentNode instanceof DOMElement) {
            throw new Exception('Current node is not DOMElement');
        }
        $this->currentNode->setAttribute($attr, $val);
        return $this;
    }

    /**
     * Traverses to parent
     */
    public function parent(): self
    {
        if ($this->currentNode->parentNode === null) {
            throw new Exception('Element has no parent');
        }
        $this->currentNode = $this->currentNode->parentNode;
        return $this;
    }

    /**
     * Traverses to parent with $tagName
     *
     * @throws Exception
     */
    public function parents(string $tagName): self
    {
        $traverseNode = $this->currentNode;
        $elFound = false;
        while ($traverseNode->parentNode) {
            $traverseNode = $traverseNode->parentNode;
            if ($traverseNode instanceof DOMElement && $traverseNode->tagName === $tagName) {
                $this->currentNode = $traverseNode;
                $elFound = true;
                break;
            }
        }

        if (!$elFound) {
            throw new Exception("Parent {$tagName} not found in XML");
        }

        return $this;
    }

    public function __toString(): string
    {
        $string = $this->dom->saveXML();
        if ($string === false) {
            throw new Exception('Failed to convert DOM to string');
        }

        return $string;
    }

    public function getDom(): DOMDocument
    {
        return $this->dom;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util;

use Codeception\Exception\ElementNotFound;
use Codeception\Exception\MalformedLocatorException;
use Codeception\Util\Soap as SoapXmlUtil;
use DOMDocument;
use DOMNode;
use DOMXPath;
use Symfony\Component\CssSelector\CssSelectorConverter;

class XmlStructure
{
    protected DOMDocument $xml;

    /**
     * @param DOMNode|XmlBuilder|array<mixed>|string|null $xml
     */
    public function __construct(DOMNode|XmlBuilder|array|string|null $xml)
    {
        $this->xml = SoapXmlUtil::toXml($xml);
    }

    public function matchesXpath(string $xpath): bool
    {
        $domXpath = new DOMXPath($this->xml);
        $res = $domXpath->query($xpath);
        if ($res === false) {
            throw new MalformedLocatorException($xpath);
        }
        return $res->length > 0;
    }

    public function matchElement(string $cssOrXPath): ?DOMNode
    {
        $domXpath = new DOMXpath($this->xml);
        $selector = (new CssSelectorConverter())->toXPath($cssOrXPath);
        $els = $domXpath->query($selector);
        if ($els !== false && count($els) > 0) {
            return $els->item(0);
        }
        $els = $domXpath->query($cssOrXPath);
        if ($els !== false && count($els) > 0) {
            return $els->item(0);
        }
        throw new ElementNotFound($cssOrXPath);
    }

    /**
     * @param DOMNode|XmlBuilder|array<mixed>|string|null $xml
     */
    public function matchXmlStructure(DOMNode|XmlBuilder|array|string|null $xml): bool
    {
        $xml = SoapXmlUtil::toXml($xml);
        $root = $xml->firstChild;
        if ($root === null) {
            throw new \Exception('XML is empty');
        }
        $els = $this->xml->getElementsByTagName($root->nodeName);
        if (count($els) === 0) {
            throw new ElementNotFound($root->nodeName, 'Element');
        }

        foreach ($els as $node) {
            /**
             * @var DOMNode $node
             */
            if ($this->matchForNode($root, $node)) {
                return true;
            }
        }

        return false;
    }

    protected function matchForNode(DOMNode $schema, DOMNode $xml): bool
    {
        foreach ($schema->childNodes as $node1) {
            /**
             * @var DOMNode $node1
             */
            $matched = false;
            foreach ($xml->childNodes as $node2) {
                /**
                 * @var DOMNode $node2
                 */
                if ($node1->nodeName == $node2->nodeName) {
                    $matched = $this->matchForNode($node1, $node2);
                    if ($matched) {
                        break;
                    }
                }
            }
            if (!$matched) {
                return false;
            }
        }
        return true;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util\Shared;

use Codeception\PHPUnit\TestCase;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint as PHPUnitConstraint;

trait InheritedAsserts
{
    /**
     * Asserts that an array has a specified key.
     *
     * @param int|string $key
     * @param array|\ArrayAccess $array
     */
    protected function assertArrayHasKey($key, $array, string $message = '')
    {
        Assert::assertArrayHasKey($key, $array, $message);
    }

    /**
     * Asserts that an array does not have a specified key.
     *
     * @param int|string $key
     * @param array|\ArrayAccess $array
     */
    protected function assertArrayNotHasKey($key, $array, string $message = '')
    {
        Assert::assertArrayNotHasKey($key, $array, $message);
    }

    /**
     * Asserts that a class has a specified attribute.
     */
    protected function assertClassHasAttribute(string $attributeName, string $className, string $message = '')
    {
        trigger_error(__FUNCTION__ . ' was removed from PHPUnit since PHPUnit 10', E_USER_DEPRECATED);

        if (method_exists(Assert::class, 'assertClassHasAttribute')) {
            Assert::assertClassHasAttribute($attributeName, $className, $message);
        } else {
            Assert::assertTrue(property_exists($className, $attributeName), $message);
        }
    }

    /**
     * Asserts that a class has a specified static attribute.
     */
    protected function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '')
    {
        Assert::assertClassHasStaticAttribute($attributeName, $className, $message);
    }

    /**
     * Asserts that a class does not have a specified attribute.
     */
    protected function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '')
    {
        trigger_error(__FUNCTION__ . ' was removed from PHPUnit since PHPUnit 10', E_USER_DEPRECATED);

        if (method_exists(Assert::class, 'assertClassNotHasAttribute')) {
            Assert::assertClassNotHasAttribute($attributeName, $className, $message);
        } else {
            Assert::assertFalse(property_exists($className, $attributeName), $message);
        }
    }

    /**
     * Asserts that a class does not have a specified static attribute.
     */
    protected function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '')
    {
        trigger_error(__FUNCTION__ . ' was removed from PHPUnit since PHPUnit 10', E_USER_DEPRECATED);

        Assert::assertClassNotHasStaticAttribute($attributeName, $className, $message);
    }

    /**
     * Asserts that a haystack contains a needle.
     *
     * @param mixed $needle
     */
    protected function assertContains($needle, iterable $haystack, string $message = '')
    {
        Assert::assertContains($needle, $haystack, $message);
    }

    /**
     * @param mixed $needle
     */
    protected function assertContainsEquals($needle, iterable $haystack, string $message = '')
    {
        Assert::assertContainsEquals($needle, $haystack, $message);
    }

    /**
     * Asserts that a haystack contains only values of a given type.
     */
    protected function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '')
    {
        Assert::assertContainsOnly($type, $haystack, $isNativeType, $message);
    }

    /**
     * Asserts that a haystack contains only instances of a given class name.
     */
    protected function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '')
    {
        Assert::assertContainsOnlyInstancesOf($className, $haystack, $message);
    }

    /**
     * Asserts the number of elements of an array, Countable or Traversable.
     *
     * @param \Countable|iterable $haystack
     */
    protected function assertCount(int $expectedCount, $haystack, string $message = '')
    {
        Assert::assertCount($expectedCount, $haystack, $message);
    }

    /**
     * Asserts that a directory does not exist.
     */
    protected function assertDirectoryDoesNotExist(string $directory, string $message = '')
    {
        Assert::assertDirectoryDoesNotExist($directory, $message);
    }

    /**
     * Asserts that a directory exists.
     */
    protected function assertDirectoryExists(string $directory, string $message = '')
    {
        Assert::assertDirectoryExists($directory, $message);
    }

    /**
     * Asserts that a directory exists and is not readable.
     */
    protected function assertDirectoryIsNotReadable(string $directory, string $message = '')
    {
        Assert::assertDirectoryIsNotReadable($directory, $message);
    }

    /**
     * Asserts that a directory exists and is not writable.
     */
    protected function assertDirectoryIsNotWritable(string $directory, string $message = '')
    {
        Assert::assertDirectoryIsNotWritable($directory, $message);
    }

    /**
     * Asserts that a directory exists and is readable.
     */
    protected function assertDirectoryIsReadable(string $directory, string $message = '')
    {
        Assert::assertDirectoryIsReadable($directory, $message);
    }

    /**
     * Asserts that a directory exists and is writable.
     */
    protected function assertDirectoryIsWritable(string $directory, string $message = '')
    {
        Assert::assertDirectoryIsWritable($directory, $message);
    }

    /**
     * Asserts that a string does not match a given regular expression.
     */
    protected function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '')
    {
        TestCase::assertDoesNotMatchRegularExpression($pattern, $string, $message);
    }

    /**
     * Asserts that a variable is empty.
     *
     * @param mixed $actual
     */
    protected function assertEmpty($actual, string $message = '')
    {
        Assert::assertEmpty($actual, $message);
    }

    /**
     * Asserts that two variables are equal.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertEquals($expected, $actual, string $message = '')
    {
        Assert::assertEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two variables are equal (canonicalizing).
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertEqualsCanonicalizing($expected, $actual, string $message = '')
    {
        TestCase::assertEqualsCanonicalizing($expected, $actual, $message);
    }

    /**
     * Asserts that two variables are equal (ignoring case).
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertEqualsIgnoringCase($expected, $actual, string $message = '')
    {
        TestCase::assertEqualsIgnoringCase($expected, $actual, $message);
    }

    /**
     * Asserts that two variables are equal (with delta).
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '')
    {
        TestCase::assertEqualsWithDelta($expected, $actual, $delta, $message);
    }

    /**
     * Asserts that a condition is false.
     *
     * @param mixed $condition
     */
    protected function assertFalse($condition, string $message = '')
    {
        Assert::assertFalse($condition, $message);
    }

    /**
     * Asserts that a file does not exist.
     */
    protected function assertFileDoesNotExist(string $filename, string $message = '')
    {
        TestCase::assertFileDoesNotExist($filename, $message);
    }

    /**
     * Asserts that the contents of one file is equal to the contents of another file.
     */
    protected function assertFileEquals(string $expected, string $actual, string $message = '')
    {
        Assert::assertFileEquals($expected, $actual, $message);
    }

    /**
     * Asserts that the contents of one file is equal to the contents of another file (canonicalizing).
     */
    protected function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '')
    {
        Assert::assertFileEqualsCanonicalizing($expected, $actual, $message);
    }

    /**
     * Asserts that the contents of one file is equal to the contents of another file (ignoring case).
     */
    protected function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '')
    {
        Assert::assertFileEqualsIgnoringCase($expected, $actual, $message);
    }

    /**
     * Asserts that a file exists.
     */
    protected function assertFileExists(string $filename, string $message = '')
    {
        Assert::assertFileExists($filename, $message);
    }

    /**
     * Asserts that a file exists and is not readable.
     */
    protected function assertFileIsNotReadable(string $file, string $message = '')
    {
        Assert::assertFileIsNotReadable($file, $message);
    }

    /**
     * Asserts that a file exists and is not writable.
     */
    protected function assertFileIsNotWritable(string $file, string $message = '')
    {
        Assert::assertFileIsNotWritable($file, $message);
    }

    /**
     * Asserts that a file exists and is readable.
     */
    protected function assertFileIsReadable(string $file, string $message = '')
    {
        Assert::assertFileIsReadable($file, $message);
    }

    /**
     * Asserts that a file exists and is writable.
     */
    protected function assertFileIsWritable(string $file, string $message = '')
    {
        Assert::assertFileIsWritable($file, $message);
    }

    /**
     * Asserts that the contents of one file is not equal to the contents of another file.
     */
    protected function assertFileNotEquals(string $expected, string $actual, string $message = '')
    {
        Assert::assertFileNotEquals($expected, $actual, $message);
    }

    /**
     * Asserts that the contents of one file is not equal to the contents of another file (canonicalizing).
     */
    protected function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '')
    {
        Assert::assertFileNotEqualsCanonicalizing($expected, $actual, $message);
    }

    /**
     * Asserts that the contents of one file is not equal to the contents of another file (ignoring case).
     */
    protected function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '')
    {
        Assert::assertFileNotEqualsIgnoringCase($expected, $actual, $message);
    }

    /**
     * Asserts that a variable is finite.
     *
     * @param mixed $actual
     */
    protected function assertFinite($actual, string $message = '')
    {
        Assert::assertFinite($actual, $message);
    }

    /**
     * Asserts that a value is greater than another value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertGreaterThan($expected, $actual, string $message = '')
    {
        Assert::assertGreaterThan($expected, $actual, $message);
    }

    /**
     * Asserts that a value is greater than or equal to another value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertGreaterThanOrEqual($expected, $actual, string $message = '')
    {
        Assert::assertGreaterThanOrEqual($expected, $actual, $message);
    }

    /**
     * Asserts that a variable is infinite.
     *
     * @param mixed $actual
     */
    protected function assertInfinite($actual, string $message = '')
    {
        Assert::assertInfinite($actual, $message);
    }

    /**
     * Asserts that a variable is of a given type.
     *
     * @param mixed $actual
     */
    protected function assertInstanceOf(string $expected, $actual, string $message = '')
    {
        Assert::assertInstanceOf($expected, $actual, $message);
    }

    /**
     * Asserts that a variable is of type array.
     *
     * @param mixed $actual
     */
    protected function assertIsArray($actual, string $message = '')
    {
        TestCase::assertIsArray($actual, $message);
    }

    /**
     * Asserts that a variable is of type bool.
     *
     * @param mixed $actual
     */
    protected function assertIsBool($actual, string $message = '')
    {
        TestCase::assertIsBool($actual, $message);
    }

    /**
     * Asserts that a variable is of type callable.
     *
     * @param mixed $actual
     */
    protected function assertIsCallable($actual, string $message = '')
    {
        TestCase::assertIsCallable($actual, $message);
    }

    /**
     * Asserts that a variable is of type resource and is closed.
     *
     * @param mixed $actual
     */
    protected function assertIsClosedResource($actual, string $message = '')
    {
        TestCase::assertIsClosedResource($actual, $message);
    }

    /**
     * Asserts that a variable is of type float.
     *
     * @param mixed $actual
     */
    protected function assertIsFloat($actual, string $message = '')
    {
        TestCase::assertIsFloat($actual, $message);
    }

    /**
     * Asserts that a variable is of type int.
     *
     * @param mixed $actual
     */
    protected function assertIsInt($actual, string $message = '')
    {
        TestCase::assertIsInt($actual, $message);
    }

    /**
     * Asserts that a variable is of type iterable.
     *
     * @param mixed $actual
     */
    protected function assertIsIterable($actual, string $message = '')
    {
        TestCase::assertIsIterable($actual, $message);
    }

    /**
     * Asserts that a variable is not of type array.
     *
     * @param mixed $actual
     */
    protected function assertIsNotArray($actual, string $message = '')
    {
        TestCase::assertIsNotArray($actual, $message);
    }

    /**
     * Asserts that a variable is not of type bool.
     *
     * @param mixed $actual
     */
    protected function assertIsNotBool($actual, string $message = '')
    {
        TestCase::assertIsNotBool($actual, $message);
    }

    /**
     * Asserts that a variable is not of type callable.
     *
     * @param mixed $actual
     */
    protected function assertIsNotCallable($actual, string $message = '')
    {
        TestCase::assertIsNotCallable($actual, $message);
    }

    /**
     * Asserts that a variable is not of type resource.
     *
     * @param mixed $actual
     */
    protected function assertIsNotClosedResource($actual, string $message = '')
    {
        TestCase::assertIsNotClosedResource($actual, $message);
    }

    /**
     * Asserts that a variable is not of type float.
     *
     * @param mixed $actual
     */
    protected function assertIsNotFloat($actual, string $message = '')
    {
        TestCase::assertIsNotFloat($actual, $message);
    }

    /**
     * Asserts that a variable is not of type int.
     *
     * @param mixed $actual
     */
    protected function assertIsNotInt($actual, string $message = '')
    {
        TestCase::assertIsNotInt($actual, $message);
    }

    /**
     * Asserts that a variable is not of type iterable.
     *
     * @param mixed $actual
     */
    protected function assertIsNotIterable($actual, string $message = '')
    {
        TestCase::assertIsNotIterable($actual, $message);
    }

    /**
     * Asserts that a variable is not of type numeric.
     *
     * @param mixed $actual
     */
    protected function assertIsNotNumeric($actual, string $message = '')
    {
        TestCase::assertIsNotNumeric($actual, $message);
    }

    /**
     * Asserts that a variable is not of type object.
     *
     * @param mixed $actual
     */
    protected function assertIsNotObject($actual, string $message = '')
    {
        TestCase::assertIsNotObject($actual, $message);
    }

    /**
     * Asserts that a file/dir exists and is not readable.
     */
    protected function assertIsNotReadable(string $filename, string $message = '')
    {
        TestCase::assertIsNotReadable($filename, $message);
    }

    /**
     * Asserts that a variable is not of type resource.
     *
     * @param mixed $actual
     */
    protected function assertIsNotResource($actual, string $message = '')
    {
        TestCase::assertIsNotResource($actual, $message);
    }

    /**
     * Asserts that a variable is not of type scalar.
     *
     * @param mixed $actual
     */
    protected function assertIsNotScalar($actual, string $message = '')
    {
        TestCase::assertIsNotScalar($actual, $message);
    }

    /**
     * Asserts that a variable is not of type string.
     *
     * @param mixed $actual
     */
    protected function assertIsNotString($actual, string $message = '')
    {
        TestCase::assertIsNotString($actual, $message);
    }

    /**
     * Asserts that a file/dir exists and is not writable.
     */
    protected function assertIsNotWritable(string $filename, string $message = '')
    {
        TestCase::assertIsNotWritable($filename, $message);
    }

    /**
     * Asserts that a variable is of type numeric.
     *
     * @param mixed $actual
     */
    protected function assertIsNumeric($actual, string $message = '')
    {
        TestCase::assertIsNumeric($actual, $message);
    }

    /**
     * Asserts that a variable is of type object.
     *
     * @param mixed $actual
     */
    protected function assertIsObject($actual, string $message = '')
    {
        TestCase::assertIsObject($actual, $message);
    }

    /**
     * Asserts that a file/dir is readable.
     */
    protected function assertIsReadable(string $filename, string $message = '')
    {
        TestCase::assertIsReadable($filename, $message);
    }

    /**
     * Asserts that a variable is of type resource.
     *
     * @param mixed $actual
     */
    protected function assertIsResource($actual, string $message = '')
    {
        TestCase::assertIsResource($actual, $message);
    }

    /**
     * Asserts that a variable is of type scalar.
     *
     * @param mixed $actual
     */
    protected function assertIsScalar($actual, string $message = '')
    {
        TestCase::assertIsScalar($actual, $message);
    }

    /**
     * Asserts that a variable is of type string.
     *
     * @param mixed $actual
     */
    protected function assertIsString($actual, string $message = '')
    {
        TestCase::assertIsString($actual, $message);
    }

    /**
     * Asserts that a file/dir exists and is writable.
     */
    protected function assertIsWritable(string $filename, string $message = '')
    {
        TestCase::assertIsWritable($filename, $message);
    }

    /**
     * Asserts that a string is a valid JSON string.
     */
    protected function assertJson(string $actualJson, string $message = '')
    {
        Assert::assertJson($actualJson, $message);
    }

    /**
     * Asserts that two JSON files are equal.
     */
    protected function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '')
    {
        Assert::assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message);
    }

    /**
     * Asserts that two JSON files are not equal.
     */
    protected function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '')
    {
        Assert::assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message);
    }

    /**
     * Asserts that the generated JSON encoded object and the content of the given file are equal.
     */
    protected function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '')
    {
        Assert::assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message);
    }

    /**
     * Asserts that two given JSON encoded objects or arrays are equal.
     */
    protected function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '')
    {
        Assert::assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message);
    }

    /**
     * Asserts that the generated JSON encoded object and the content of the given file are not equal.
     */
    protected function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '')
    {
        Assert::assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message);
    }

    /**
     * Asserts that two given JSON encoded objects or arrays are not equal.
     */
    protected function assertJsonStringNotEqualsJsonString(string $expectedJson, string $actualJson, string $message = '')
    {
        Assert::assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message);
    }

    /**
     * Asserts that a value is smaller than another value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertLessThan($expected, $actual, string $message = '')
    {
        Assert::assertLessThan($expected, $actual, $message);
    }

    /**
     * Asserts that a value is smaller than or equal to another value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertLessThanOrEqual($expected, $actual, string $message = '')
    {
        Assert::assertLessThanOrEqual($expected, $actual, $message);
    }

    /**
     * Asserts that a string matches a given regular expression.
     */
    protected function assertMatchesRegularExpression(string $pattern, string $string, string $message = '')
    {
        TestCase::assertMatchesRegularExpression($pattern, $string, $message);
    }

    /**
     * Asserts that a variable is nan.
     *
     * @param mixed $actual
     */
    protected function assertNan($actual, string $message = '')
    {
        Assert::assertNan($actual, $message);
    }

    /**
     * Asserts that a haystack does not contain a needle.
     *
     * @param mixed $needle
     */
    protected function assertNotContains($needle, iterable $haystack, string $message = '')
    {
        Assert::assertNotContains($needle, $haystack, $message);
    }

    protected function assertNotContainsEquals($needle, iterable $haystack, string $message = '')
    {
        Assert::assertNotContainsEquals($needle, $haystack, $message);
    }

    /**
     * Asserts that a haystack does not contain only values of a given type.
     */
    protected function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '')
    {
        Assert::assertNotContainsOnly($type, $haystack, $isNativeType, $message);
    }

    /**
     * Asserts the number of elements of an array, Countable or Traversable.
     *
     * @param \Countable|iterable $haystack
     */
    protected function assertNotCount(int $expectedCount, $haystack, string $message = '')
    {
        Assert::assertNotCount($expectedCount, $haystack, $message);
    }

    /**
     * Asserts that a variable is not empty.
     *
     * @param mixed $actual
     */
    protected function assertNotEmpty($actual, string $message = '')
    {
        Assert::assertNotEmpty($actual, $message);
    }

    /**
     * Asserts that two variables are not equal.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertNotEquals($expected, $actual, string $message = '')
    {
        TestCase::assertNotEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two variables are not equal (canonicalizing).
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertNotEqualsCanonicalizing($expected, $actual, string $message = '')
    {
        TestCase::assertNotEqualsCanonicalizing($expected, $actual, $message);
    }

    /**
     * Asserts that two variables are not equal (ignoring case).
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertNotEqualsIgnoringCase($expected, $actual, string $message = '')
    {
        TestCase::assertNotEqualsIgnoringCase($expected, $actual, $message);
    }

    /**
     * Asserts that two variables are not equal (with delta).
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '')
    {
        TestCase::assertNotEqualsWithDelta($expected, $actual, $delta, $message);
    }

    /**
     * Asserts that a condition is not false.
     *
     * @param mixed $condition
     */
    protected function assertNotFalse($condition, string $message = '')
    {
        Assert::assertNotFalse($condition, $message);
    }

    /**
     * Asserts that a variable is not of a given type.
     *
     * @param mixed $actual
     */
    protected function assertNotInstanceOf(string $expected, $actual, string $message = '')
    {
        Assert::assertNotInstanceOf($expected, $actual, $message);
    }

    /**
     * Asserts that a variable is not null.
     *
     * @param mixed $actual
     */
    protected function assertNotNull($actual, string $message = '')
    {
        Assert::assertNotNull($actual, $message);
    }

    /**
     * Asserts that two variables do not have the same type and value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertNotSame($expected, $actual, string $message = '')
    {
        Assert::assertNotSame($expected, $actual, $message);
    }

    /**
     * Assert that the size of two arrays (or `Countable` or `Traversable` objects) is not the same.
     *
     * @param \Countable|iterable $expected
     * @param \Countable|iterable $actual
     */
    protected function assertNotSameSize($expected, $actual, string $message = '')
    {
        Assert::assertNotSameSize($expected, $actual, $message);
    }

    /**
     * Asserts that a condition is not true.
     *
     * @param mixed $condition
     */
    protected function assertNotTrue($condition, string $message = '')
    {
        Assert::assertNotTrue($condition, $message);
    }

    /**
     * Asserts that a variable is null.
     *
     * @param mixed $actual
     */
    protected function assertNull($actual, string $message = '')
    {
        Assert::assertNull($actual, $message);
    }

    /**
     * Asserts that an object has a specified attribute.
     */
    protected function assertObjectHasAttribute(string $attributeName, object $object, string $message = '')
    {
        trigger_error(__FUNCTION__ . ' was removed from PHPUnit since PHPUnit 10', E_USER_DEPRECATED);

        if (method_exists(Assert::class, 'assertObjectHasAttribute')) {
            Assert::assertObjectHasAttribute($attributeName, $object, $message);
        } else {
            Assert::assertTrue(property_exists($object, $attributeName), $message);
        }
    }

    /**
     * Asserts that an object does not have a specified attribute.
     */
    protected function assertObjectNotHasAttribute(string $attributeName, object $object, string $message = '')
    {
        trigger_error(__FUNCTION__ . ' was removed from PHPUnit since PHPUnit 10', E_USER_DEPRECATED);

        if (method_exists(Assert::class, 'assertObjectNotHasAttribute')) {
            Assert::assertObjectNotHasAttribute($attributeName, $object, $message);
        } else {
            Assert::assertFalse(property_exists($object, $attributeName), $message);
        }
    }

    /**
     * Asserts that two variables have the same type and value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertSame($expected, $actual, string $message = '')
    {
        Assert::assertSame($expected, $actual, $message);
    }

    /**
     * Assert that the size of two arrays (or `Countable` or `Traversable` objects) is the same.
     *
     * @param \Countable|iterable $expected
     * @param \Countable|iterable $actual
     */
    protected function assertSameSize($expected, $actual, string $message = '')
    {
        Assert::assertSameSize($expected, $actual, $message);
    }

    protected function assertStringContainsString(string $needle, string $haystack, string $message = '')
    {
        TestCase::assertStringContainsString($needle, $haystack, $message);
    }

    protected function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '')
    {
        TestCase::assertStringContainsStringIgnoringCase($needle, $haystack, $message);
    }

    /**
     * Asserts that a string ends not with a given suffix.
     */
    protected function assertStringEndsNotWith(string $suffix, string $string, string $message = '')
    {
        TestCase::assertStringEndsNotWith($suffix, $string, $message);
    }

    /**
     * Asserts that a string ends with a given suffix.
     */
    protected function assertStringEndsWith(string $suffix, string $string, string $message = '')
    {
        TestCase::assertStringEndsWith($suffix, $string, $message);
    }

    /**
     * Asserts that the contents of a string is equal to the contents of a file.
     */
    protected function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '')
    {
        Assert::assertStringEqualsFile($expectedFile, $actualString, $message);
    }

    /**
     * Asserts that the contents of a string is equal to the contents of a file (canonicalizing).
     */
    protected function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '')
    {
        Assert::assertStringEqualsFileCanonicalizing($expectedFile, $actualString, $message);
    }

    /**
     * Asserts that the contents of a string is equal to the contents of a file (ignoring case).
     */
    protected function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '')
    {
        Assert::assertStringEqualsFileIgnoringCase($expectedFile, $actualString, $message);
    }

    /**
     * Asserts that a string matches a given format string.
     */
    protected function assertStringMatchesFormat(string $format, string $string, string $message = '')
    {
        Assert::assertStringMatchesFormat($format, $string, $message);
    }

    /**
     * Asserts that a string matches a given format file.
     */
    protected function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '')
    {
        Assert::assertStringMatchesFormatFile($formatFile, $string, $message);
    }

    protected function assertStringNotContainsString(string $needle, string $haystack, string $message = '')
    {
        TestCase::assertStringNotContainsString($needle, $haystack, $message);
    }

    protected function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '')
    {
        TestCase::assertStringNotContainsStringIgnoringCase($needle, $haystack, $message);
    }

    /**
     * Asserts that the contents of a string is not equal to the contents of a file.
     */
    protected function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '')
    {
        Assert::assertStringNotEqualsFile($expectedFile, $actualString, $message);
    }

    /**
     * Asserts that the contents of a string is not equal to the contents of a file (canonicalizing).
     */
    protected function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '')
    {
        Assert::assertStringNotEqualsFileCanonicalizing($expectedFile, $actualString, $message);
    }

    /**
     * Asserts that the contents of a string is not equal to the contents of a file (ignoring case).
     */
    protected function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '')
    {
        Assert::assertStringNotEqualsFileIgnoringCase($expectedFile, $actualString, $message);
    }

    /**
     * Asserts that a string does not match a given format string.
     */
    protected function assertStringNotMatchesFormat(string $format, string $string, string $message = '')
    {
        Assert::assertStringNotMatchesFormat($format, $string, $message);
    }

    /**
     * Asserts that a string does not match a given format string.
     */
    protected function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '')
    {
        Assert::assertStringNotMatchesFormatFile($formatFile, $string, $message);
    }

    /**
     * Asserts that a string starts not with a given prefix.
     */
    protected function assertStringStartsNotWith(string $prefix, string $string, string $message = '')
    {
        Assert::assertStringStartsNotWith($prefix, $string, $message);
    }

    /**
     * Asserts that a string starts with a given prefix.
     */
    protected function assertStringStartsWith(string $prefix, string $string, string $message = '')
    {
        Assert::assertStringStartsWith($prefix, $string, $message);
    }

    /**
     * Evaluates a PHPUnit\Framework\Constraint matcher object.
     *
     * @param mixed $value
     */
    protected function assertThat($value, PHPUnitConstraint $constraint, string $message = '')
    {
        Assert::assertThat($value, $constraint, $message);
    }

    /**
     * Asserts that a condition is true.
     *
     * @param mixed $condition
     */
    protected function assertTrue($condition, string $message = '')
    {
        Assert::assertTrue($condition, $message);
    }

    /**
     * Asserts that two XML files are equal.
     */
    protected function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '')
    {
        Assert::assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message);
    }

    /**
     * Asserts that two XML files are not equal.
     */
    protected function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '')
    {
        Assert::assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message);
    }

    /**
     * Asserts that two XML documents are equal.
     *
     * @param \DOMDocument|string $actualXml
     */
    protected function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '')
    {
        Assert::assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message);
    }

    /**
     * Asserts that two XML documents are equal.
     *
     * @param \DOMDocument|string $expectedXml
     * @param \DOMDocument|string $actualXml
     */
    protected function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '')
    {
        Assert::assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message);
    }

    /**
     * Asserts that two XML documents are not equal.
     *
     * @param \DOMDocument|string $actualXml
     */
    protected function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '')
    {
        Assert::assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message);
    }

    /**
     * Asserts that two XML documents are not equal.
     *
     * @param \DOMDocument|string $expectedXml
     * @param \DOMDocument|string $actualXml
     */
    protected function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '')
    {
        Assert::assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message);
    }

    /**
     * Fails a test with the given message.
     */
    protected function fail(string $message = '')
    {
        Assert::fail($message);
    }

    /**
     * Mark the test as incomplete.
     */
    protected function markTestIncomplete(string $message = '')
    {
        Assert::markTestIncomplete($message);
    }

    /**
     * Mark the test as skipped.
     */
    protected function markTestSkipped(string $message = '')
    {
        Assert::markTestSkipped($message);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Util\Shared;

use Codeception\PHPUnit\TestCase;
use PHPUnit\Framework\Assert as PHPUnitAssert;
use PHPUnit\Framework\Constraint\Constraint as PHPUnitConstraint;
use PHPUnit\Framework\Constraint\LogicalNot;

trait Asserts
{
    use InheritedAsserts;

    protected function assert(array $arguments, bool $not = false)
    {
        $not = $not ? 'Not' : '';
        $method = ucfirst(array_shift($arguments));
        if (($method === 'True') && $not) {
            $method = 'False';
            $not = '';
        }

        if (($method === 'False') && $not) {
            $method = 'True';
            $not = '';
        }

        call_user_func_array([PHPUnitAssert::class, 'assert' . $not . $method], $arguments);
    }

    protected function assertNot($arguments)
    {
        $this->assert($arguments, true);
    }

    /**
     * Asserts that a file does not exist.
     */
    protected function assertFileNotExists(string $filename, string $message = '')
    {
        TestCase::assertFileDoesNotExist($filename, $message);
    }

    /**
     * Asserts that a value is greater than or equal to another value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertGreaterOrEquals($expected, $actual, string $message = '')
    {
        TestCase::assertGreaterThanOrEqual($expected, $actual, $message);
    }

    /**
     * Asserts that a variable is empty.
     *
     * @param mixed $actual
     */
    protected function assertIsEmpty($actual, string $message = '')
    {
        TestCase::assertEmpty($actual, $message);
    }

    /**
     * Asserts that a value is smaller than or equal to another value.
     *
     * @param mixed $expected
     * @param mixed $actual
     */
    protected function assertLessOrEquals($expected, $actual, string $message = '')
    {
        TestCase::assertLessThanOrEqual($expected, $actual, $message);
    }

    /**
     * Asserts that a string does not match a given regular expression.
     */
    protected function assertNotRegExp(string $pattern, string $string, string $message = '')
    {
        TestCase::assertDoesNotMatchRegularExpression($pattern, $string, $message);
    }

    /**
     * Asserts that a string matches a given regular expression.
     */
    protected function assertRegExp(string $pattern, string $string, string $message = '')
    {
        TestCase::assertMatchesRegularExpression($pattern, $string, $message);
    }

    /**
     * Evaluates a PHPUnit\Framework\Constraint matcher object.
     *
     * @param mixed $value
     */
    protected function assertThatItsNot($value, PHPUnitConstraint $constraint, string $message = '')
    {
        $constraint = new LogicalNot($constraint);
        TestCase::assertThat($value, $constraint, $message);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\TestInterface;
use Codeception\Exception\ModuleException;

/**
 * This module interacts with the [Alternative PHP Cache (APC)](https://php.net/manual/en/intro.apcu.php)
 * using _APCu_ extension.
 *
 * Performs a cleanup by flushing all values after each test run.
 *
 * ## Status
 *
 * * Maintainer: **Serghei Iakovlev**
 * * Stability: **stable**
 * * Contact: serghei@phalcon.io
 *
 * ### Example (`unit.suite.yml`)
 *
 * ```yaml
 *    modules:
 *        - Apc
 * ```
 *
 * Be sure you don't use the production server to connect.
 *
 */
class Apc extends Module
{
    /**
     * Code to run before each test.
     *
     * @throws ModuleException
     */
    public function _before(TestInterface $test)
    {
        if (!extension_loaded('apcu')) {
            throw new ModuleException(
                __CLASS__,
                'The APCu extension not loaded.'
            );
        }

        if (!ini_get('apc.enabled') || (PHP_SAPI === 'cli' && !ini_get('apc.enable_cli'))) {
            throw new ModuleException(
                __CLASS__,
                'The "apc.enable_cli" parameter must be set to "On".'
            );
        }
    }

    /**
     * Code to run after each test.
     */
    public function _after(TestInterface $test)
    {
        $this->clear();
    }

    /**
     * Grabs value from APCu by key.
     *
     * Example:
     *
     * ``` php
     * <?php
     * $users_count = $I->grabValueFromApc('users_count');
     * ```
     */
    public function grabValueFromApc(string $key): mixed
    {
        $value = $this->fetch($key);
        $this->debugSection('Value', $value);

        return $value;
    }

    /**
     * Checks item in APCu exists and the same as expected.
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // With only one argument, only checks the key exists
     * $I->seeInApc('users_count');
     *
     * // Checks a 'users_count' exists and has the value 200
     * $I->seeInApc('users_count', 200);
     * ```
     *
     */
    public function seeInApc(string $key, mixed $value = null): void
    {
        if (null === $value) {
            $this->assertTrue($this->exists($key), "Cannot find key '$key' in APCu.");
            return;
        }

        $actual = $this->grabValueFromApc($key);
        $this->assertEquals($value, $actual, "Cannot find key '$key' in APCu with the provided value.");
    }

    /**
     * Checks item in APCu doesn't exist or is the same as expected.
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // With only one argument, only checks the key does not exist
     * $I->dontSeeInApc('users_count');
     *
     * // Checks a 'users_count' exists does not exist or its value is not the one provided
     * $I->dontSeeInApc('users_count', 200);
     * ```
     */
    public function dontSeeInApc(string $key, mixed $value = null): void
    {
        if (null === $value) {
            $this->assertFalse($this->exists($key), "The key '$key' exists in APCu.");
            return;
        }

        $actual = $this->grabValueFromApc($key);
        $this->assertFalse($actual, "The key '$key' exists in APCu with the provided value.");
    }

    /**
     * Stores an item `$value` with `$key` on the APCu.
     *
     * Examples:
     *
     * ```php
     * <?php
     * // Array
     * $I->haveInApc('users', ['name' => 'miles', 'email' => 'miles@davis.com']);
     *
     * // Object
     * $I->haveInApc('user', UserRepository::findFirst());
     *
     * // Key as array of 'key => value'
     * $entries = [];
     * $entries['key1'] = 'value1';
     * $entries['key2'] = 'value2';
     * $entries['key3'] = ['value3a','value3b'];
     * $entries['key4'] = 4;
     * $I->haveInApc($entries, null);
     * ```
     */
    public function haveInApc(string $key, mixed $value, int $expiration = 0): string
    {
        $this->store($key, $value, $expiration);

        return $key;
    }

    /**
     * Clears the APCu cache
     */
    public function flushApc(): void
    {
        // Returns TRUE always
        $this->clear();
    }

    /**
     * Clears the APCu cache.
     */
    protected function clear(): bool
    {
        return apcu_clear_cache();
    }

    /**
     * Checks if entry exists
     */
    protected function exists(string $key): bool
    {
        return apcu_exists($key);
    }

    /**
     * Fetch a stored variable from the cache
     */
    protected function fetch(string $key): mixed
    {
        $success = false;

        $data = apcu_fetch($key, $success);

        $this->debugSection('Fetching a stored variable', $success ? 'OK' : 'FAILED');

        return $data;
    }

    /**
     * Cache a variable in the data store.
     */
    protected function store(string $key, mixed $var, int $ttl = 0): bool
    {
        return apcu_store($key, $var, $ttl);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\Configuration;
use Codeception\Exception\ModuleConfigException;
use Codeception\Exception\ModuleException;
use Codeception\Lib\Driver\MongoDb as MongoDbDriver;
use Codeception\TestInterface;
use Exception;
use MongoConnectionException;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\ExpectationFailedException;

/**
 * Works with MongoDb database.
 *
 * The most important function of this module is cleaning database before each test.
 * To have your database properly cleaned you should configure it to access the database.
 *
 * In order to have your database populated with data you need a valid js file with data (of the same style which can be fed up to mongo binary)
 * File can be generated by RockMongo export command
 * You can also use directory, generated by ```mongodump``` tool or it's ```.tar.gz``` archive (not available for Windows systems), generated by ```tar -czf <archive_file_name>.tar.gz <path_to dump directory>```.
 * Just put it in ``` tests/_data ``` dir (by default) and specify path to it in config.
 * Next time after database is cleared all your data will be restored from dump.
 * The DB preparation should as following:
 * - clean database
 * - system collection system.users should contain the user which will be authenticated while script performs DB operations
 *
 * Connection is done by MongoDb driver, which is stored in Codeception\Lib\Driver namespace.
 * Check out the driver if you get problems loading dumps and cleaning databases.
 *
 * HINT: This module can be used with [Mongofill](https://github.com/mongofill/mongofill) library which is Mongo client written in PHP without extension.
 *
 * ## Status
 *
 * * Maintainer: **judgedim**, **davert**
 * * Stability: **beta**
 * * Contact: davert@codeception.com
 *
 * *Please review the code of non-stable modules and provide patches if you have issues.*
 *
 * ## Config
 *
 * * dsn *required* - MongoDb DSN with the db name specified at the end of the host after slash
 * * user *required* - user to access database
 * * password *required* - password
 * * dump_type *required* - type of dump.
 *   One of 'js' (MongoDb::DUMP_TYPE_JS), 'mongodump' (MongoDb::DUMP_TYPE_MONGODUMP) or 'mongodump-tar-gz' (MongoDb::DUMP_TYPE_MONGODUMP_TAR_GZ).
 *   default: MongoDb::DUMP_TYPE_JS).
 * * dump - path to database dump
 * * populate: true - should the dump be loaded before test suite is started.
 * * cleanup: true - should the dump be reloaded after each test.
 *   Boolean or 'dirty'. If cleanup is set to 'dirty', the dump is only reloaded if any data has been written to the db during a test. This is
 *   checked using the [dbHash](https://docs.mongodb.com/manual/reference/command/dbHash/) command.
 *
 */
class MongoDb extends Module
{
    /**
     * @var string
     */
    public const DUMP_TYPE_JS = 'js';

    /**
     * @var string
     */
    public const DUMP_TYPE_MONGODUMP = 'mongodump';

    /**
     * @var string
     */
    public const DUMP_TYPE_MONGODUMP_TAR_GZ = 'mongodump-tar-gz';

    protected string $dumpFile;

    protected bool $isDumpFileEmpty = true;

    protected ?string $dbHash;

    /**
     * @var array<string, mixed>
     */
    protected array $config = [
        'populate'  => true,
        'cleanup'   => true,
        'dsn'       => '',
        'dump'      => null,
        'dump_type' => self::DUMP_TYPE_JS,
        'user'      => '',
        'password'  => '',
        'quiet'     => false,
    ];

    protected bool $populated = false;

    public ?MongoDbDriver $driver = null;

    /**
     * @var string[]
     */
    protected array $requiredFields = ['dsn'];

    public function _initialize(): void
    {
        try {
            $this->driver = MongoDbDriver::create(
                $this->config['dsn'],
                $this->config['user'],
                $this->config['password']
            );
        } catch (MongoConnectionException $exception) {
            throw new ModuleException(__CLASS__,
                $exception->getMessage() . ' while creating Mongo connection'
            );
        }

        if ($this->config['populate']) {
            $this->cleanup();
            $this->loadDump();
            $this->populated = true;
        }
    }

    private function validateDump(): void
    {
        if ($this->config['dump'] && ($this->config['cleanup'] || $this->config['populate'])) {
            if (!file_exists(Configuration::projectDir() . $this->config['dump'])) {
                throw new ModuleConfigException(
                    __CLASS__,
                    "File with dump doesn't exist.\n
                    Please, check path for dump file: " . $this->config['dump']
                );
            }

            $this->dumpFile = Configuration::projectDir() . $this->config['dump'];
            $this->isDumpFileEmpty = false;

            if ($this->config['dump_type'] === self::DUMP_TYPE_JS) {
                $content = file_get_contents($this->dumpFile);
                $content = trim(preg_replace('#/\*(?:(?!\*/).)*\*/#s', "", $content));
                if (count(explode("\n", $content)) === 0) {
                    $this->isDumpFileEmpty = true;
                }

                return;
            }

            if ($this->config['dump_type'] === self::DUMP_TYPE_MONGODUMP) {
                if (!is_dir($this->dumpFile)) {
                    throw new ModuleConfigException(
                        __CLASS__,
                        "Dump must be a directory.\n
                        Please, check dump: " . $this->config['dump']
                    );
                }

                $this->isDumpFileEmpty = true;
                $dumpDir = dir($this->dumpFile);
                while (false !== ($entry = $dumpDir->read())) {
                    if ($entry !== '..' && $entry !== '.') {
                        $this->isDumpFileEmpty = false;
                        break;
                    }
                }

                $dumpDir->close();
                return;
            }

            if ($this->config['dump_type'] === self::DUMP_TYPE_MONGODUMP_TAR_GZ) {
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                    throw new ModuleConfigException(
                        __CLASS__,
                        "Tar gunzip archives are not supported for Windows systems"
                    );
                }

                if (!preg_match('#(\.tar\.gz|\.tgz)$#', $this->dumpFile)) {
                    throw new ModuleConfigException(
                        __CLASS__,
                        "Dump file must be a valid tar gunzip archive.\n
                        Please, check dump file: " . $this->config['dump']
                    );
                }

                return;
            }

            throw new ModuleConfigException(
                __CLASS__,
                '\"dump_type\" must be one of ["'
                . self::DUMP_TYPE_JS . '", "'
                . self::DUMP_TYPE_MONGODUMP . '", "'
                . self::DUMP_TYPE_MONGODUMP_TAR_GZ . '"].'
            );
        }
    }

    public function _before(TestInterface $test): void
    {
        if ($this->shouldCleanup()) {
            $this->cleanup();
            $this->loadDump();
        }
    }

    public function _after(TestInterface $test): void
    {
        $this->populated = false;
    }

    protected function shouldCleanup(): bool
    {
        if ($this->populated) {
            return false;
        }

        return $this->config['cleanup'] === 'dirty'
            ? ($this->dbHash === null || $this->driver->getDbHash() !== $this->dbHash)
            : (bool)$this->config['cleanup'];
    }

    protected function cleanup(): void
    {
        $dbh = $this->driver->getDbh();
        if ($dbh === null) {
            throw new ModuleConfigException(
                __CLASS__,
                "No connection to database. Remove this module from config if you don't need database repopulation"
            );
        }

        try {
            $this->driver->cleanup();
        } catch (Exception $exception) {
            throw new ModuleException(__CLASS__, $exception->getMessage());
        }
    }

    protected function loadDump(): void
    {
        $this->validateDump();

        if ($this->isDumpFileEmpty) {
            return;
        }

        try {
            if ($this->config['dump_type'] === self::DUMP_TYPE_JS) {
                $this->driver->load($this->dumpFile);
            }

            if ($this->config['dump_type'] === self::DUMP_TYPE_MONGODUMP) {
                $this->driver->setQuiet($this->config['quiet']);
                $this->driver->loadFromMongoDump($this->dumpFile);
            }

            if ($this->config['dump_type'] === self::DUMP_TYPE_MONGODUMP_TAR_GZ) {
                $this->driver->setQuiet($this->config['quiet']);
                $this->driver->loadFromTarGzMongoDump($this->dumpFile);
            }
        } catch (Exception $e) {
            throw new ModuleException(__CLASS__, $e->getMessage());
        }

        if ($this->config['cleanup'] === 'dirty') {
            $this->dbHash = $this->driver->getDbHash();
        }
    }

    /**
     * Specify the database to use
     *
     * ```php
     * <?php
     * $I->useDatabase('db_1');
     * ```
     */
    public function useDatabase(string $dbName): void
    {
        $this->driver->setDatabase($dbName);
    }

    /**
     * Inserts data into collection
     *
     * ```php
     * <?php
     * $I->haveInCollection('users', ['name' => 'John', 'email' => 'john@coltrane.com']);
     * $user_id = $I->haveInCollection('users', ['email' => 'john@coltrane.com']);
     * ```
     *
     * @param array<string, mixed> $data
     */
    public function haveInCollection(string $collection, array $data): string
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);

        $response = $collection->insertOne($data);
        return (string) $response->getInsertedId();
    }

    /**
     * Checks if collection contains an item.
     *
     * ```php
     * <?php
     * $I->seeInCollection('users', ['name' => 'miles']);
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function seeInCollection(string $collection, array $criteria = []): void
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);
        $res = $collection->count($criteria);
        Assert::assertGreaterThan(0, $res);
    }

    /**
     * Checks if collection doesn't contain an item.
     *
     * ```php
     * <?php
     * $I->dontSeeInCollection('users', ['name' => 'miles']);
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function dontSeeInCollection(string $collection, array $criteria = []): void
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);
        $res = $collection->count($criteria);
        Assert::assertLessThan(1, $res);
    }

    /**
     * Grabs a data from collection
     *
     * ```php
     * <?php
     * $user = $I->grabFromCollection('users', ['name' => 'miles']);
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function grabFromCollection(string $collection, array $criteria = []): array|object|null
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);
        return $collection->findOne($criteria);
    }

    /**
     * Grabs the documents count from a collection
     *
     * ```php
     * <?php
     * $count = $I->grabCollectionCount('users');
     * // or
     * $count = $I->grabCollectionCount('users', ['isAdmin' => true]);
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function grabCollectionCount(string $collection, array $criteria = []): int
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);
        return $collection->count($criteria);
    }

    /**
     * Asserts that an element in a collection exists and is an Array
     *
     * ```php
     * <?php
     * $I->seeElementIsArray('users', ['name' => 'John Doe'], 'data.skills');
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function seeElementIsArray(string $collection, array $criteria, string $elementToCheck): void
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);

        $res = $collection->count(
            array_merge(
                $criteria,
                [
                    $elementToCheck => ['$exists' => true],
                    '$where' => sprintf('Array.isArray(this.%s)', $elementToCheck)
                ]
            )
        );
        if ($res > 1) {
            throw new ExpectationFailedException(
                'Error: you should test against a single element criteria when asserting that elementIsArray'
            );
        }
        Assert::assertSame(1, $res, 'Specified element is not a Mongo Object');
    }

    /**
     * Asserts that an element in a collection exists and is an Object
     *
     * ```php
     * <?php
     * $I->seeElementIsObject('users', ['name' => 'John Doe'], 'data');
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function seeElementIsObject(string $collection, array $criteria, string $elementToCheck): void
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);

        $res = $collection->count(
            array_merge(
                $criteria,
                [
                    $elementToCheck => ['$exists' => true],
                    '$where' => sprintf('! Array.isArray(this.%s) && isObject(this.%s)', $elementToCheck, $elementToCheck)
                ]
            )
        );
        if ($res > 1) {
            throw new ExpectationFailedException(
                'Error: you should test against a single element criteria when asserting that elementIsObject'
            );
        }
        Assert::assertSame(1, $res, 'Specified element is not a Mongo Object');
    }

    /**
     * Count number of records in a collection
     *
     * ```php
     * <?php
     * $I->seeNumElementsInCollection('users', 2);
     * $I->seeNumElementsInCollection('users', 1, ['name' => 'miles']);
     * ```
     *
     * @param array<string, mixed> $criteria
     */
    public function seeNumElementsInCollection(string $collection, int $expected, array $criteria = []): void
    {
        $collection = $this->driver->getDbh()->selectCollection($collection);
        $res = $collection->count($criteria);
        Assert::assertSame($expected, $res);
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Lib\Driver;

use Codeception\Exception\ModuleConfigException;
use Codeception\Exception\ModuleException;
use Exception;
use MongoDB\Client;
use MongoDB\Database;
use MongoDB\Driver\Exception\Exception as MongoDbDriverException;

class MongoDb
{
    /**
     * @var int
     */
    public const DEFAULT_PORT = 27017;

    private ?Client $client;
    private Database $dbh;
    private ?string $dbName;
    private string $host;
    private string $user;
    private string $password;
    private string $quiet = '';

    /**
     * Connect to the Mongo server using the MongoDB extension.
     */
    protected function setupMongoDB(string $dsn, array $options): void
    {
        try {
            $this->client = new Client($dsn, $options);
            $this->dbh    = $this->client->selectDatabase($this->dbName);
        } catch (MongoDbDriverException $e) {
            throw new ModuleException($this, sprintf('Failed to open Mongo connection: %s', $e->getMessage()));
        }
    }

    /**
     * Clean up the Mongo database using the MongoDB extension.
     */
    protected function cleanupMongoDB(): void
    {
        try {
            $this->dbh->drop();
        } catch (MongoDbDriverException $e) {
            throw new Exception(sprintf('Failed to drop the DB: %s', $e->getMessage()), $e->getCode(), $e);
        }
    }

    /**
     * $dsn has to contain db_name after the host. E.g. "mongodb://localhost:27017/mongo_test_db"
     *
     * @static
     *
     * @throws ModuleConfigException
     * @throws Exception
     */
    public function __construct(string $dsn, string $user, string $password)
    {
        /* defining DB name */
        $this->dbName = preg_replace('#\?.*#', '', substr($dsn, strrpos($dsn, '/') + 1));

        if (strlen($this->dbName) == 0) {
            throw new ModuleConfigException($this, 'Please specify valid $dsn with DB name after the host:port');
        }

        /* defining host */
        if (str_starts_with($dsn, 'mongodb://')) {
            $this->host = str_replace('mongodb://', '', preg_replace('#\?.*#', '', $dsn));
        } else {
            $this->host = $dsn;
        }
        $this->host = rtrim(str_replace($this->dbName, '', $this->host), '/');

        $options = [
            'connect' => true
        ];

        if ($user && $password) {
            $options += [
                'username' => $user,
                'password' => $password
            ];
        }

        $this->setupMongoDB($dsn, $options);
        $this->user = $user;
        $this->password = $password;
    }

    /**
     * @static
     */
    public static function create(string $dsn, string $user, string $password): \Codeception\Lib\Driver\MongoDb
    {
        return new MongoDb($dsn, $user, $password);
    }

    public function cleanup(): void
    {
        $this->cleanupMongoDB();
    }

    /**
     * dump file has to be a javascript document where one can use all the mongo shell's commands
     * just FYI: this file can be easily created be RockMongo's export button
     */
    public function load(string $dumpFile): void
    {
        $cmd = sprintf(
            'mongo %s %s%s',
            $this->host . '/' . $this->dbName,
            $this->createUserPasswordCmdString(),
            escapeshellarg($dumpFile)
        );
        shell_exec($cmd);
    }

    public function loadFromMongoDump(string $dumpFile): void
    {
        [$host, $port] = $this->getHostPort();
        $cmd = sprintf(
            "mongorestore %s --host %s --port %s -d %s %s %s",
            $this->quiet,
            $host,
            $port,
            $this->dbName,
            $this->createUserPasswordCmdString(),
            escapeshellarg($dumpFile)
        );
        shell_exec($cmd);
    }

    public function loadFromTarGzMongoDump(string $dumpFile): void
    {
        [$host, $port] = $this->getHostPort();
        $getDirCmd = sprintf(
            "tar -tf %s | awk 'BEGIN { FS = \"/\" } ; { print $1 }' | uniq",
            escapeshellarg($dumpFile)
        );
        $dirCountCmd = $getDirCmd . ' | wc -l';
        if (trim(shell_exec($dirCountCmd)) !== '1') {
            throw new ModuleException(
                $this,
                'Archive MUST contain single directory with db dump'
            );
        }
        $dirName = trim(shell_exec($getDirCmd));
        $cmd = sprintf(
            'tar -xzf %s && mongorestore %s --host %s --port %s -d %s %s %s && rm -r %s',
            escapeshellarg($dumpFile),
            $this->quiet,
            $host,
            $port,
            $this->dbName,
            $this->createUserPasswordCmdString(),
            $dirName,
            $dirName
        );
        shell_exec($cmd);
    }

    private function createUserPasswordCmdString(): string
    {
        if ($this->user && $this->password) {
            return sprintf(
                '--username %s --password %s ',
                $this->user,
                $this->password
            );
        }
        return '';
    }

    public function getDbh(): Database
    {
        return $this->dbh;
    }

    public function setDatabase(string $dbName): void
    {
        $this->dbh = $this->client->selectDatabase($dbName);
    }

    public function getDbHash(): ?string
    {
        $result = $this->dbh->command(['dbHash' => 1]);

        if (!is_array($result)) {
            $result = iterator_to_array($result);
        }

        return $result[0]->md5 ?? null;
    }

    /**
     * @return array<string|int>
     */
    private function getHostPort(): array
    {
        $hostPort = explode(':', $this->host);
        if (count($hostPort) === 2) {
            return $hostPort;
        }
        if (count($hostPort) === 1) {
            return [$hostPort[0], self::DEFAULT_PORT];
        }
        throw new ModuleException($this, '$dsn MUST be like (mongodb://)<host>:<port>/<db name>');
    }

    public function setQuiet(bool $quiet): void
    {
        $this->quiet = $quiet ? '--quiet' : '';
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Module;
use Codeception\Exception\ModuleException;
use Codeception\TestInterface;
use Exception;
use PHPUnit\Framework\ExpectationFailedException;
use Predis\Client as RedisDriver;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;

/**
 * This module uses the [Predis](https://github.com/nrk/predis) library
 * to interact with a Redis server.
 *
 * ## Status
 *
 * * Stability: **beta**
 *
 * ## Configuration
 *
 * * **`host`** (`string`, default `'127.0.0.1'`) - The Redis host
 * * **`port`** (`int`, default `6379`) - The Redis port
 * * **`database`** (`int`, no default) - The Redis database. Needs to be specified.
 * * **`username`** (`string`, no default) - When ACLs are enabled on Redis >= 6.0, both username and password are required for user authentication.
 * * **`password`** (`string`, no default) - The Redis password/secret.
 * * **`cleanupBefore`**: (`string`, default `'never'`) - Whether/when to flush the database:
 *     * `suite`: at the beginning of every suite
 *     * `test`: at the beginning of every test
 *     * Any other value: never
 *
 * Note: The full configuration list can be found on Predis' github.
 *
 * ### Example (`unit.suite.yml`)
 *
 * ```yaml
 *    modules:
 *        - Redis:
 *            host: '127.0.0.1'
 *            port: 6379
 *            database: 0
 *            cleanupBefore: 'never'
 * ```
 *
 * ## Public Properties
 *
 * * **driver** - Contains the Predis client/driver
 *
 * @author Marc Verney <marc@marcverney.net>
 */
class Redis extends Module
{
    /**
     * {@inheritdoc}
     *
     * No default value is set for the database, using this parameter.
     *
     * @var array>string, mixed>
     */
    protected array $config = [
        'host'          => '127.0.0.1',
        'port'          => 6379,
        'cleanupBefore' => 'never'
    ];

    /**
     * {@inheritdoc}
     *
     * @var string[]
     */
    protected array $requiredFields = [
        'database'
    ];

    /**
     * The Redis driver
     */
    public ?RedisDriver $driver = null;

    /**
     * Instructions to run after configuration is loaded
     *
     * @throws ModuleException
     */
    public function _initialize(): void
    {
        try {
            $this->driver = new RedisDriver($this->config);
        } catch (Exception $exception) {
            throw new ModuleException(
                __CLASS__,
                $exception->getMessage()
            );
        }
    }

    /**
     * Code to run before each suite
     *
     * @param array<string, mixed> $settings
     */
    public function _beforeSuite($settings = []): void
    {
        if ($this->config['cleanupBefore'] === 'suite') {
            $this->cleanup();
        }
    }

    /**
     * Code to run before each test
     */
    public function _before(TestInterface $test): void
    {
        if ($this->config['cleanupBefore'] === 'test') {
            $this->cleanup();
        }
    }

    /**
     * Delete all the keys in the Redis database
     *
     * @throws ModuleException
     */
    public function cleanup(): void
    {
        try {
            $this->debugSection('Redis', 'Performing cleanup');
            $this->driver->flushdb();
        } catch (Exception $e) {
            throw new ModuleException(
                __CLASS__,
                $e->getMessage()
            );
        }
    }

    /**
     * Returns the value of a given key
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // Strings
     * $I->grabFromRedis('string');
     *
     * // Lists: get all members
     * $I->grabFromRedis('example:list');
     *
     * // Lists: get a specific member
     * $I->grabFromRedis('example:list', 2);
     *
     * // Lists: get a range of elements
     * $I->grabFromRedis('example:list', 2, 4);
     *
     * // Sets: get all members
     * $I->grabFromRedis('example:set');
     *
     * // ZSets: get all members
     * $I->grabFromRedis('example:zset');
     *
     * // ZSets: get a range of members
     * $I->grabFromRedis('example:zset', 3, 12);
     *
     * // Hashes: get all fields of a key
     * $I->grabFromRedis('example:hash');
     *
     * // Hashes: get a specific field of a key
     * $I->grabFromRedis('example:hash', 'foo');
     * ```
     *
     * @param string $key The key name
     *
     * @throws ModuleException if the key does not exist
     */
    public function grabFromRedis(string $key): array|string|null
    {
        $args = func_get_args();

        switch ($this->driver->type($key)) {
            case 'none':
                throw new ModuleException(
                    $this,
                    sprintf('Cannot grab key "%s" as it does not exist', $key)
                );

            case 'string':
                $reply = $this->driver->get($key);
                break;

            case 'list':
                if (count($args) === 2) {
                    $reply = $this->driver->lindex($key, $args[1]);
                } else {
                    $reply = $this->driver->lrange(
                        $key,
                        $args[1] ?? 0,
                        $args[2] ?? -1
                    );
                }

                break;

            case 'set':
                $reply = $this->driver->smembers($key);
                break;

            case 'zset':
                if (count($args) === 2) {
                    throw new ModuleException(
                        $this,
                        'The method grabFromRedis(), when used with sorted sets, expects either one argument or three'
                    );
                }

                $reply = $this->driver->zrange(
                    $key,
                    isset($args[2]) ? $args[1] : 0,
                    $args[2] ?? -1,
                    'WITHSCORES'
                );
                break;

            case 'hash':
                $reply = isset($args[1])
                    ? $this->driver->hget($key, $args[1])
                    : $this->driver->hgetall($key);
                break;

            default:
                $reply = null;
        }

        return $reply;
    }

    /**
     * Creates or modifies keys
     *
     * If $key already exists:
     *
     * - Strings: its value will be overwritten with $value
     * - Other types: $value items will be appended to its value
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // Strings: $value must be a scalar
     * $I->haveInRedis('string', 'Obladi Oblada');
     *
     * // Lists: $value can be a scalar or an array
     * $I->haveInRedis('list', ['riri', 'fifi', 'loulou']);
     *
     * // Sets: $value can be a scalar or an array
     * $I->haveInRedis('set', ['riri', 'fifi', 'loulou']);
     *
     * // ZSets: $value must be an associative array with scores
     * $I->haveInRedis('zset', ['riri' => 1, 'fifi' => 2, 'loulou' => 3]);
     *
     * // Hashes: $value must be an associative array
     * $I->haveInRedis('hash', ['obladi' => 'oblada']);
     * ```
     *
     * @param string $type  The type of the key
     * @param string $key   The key name
     * @param mixed  $value The value
     *
     * @throws ModuleException
     */
    public function haveInRedis(string $type, string $key, mixed $value): void
    {
        switch (strtolower($type)) {
            case 'string':
                if (!is_scalar($value)) {
                    throw new ModuleException(
                        $this,
                        'If second argument of haveInRedis() method is "string", third argument must be a scalar'
                    );
                }

                $this->driver->set($key, $value);
                break;

            case 'list':
                $this->driver->rpush($key, $value);
                break;

            case 'set':
                $this->driver->sadd($key, $value);
                break;

            case 'zset':
                if (!is_array($value)) {
                    throw new ModuleException(
                        $this,
                        'If second argument of haveInRedis() method is "zset", third argument must be an (associative) array'
                    );
                }

                $this->driver->zadd($key, $value);
                break;

            case 'hash':
                if (!is_array($value)) {
                    throw new ModuleException(
                        $this,
                        'If second argument of haveInRedis() method is "hash", third argument must be an array'
                    );
                }

                $this->driver->hmset($key, $value);
                break;

            default:
                throw new ModuleException(
                    $this,
                    sprintf('Unknown type "%s" for key "%s". Allowed types are ', $type, $key)
                    . '"string", "list", "set", "zset", "hash"'
                );
        }
    }

    /**
     * Asserts that a key does not exist or, optionally, that it doesn't have the
     * provided $value
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // With only one argument, only checks the key does not exist
     * $I->dontSeeInRedis('example:string');
     *
     * // Checks a String does not exist or its value is not the one provided
     * $I->dontSeeInRedis('example:string', 'life');
     *
     * // Checks a List does not exist or its value is not the one provided (order of elements is compared).
     * $I->dontSeeInRedis('example:list', ['riri', 'fifi', 'loulou']);
     *
     * // Checks a Set does not exist or its value is not the one provided (order of members is ignored).
     * $I->dontSeeInRedis('example:set', ['riri', 'fifi', 'loulou']);
     *
     * // Checks a ZSet does not exist or its value is not the one provided (scores are required, order of members is compared)
     * $I->dontSeeInRedis('example:zset', ['riri' => 1, 'fifi' => 2, 'loulou' => 3]);
     *
     * // Checks a Hash does not exist or its value is not the one provided (order of members is ignored).
     * $I->dontSeeInRedis('example:hash', ['riri' => true, 'fifi' => 'Dewey', 'loulou' => 2]);
     * ```
     *
     * @param string $key   The key name
     * @param mixed  $value Optional. If specified, also checks the key has this
     * value. Booleans will be converted to 1 and 0 (even inside arrays)
     */
    public function dontSeeInRedis(string $key, mixed $value = null): void
    {
        try {
            $this->assertFalse(
                $this->checkKeyExists($key, $value),
                sprintf('The key "%s" exists', $key) . ($value ? ' and its value matches the one provided' : '')
            );
        } catch (ComparisonFailure $failure) {
            // values are different
            $this->assertFalse(false);
        }
    }

    /**
     * Asserts that a given key does not contain a given item
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // Strings: performs a substring search
     * $I->dontSeeRedisKeyContains('string', 'bar');
     *
     * // Lists
     * $I->dontSeeRedisKeyContains('example:list', 'poney');
     *
     * // Sets
     * $I->dontSeeRedisKeyContains('example:set', 'cat');
     *
     * // ZSets: check whether the zset has this member
     * $I->dontSeeRedisKeyContains('example:zset', 'jordan');
     *
     * // ZSets: check whether the zset has this member with this score
     * $I->dontSeeRedisKeyContains('example:zset', 'jordan', 23);
     *
     * // Hashes: check whether the hash has this field
     * $I->dontSeeRedisKeyContains('example:hash', 'magic');
     *
     * // Hashes: check whether the hash has this field with this value
     * $I->dontSeeRedisKeyContains('example:hash', 'magic', 32);
     * ```
     *
     * @param string $key       The key
     * @param mixed  $item      The item
     * @param mixed  $itemValue Optional and only used for zsets and hashes. If
     * specified, the method will also check that the $item has this value/score
     */
    public function dontSeeRedisKeyContains(string $key, mixed $item, mixed $itemValue = null): void
    {
        $this->assertFalse(
            $this->checkKeyContains($key, $item, $itemValue),
            sprintf('The key "%s" contains ', $key) . (
                is_null($itemValue)
                ? sprintf('"%s"', $item)
                : sprintf('["%s" => "%s"]', $item, $itemValue)
            )
        );
    }

    /**
     * Asserts that a key exists, and optionally that it has the provided $value
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // With only one argument, only checks the key exists
     * $I->seeInRedis('example:string');
     *
     * // Checks a String exists and has the value "life"
     * $I->seeInRedis('example:string', 'life');
     *
     * // Checks the value of a List. Order of elements is compared.
     * $I->seeInRedis('example:list', ['riri', 'fifi', 'loulou']);
     *
     * // Checks the value of a Set. Order of members is ignored.
     * $I->seeInRedis('example:set', ['riri', 'fifi', 'loulou']);
     *
     * // Checks the value of a ZSet. Scores are required. Order of members is compared.
     * $I->seeInRedis('example:zset', ['riri' => 1, 'fifi' => 2, 'loulou' => 3]);
     *
     * // Checks the value of a Hash. Order of members is ignored.
     * $I->seeInRedis('example:hash', ['riri' => true, 'fifi' => 'Dewey', 'loulou' => 2]);
     * ```
     *
     * @param string $key   The key name
     * @param mixed  $value Optional. If specified, also checks the key has this
     * value. Booleans will be converted to 1 and 0 (even inside arrays)
     */
    public function seeInRedis(string $key, mixed $value = null): void
    {
        try {
            $this->assertTrue(
                $this->checkKeyExists($key, $value),
                sprintf('Cannot find key "%s"', $key)
            );
        } catch (ComparisonFailure $failure) {
            throw new ExpectationFailedException(
                sprintf("Value of key \"%s\" does not match expected value", $key),
                $failure
            );
        }
    }

    /**
     * Sends a command directly to the Redis driver. See documentation at
     * https://github.com/nrk/predis
     * Every argument that follows the $command name will be passed to it.
     *
     * Examples:
     *
     * ``` php
     * <?php
     * $I->sendCommandToRedis('incr', 'example:string');
     * $I->sendCommandToRedis('strLen', 'example:string');
     * $I->sendCommandToRedis('lPop', 'example:list');
     * $I->sendCommandToRedis('zRangeByScore', 'example:set', '-inf', '+inf', ['withscores' => true, 'limit' => [1, 2]]);
     * $I->sendCommandToRedis('flushdb');
     * ```
     *
     * @param string $command The command name
     * @return mixed
     */
    public function sendCommandToRedis(string $command): mixed
    {
        return call_user_func_array(
            [$this->driver, $command],
            array_slice(func_get_args(), 1)
        );
    }

    /**
     * Asserts that a given key contains a given item
     *
     * Examples:
     *
     * ``` php
     * <?php
     * // Strings: performs a substring search
     * $I->seeRedisKeyContains('example:string', 'bar');
     *
     * // Lists
     * $I->seeRedisKeyContains('example:list', 'poney');
     *
     * // Sets
     * $I->seeRedisKeyContains('example:set', 'cat');
     *
     * // ZSets: check whether the zset has this member
     * $I->seeRedisKeyContains('example:zset', 'jordan');
     *
     * // ZSets: check whether the zset has this member with this score
     * $I->seeRedisKeyContains('example:zset', 'jordan', 23);
     *
     * // Hashes: check whether the hash has this field
     * $I->seeRedisKeyContains('example:hash', 'magic');
     *
     * // Hashes: check whether the hash has this field with this value
     * $I->seeRedisKeyContains('example:hash', 'magic', 32);
     * ```
     *
     * @param string $key       The key
     * @param mixed  $item      The item
     * @param mixed  $itemValue Optional and only used for zsets and hashes. If
     * specified, the method will also check that the $item has this value/score
     */
    public function seeRedisKeyContains(string $key, mixed $item, mixed $itemValue = null): void
    {
        $this->assertTrue(
            $this->checkKeyContains($key, $item, $itemValue),
            sprintf('The key "%s" does not contain ', $key) . (
            is_null($itemValue)
                ? sprintf('"%s"', $item)
                : sprintf('["%s" => "%s"]', $item, $itemValue)
            )
        );
    }

    /**
     * Converts boolean values to "0" and "1"
     */
    private function boolToString(mixed $var): mixed
    {
        $copy = is_array($var) ? $var : [$var];

        foreach ($copy as $key => $value) {
            if (is_bool($value)) {
                $copy[$key] = $value ? '1' : '0';
            }
        }

        return is_array($var) ? $copy : $copy[0];
    }

    /**
     * Checks whether a key contains a given item
     *
     * @param string $key       The key
     * @param mixed  $item      The item
     * @param mixed   $itemValue Optional and only used for zsets and hashes. If
     * specified, the method will also check that the $item has this value/score
     *
     * @throws ModuleException
     */
    private function checkKeyContains(string $key, mixed $item, mixed $itemValue = null): bool
    {
        $result = null;

        if (!is_scalar($item)) {
            throw new ModuleException(
                $this,
                "All arguments of [dont]seeRedisKeyContains() must be scalars"
            );
        }

        switch ($this->driver->type($key)) {
            case 'string':
                $reply = $this->driver->get($key);
                $result = strpos($reply, (string) $item) !== false;
                break;

            case 'list':
                $reply = $this->driver->lrange($key, 0, -1);
                $result = in_array($item, $reply);
                break;

            case 'set':
                $result = $this->driver->sismember($key, $item);
                break;

            case 'zset':
                $reply = $this->driver->zscore($key, $item);

                if (is_null($reply)) {
                    $result = false;
                } elseif (!is_null($itemValue)) {
                    $result = (float) $reply === (float) $itemValue;
                } else {
                    $result = true;
                }

                break;

            case 'hash':
                $reply = $this->driver->hget($key, $item);

                $result = is_null($itemValue)
                    ? !is_null($reply)
                    : (string) $reply === (string) $itemValue;
                break;

            case 'none':
                throw new ModuleException(
                    $this,
                    sprintf('Key "%s" does not exist', $key)
                );
        }

        return (bool) $result;
    }

    /**
     * Checks whether a key exists and, optionally, whether it has a given $value
     *
     * @param string $key   The key name
     * @param mixed  $value Optional. If specified, also checks the key has this
     * value. Booleans will be converted to 1 and 0 (even inside arrays)
     */
    private function checkKeyExists(string $key, mixed $value): bool
    {
        $type = $this->driver->type($key);

        if ($type == 'none') {
            return false;
        }

        if (is_null($value)) {
            return true;
        }

        $value = $this->boolToString($value);

        switch ($type) {
            case 'string':
                $reply = $this->driver->get($key);
                // Allow non strict equality (2 equals '2')
                $result = $reply == $value;
                break;

            case 'list':
                $reply = $this->driver->lrange($key, 0, -1);
                // Check both arrays have the same key/value pairs + same order
                $result = $reply === $value;
                break;

            case 'set':
                $reply = $this->driver->smembers($key);
                // Only check both arrays have the same values
                sort($reply);
                sort($value);
                $result = $reply === $value;
                break;

            case 'zset':
                $reply = $this->driver->zrange($key, 0, -1, 'WITHSCORES');
                // Check both arrays have the same key/value pairs + same order
                $reply = $this->scoresToFloat($reply);
                $value = $this->scoresToFloat($value);
                $result = $reply === $value;
                break;

            case 'hash':
                $reply = $this->driver->hgetall($key);
                // Only check both arrays have the same key/value pairs (==)
                $result = $reply == $value;
                break;

            default:
                throw new ModuleException(
                    $this,
                    sprintf("Unexpected value type %s", $type)
                );
        }

        if (!$result) {
            $comparatorFactory = new ComparatorFactory();
            $comparator = $comparatorFactory->getComparatorFor($value, $reply);
            $comparator->assertEquals($value, $reply);

            if ($type == 'zset') {
                /**
                 * ArrayComparator considers out of order assoc arrays as equal
                 * So we have to compare them as strings
                 */
                $replyAsString = var_export($reply, true);
                $valueAsString = var_export($value, true);
                $comparator = $comparatorFactory->getComparatorFor($valueAsString, $replyAsString);
                $comparator->assertEquals($valueAsString, $replyAsString);
            }
            // If comparator things that values are equal, then we trust it
            // This shouldn't happen in practice.
            return true;
        }

        return $result;
    }

    /**
     * Explicitly cast the scores of a Zset associative array as float/double
     *
     * @param array $arr The ZSet associative array
     */
    private function scoresToFloat(array $arr): array
    {
        foreach ($arr as $member => $score) {
            $arr[$member] = (float) $score;
        }

        return $arr;
    }
}
<?php

declare(strict_types=1);

namespace Codeception\Module;

use Codeception\Exception\ModuleException;
use Codeception\Exception\ModuleRequireException;
use Codeception\Lib\Framework;
use Codeception\Lib\InnerBrowser;
use Codeception\Lib\Interfaces\DependsOnModule;
use Codeception\Module;
use Codeception\TestInterface;
use Codeception\Util\Soap as SoapUtils;
use Codeception\Util\XmlBuilder;
use Codeception\Util\XmlStructure;
use DOMDocument;
use DOMNode;
use ErrorException;
use PHPUnit\Framework\Assert;
use Symfony\Component\BrowserKit\AbstractBrowser;

/**
 * Module for testing SOAP WSDL web services.
 * Send requests and check if response matches the pattern.
 *
 * This module can be used either with frameworks or PHPBrowser.
 * It tries to guess the framework is is attached to.
 * If a endpoint is a full url then it uses PHPBrowser.
 *
 * ### Using Inside Framework
 *
 * Please note, that PHP SoapServer::handle method sends additional headers.
 * This may trigger warning: "Cannot modify header information"
 * If you use PHP SoapServer with framework, try to block call to this method in testing environment.
 *
 * ## Status
 *
 * * Maintainer: **davert**
 * * Stability: **stable**
 * * Contact: codecept@davert.mail.ua
 *
 * ## Configuration
 *
 * * endpoint *required* - soap wsdl endpoint
 * * SOAPAction - replace SOAPAction HTTP header (Set to '' to SOAP 1.2)
 *
 * ## Public Properties
 *
 * * xmlRequest - last SOAP request (DOMDocument)
 * * xmlResponse - last SOAP response (DOMDocument)
 *
 */
class SOAP extends Module implements DependsOnModule
{
    /**
     * @var array<string, mixed>
     */
    protected array $config = [
        'schema' => "",
        'schema_url' => 'http://schemas.xmlsoap.org/soap/envelope/',
        'framework_collect_buffer' => true
    ];

    /**
     * @var string[]
     */
    protected array $requiredFields = ['endpoint'];

    protected string $dependencyMessage = <<<EOF
Example using PhpBrowser as backend for SOAP module.
--
modules:
    enabled:
        - SOAP:
            depends: PhpBrowser
--
Framework modules can be used as well for functional testing of SOAP API.
EOF;

    public ?AbstractBrowser $client;

    public bool $isFunctional = false;

    /**
     * @var DOMNode|DOMDocument|null
     */
    public $xmlRequest = null;

    /**
     * @var DOMNode|DOMDocument|null
     */
    public $xmlResponse = null;

    protected ?XmlStructure $xmlStructure = null;

    protected ?InnerBrowser $connectionModule = null;

    public function _before(TestInterface $test): void
    {
        $this->client = &$this->connectionModule->client;
        $this->buildRequest();
        $this->xmlResponse = null;
        $this->xmlStructure = null;
    }

    protected function onReconfigure(): void
    {
        $this->buildRequest();
        $this->xmlResponse = null;
        $this->xmlStructure = null;
    }

    public function _depends(): array
    {
        return [InnerBrowser::class => $this->dependencyMessage];
    }

    public function _inject(InnerBrowser $connectionModule): void
    {
        $this->connectionModule = $connectionModule;
        if ($connectionModule instanceof Framework) {
            $this->isFunctional = true;
        }
    }

    private function getClient(): AbstractBrowser
    {
        if (!$this->client) {
            throw new ModuleRequireException($this, 'Connection client is not available.');
        }

        return $this->client;
    }

    private function getXmlResponse(): DOMDocument
    {
        if (!$this->xmlResponse) {
            throw new ModuleException($this, "No XML response, use `\$I->sendSoapRequest` to receive it");
        }

        return $this->xmlResponse;
    }

    private function getXmlStructure(): XmlStructure
    {
        if (!$this->xmlStructure) {
            $this->xmlStructure = new XmlStructure($this->getXmlResponse());
        }

        return $this->xmlStructure;
    }

    /**
     * Prepare SOAP header.
     * Receives header name and parameters as array.
     *
     * Example:
     *
     * ``` php
     * <?php
     * $I->haveSoapHeader('AuthHeader', array('username' => 'davert', 'password' => '123345'));
     * ```
     *
     * Will produce header:
     *
     * ```
     *    <soapenv:Header>
     *      <SessionHeader>
     *      <AuthHeader>
     *          <username>davert</username>
     *          <password>12345</password>
     *      </AuthHeader>
     *   </soapenv:Header>
     * ```
     */
    public function haveSoapHeader(string $header, array $params = []): void
    {
        $soap_schema_url = $this->config['schema_url'];
        $xml = $this->xmlRequest;
        $domElement = $xml->documentElement->getElementsByTagNameNS($soap_schema_url, 'Header')->item(0);
        $headerEl = $xml->createElement($header);
        SoapUtils::arrayToXml($xml, $headerEl, $params);
        $domElement->appendChild($headerEl);
    }

    /**
     * Submits request to endpoint.
     *
     * Requires of api function name and parameters.
     * Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes).
     *
     * You are allowed to execute as much requests as you need inside test.
     *
     * Example:
     *
     * ``` php
     * $I->sendSoapRequest('UpdateUser', '<user><id>1</id><name>notdavert</name></user>');
     * $I->sendSoapRequest('UpdateUser', \Codeception\Utils\Soap::request()->user
     *   ->id->val(1)->parent()
     *   ->name->val('notdavert');
     * ```
     *
     * @param string $action
     * @param object|string $body
     */
    public function sendSoapRequest(string $action, $body = ''): void
    {
        $soap_schema_url = $this->config['schema_url'];
        $xml = $this->xmlRequest;
        $call = $xml->createElement('ns:' . $action);
        if ($body) {
            $bodyXml = SoapUtils::toXml($body);
            if ($bodyXml->hasChildNodes()) {
                foreach ($bodyXml->childNodes as $bodyChildNode) {
                    $bodyNode = $xml->importNode($bodyChildNode, true);
                    $call->appendChild($bodyNode);
                }
            }
        }

        $xmlBody = $xml->getElementsByTagNameNS($soap_schema_url, 'Body')->item(0);

        // cleanup if body already set
        foreach ($xmlBody->childNodes as $node) {
            $xmlBody->removeChild($node);
        }

        $xmlBody->appendChild($call);
        $this->debugSection('Request', $req = $xml->C14N());

        if ($this->isFunctional && $this->config['framework_collect_buffer']) {
            $response = $this->processInternalRequest($action, $req);
        } else {
            $response = $this->processExternalRequest($action, $req);
        }

        $this->debugSection('Response', (string) $response);
        $this->xmlResponse = SoapUtils::toXml($response);
        $this->xmlStructure = null;
    }

    /**
     * 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).
     *
     * Example:
     *
     * ``` php
     * <?php
     * $I->seeSoapResponseEquals("<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope><SOAP-ENV:Body><result>1</result></SOAP-ENV:Envelope>");
     *
     * $dom = new \DOMDocument();
     * $dom->load($file);
     * $I->seeSoapRequestIncludes($dom);
     * ```
     */
    public function seeSoapResponseEquals(string $xml): void
    {
        $xml = SoapUtils::toXml($xml);
        $this->assertEquals($xml->C14N(), $this->getXmlResponse()->C14N());
    }

    /**
     * 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
     * <?php
     * $I->seeSoapResponseIncludes("<result>1</result>");
     * $I->seeSoapRequestIncludes(\Codeception\Utils\Soap::response()->result->val(1));
     *
     * $dom = new \DOMDocument();
     * $dom->load('template.xml');
     * $I->seeSoapRequestIncludes($dom);
     * ```
     *
     * @param XmlBuilder|DOMDocument|string $xml
     */
    public function seeSoapResponseIncludes($xml): void
    {
        $xml = $this->canonicalize($xml);
        $this->assertStringContainsString($xml, $this->getXmlResponse()->C14N(), 'found in XML Response');
    }


    /**
     * Checks XML response equals 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).
     *
     */
    public function dontSeeSoapResponseEquals(string $xml): void
    {
        $xml = SoapUtils::toXml($xml);
        Assert::assertXmlStringNotEqualsXmlString($xml->C14N(), $this->getXmlResponse()->C14N());
    }


    /**
     * 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 XmlBuilder|DOMDocument|string $xml
     */
    public function dontSeeSoapResponseIncludes($xml): void
    {
        $xml = $this->canonicalize($xml);
        $this->assertStringNotContainsString($xml, $this->getXmlResponse()->C14N(), "found in XML Response");
    }

    /**
     * Checks XML response contains provided structure.
     * Response elements will be compared with XML provided.
     * Only nodeNames are checked to see elements match.
     *
     * Example:
     *
     * ``` php
     * <?php
     *
     * $I->seeSoapResponseContainsStructure("<query><name></name></query>");
     * ```
     *
     * Use this method to check XML of valid structure is returned.
     * This method does not use schema for validation.
     * This method does not require path from root to match the structure.
     *
     */
    public function seeSoapResponseContainsStructure(string $xml): void
    {
        $xml = SoapUtils::toXml($xml);
        $this->debugSection("Structure", $xml->saveXML());
        $this->assertTrue($this->getXmlStructure()->matchXmlStructure($xml), "this structure is in response");
    }

    /**
     * Opposite to `seeSoapResponseContainsStructure`
     */
    public function dontSeeSoapResponseContainsStructure(string $xml): void
    {
        $xml = SoapUtils::toXml($xml);
        $this->debugSection("Structure", $xml->saveXML());
        $this->assertFalse($this->getXmlStructure()->matchXmlStructure($xml), "this structure is in response");
    }

    /**
     * Checks XML response with XPath locator
     *
     * ``` php
     * <?php
     * $I->seeSoapResponseContainsXPath('//root/user[@id=1]');
     * ```
     *
     * @param string $xPath
     */
    public function seeSoapResponseContainsXPath(string $xPath): void
    {
        $this->assertTrue($this->getXmlStructure()->matchesXpath($xPath));
    }

    /**
     * Checks XML response doesn't contain XPath locator
     *
     * ``` php
     * <?php
     * $I->dontSeeSoapResponseContainsXPath('//root/user[@id=1]');
     * ```
     *
     * @param string $xPath
     */
    public function dontSeeSoapResponseContainsXPath(string $xPath): void
    {
        $this->assertFalse($this->getXmlStructure()->matchesXpath($xPath));
    }


    /**
     * Checks response code from server.
     *
     * @param string $code
     */
    public function seeSoapResponseCodeIs(string $code): void
    {
        $this->assertEquals(
            $code,
            $this->client->getInternalResponse()->getStatus(),
            "soap response code matches expected"
        );
    }

    /**
     * Finds and returns text contents of element.
     * Element is matched by either CSS or XPath
     *
     * @version 1.1
     */
    public function grabTextContentFrom(string $cssOrXPath): string
    {
        $el = $this->getXmlStructure()->matchElement($cssOrXPath);
        return $el->textContent;
    }

    /**
     * Finds and returns attribute of element.
     * Element is matched by either CSS or XPath
     *
     * @version 1.1
     */
    public function grabAttributeFrom(string $cssOrXPath, string $attribute): string
    {
        $el = $this->getXmlStructure()->matchElement($cssOrXPath);
        $elHasAttribute = $el->hasAttribute($attribute);
        if (!$elHasAttribute) {
            $this->fail(sprintf("Attribute not found in element matched by '%s'", $cssOrXPath));
        }

        return $el->getAttribute($attribute);
    }

    protected function getSchema()
    {
        return $this->config['schema'];
    }

    /**
     * @param XmlBuilder|DOMDocument|string $xml
     * @return string
     */
    protected function canonicalize($xml): string
    {
        return SoapUtils::toXml($xml)->C14N();
    }

    protected function buildRequest(): DOMDocument
    {
        $soap_schema_url = $this->config['schema_url'];
        $xml = new DOMDocument();
        $root = $xml->createElement('soapenv:Envelope');
        $xml->appendChild($root);
        $root->setAttribute('xmlns:ns', $this->getSchema());
        $root->setAttribute('xmlns:soapenv', $soap_schema_url);

        $body = $xml->createElementNS($soap_schema_url, 'soapenv:Body');
        $header = $xml->createElementNS($soap_schema_url, 'soapenv:Header');
        $root->appendChild($header);

        $root->appendChild($body);

        $this->xmlRequest = $xml;
        return $xml;
    }

    protected function processRequest(string $action, string $body): void
    {
        $this->getClient()->request(
            'POST',
            $this->config['endpoint'],
            [],
            [],
            [
                'HTTP_Content-Type' => 'text/xml; charset=UTF-8',
                'HTTP_Content-Length' => strlen($body),
                'HTTP_SOAPAction' => $this->config['SOAPAction'] ?? $action
            ],
            $body
        );
    }

    /**
     * @return string|false
     */
    protected function processInternalRequest(string $action, string $body)
    {
        ob_start();
        try {
            $this->getClient()->setServerParameter('HTTP_HOST', 'localhost');
            $this->processRequest($action, $body);
        } catch (ErrorException $exception) {
            // Zend_Soap outputs warning as an exception
            if (strpos($exception->getMessage(), 'Warning: Cannot modify header information') === false) {
                ob_end_clean();
                throw $exception;
            }
        }

        $response = ob_get_contents();
        ob_end_clean();
        return $response;
    }

    protected function processExternalRequest(string $action, string $body): string
    {
        $this->processRequest($action, $body);
        return $this->client->getInternalResponse()->getContent();
    }
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Invoker;

use const SIGALRM;
use function call_user_func_array;
use function function_exists;
use function pcntl_alarm;
use function pcntl_async_signals;
use function pcntl_signal;
use function sprintf;
use Throwable;

final class Invoker
{



private $timeout;




public function invoke(callable $callable, array $arguments, int $timeout)
{
if (!$this->canInvokeWithTimeout()) {
throw new ProcessControlExtensionNotLoadedException(
'The pcntl (process control) extension for PHP is required'
);
}

pcntl_signal(
SIGALRM,
function (): void {
throw new TimeoutException(
sprintf(
'Execution aborted after %d second%s',
$this->timeout,
$this->timeout === 1 ? '' : 's'
)
);
},
true
);

$this->timeout = $timeout;

pcntl_async_signals(true);
pcntl_alarm($timeout);

try {
return call_user_func_array($callable, $arguments);
} finally {
pcntl_alarm(0);
}
}

public function canInvokeWithTimeout(): bool
{
return function_exists('pcntl_signal') && function_exists('pcntl_async_signals') && function_exists('pcntl_alarm');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Invoker;

use RuntimeException;

final class ProcessControlExtensionNotLoadedException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Invoker;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Invoker;

use RuntimeException;

final class TimeoutException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\FileIterator;

use const GLOB_ONLYDIR;
use function array_filter;
use function array_map;
use function array_merge;
use function glob;
use function is_dir;
use function is_string;
use function realpath;
use AppendIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class Factory
{





public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []): AppendIterator
{
if (is_string($paths)) {
$paths = [$paths];
}

$paths = $this->getPathsAfterResolvingWildcards($paths);
$exclude = $this->getPathsAfterResolvingWildcards($exclude);

if (is_string($prefixes)) {
if ($prefixes !== '') {
$prefixes = [$prefixes];
} else {
$prefixes = [];
}
}

if (is_string($suffixes)) {
if ($suffixes !== '') {
$suffixes = [$suffixes];
} else {
$suffixes = [];
}
}

$iterator = new AppendIterator;

foreach ($paths as $path) {
if (is_dir($path)) {
$iterator->append(
new Iterator(
$path,
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS | RecursiveDirectoryIterator::SKIP_DOTS)
),
$suffixes,
$prefixes,
$exclude
)
);
}
}

return $iterator;
}

protected function getPathsAfterResolvingWildcards(array $paths): array
{
$_paths = [[]];

foreach ($paths as $path) {
if ($locals = glob($path, GLOB_ONLYDIR)) {
$_paths[] = array_map('\realpath', $locals);
} else {
$_paths[] = [realpath($path)];
}
}

return array_filter(array_merge(...$_paths));
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\FileIterator;

use function array_filter;
use function array_map;
use function preg_match;
use function realpath;
use function str_replace;
use function strlen;
use function strpos;
use function substr;
use FilterIterator;

class Iterator extends FilterIterator
{
public const PREFIX = 0;

public const SUFFIX = 1;




private $basePath;




private $suffixes = [];




private $prefixes = [];




private $exclude = [];

public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = [])
{
$this->basePath = realpath($basePath);
$this->prefixes = $prefixes;
$this->suffixes = $suffixes;
$this->exclude = array_filter(array_map('realpath', $exclude));

parent::__construct($iterator);
}

public function accept(): bool
{
$current = $this->getInnerIterator()->current();
$filename = $current->getFilename();
$realPath = $current->getRealPath();

if ($realPath === false) {
return false;
}

return $this->acceptPath($realPath) &&
$this->acceptPrefix($filename) &&
$this->acceptSuffix($filename);
}

private function acceptPath(string $path): bool
{

if (preg_match('=/\.[^/]*/=', str_replace($this->basePath, '', $path))) {
return false;
}

foreach ($this->exclude as $exclude) {
if (strpos($path, $exclude) === 0) {
return false;
}
}

return true;
}

private function acceptPrefix(string $filename): bool
{
return $this->acceptSubString($filename, $this->prefixes, self::PREFIX);
}

private function acceptSuffix(string $filename): bool
{
return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX);
}

private function acceptSubString(string $filename, array $subStrings, int $type): bool
{
if (empty($subStrings)) {
return true;
}

$matched = false;

foreach ($subStrings as $string) {
if (($type === self::PREFIX && strpos($filename, $string) === 0) ||
($type === self::SUFFIX &&
substr($filename, -1 * strlen($string)) === $string)) {
$matched = true;

break;
}
}

return $matched;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\FileIterator;

use const DIRECTORY_SEPARATOR;
use function array_unique;
use function count;
use function dirname;
use function explode;
use function is_file;
use function is_string;
use function realpath;
use function sort;

class Facade
{





public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = false): array
{
if (is_string($paths)) {
$paths = [$paths];
}

$iterator = (new Factory)->getFileIterator($paths, $suffixes, $prefixes, $exclude);

$files = [];

foreach ($iterator as $file) {
$file = $file->getRealPath();

if ($file) {
$files[] = $file;
}
}

foreach ($paths as $path) {
if (is_file($path)) {
$files[] = realpath($path);
}
}

$files = array_unique($files);
sort($files);

if ($commonPath) {
return [
'commonPath' => $this->getCommonPath($files),
'files' => $files,
];
}

return $files;
}

protected function getCommonPath(array $files): string
{
$count = count($files);

if ($count === 0) {
return '';
}

if ($count === 1) {
return dirname($files[0]) . DIRECTORY_SEPARATOR;
}

$_files = [];

foreach ($files as $file) {
$_files[] = $_fileParts = explode(DIRECTORY_SEPARATOR, $file);

if (empty($_fileParts[0])) {
$_fileParts[0] = DIRECTORY_SEPARATOR;
}
}

$common = '';
$done = false;
$j = 0;
$count--;

while (!$done) {
for ($i = 0; $i < $count; $i++) {
if ($_files[$i][$j] != $_files[$i + 1][$j]) {
$done = true;

break;
}
}

if (!$done) {
$common .= $_files[0][$j];

if ($j > 0) {
$common .= DIRECTORY_SEPARATOR;
}
}

$j++;
}

return DIRECTORY_SEPARATOR . $common;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Timer;

use function array_pop;
use function hrtime;

final class Timer
{



private $startTimes = [];

public function start(): void
{
$this->startTimes[] = (float) hrtime(true);
}




public function stop(): Duration
{
if (empty($this->startTimes)) {
throw new NoActiveTimerException(
'Timer::start() has to be called before Timer::stop()'
);
}

return Duration::fromNanoseconds((float) hrtime(true) - array_pop($this->startTimes));
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Timer;

use function is_float;
use function memory_get_peak_usage;
use function microtime;
use function sprintf;

final class ResourceUsageFormatter
{



private const SIZES = [
'GB' => 1073741824,
'MB' => 1048576,
'KB' => 1024,
];

public function resourceUsage(Duration $duration): string
{
return sprintf(
'Time: %s, Memory: %s',
$duration->asString(),
$this->bytesToString(memory_get_peak_usage(true))
);
}




public function resourceUsageSinceStartOfRequest(): string
{
if (!isset($_SERVER['REQUEST_TIME_FLOAT'])) {
throw new TimeSinceStartOfRequestNotAvailableException(
'Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not available'
);
}

if (!is_float($_SERVER['REQUEST_TIME_FLOAT'])) {
throw new TimeSinceStartOfRequestNotAvailableException(
'Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not of type float'
);
}

return $this->resourceUsage(
Duration::fromMicroseconds(
(1000000 * (microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']))
)
);
}

private function bytesToString(int $bytes): string
{
foreach (self::SIZES as $unit => $value) {
if ($bytes >= $value) {
return sprintf('%.2f %s', $bytes >= 1024 ? $bytes / $value : $bytes, $unit);
}
}


return $bytes . ' byte' . ($bytes !== 1 ? 's' : '');

}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Timer;

use RuntimeException;

final class TimeSinceStartOfRequestNotAvailableException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Timer;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Timer;

use LogicException;

final class NoActiveTimerException extends LogicException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Timer;

use function floor;
use function sprintf;




final class Duration
{



private $nanoseconds;




private $hours;




private $minutes;




private $seconds;




private $milliseconds;

public static function fromMicroseconds(float $microseconds): self
{
return new self($microseconds * 1000);
}

public static function fromNanoseconds(float $nanoseconds): self
{
return new self($nanoseconds);
}

private function __construct(float $nanoseconds)
{
$this->nanoseconds = $nanoseconds;
$timeInMilliseconds = $nanoseconds / 1000000;
$hours = floor($timeInMilliseconds / 60 / 60 / 1000);
$hoursInMilliseconds = $hours * 60 * 60 * 1000;
$minutes = floor($timeInMilliseconds / 60 / 1000) % 60;
$minutesInMilliseconds = $minutes * 60 * 1000;
$seconds = floor(($timeInMilliseconds - $hoursInMilliseconds - $minutesInMilliseconds) / 1000);
$secondsInMilliseconds = $seconds * 1000;
$milliseconds = $timeInMilliseconds - $hoursInMilliseconds - $minutesInMilliseconds - $secondsInMilliseconds;
$this->hours = (int) $hours;
$this->minutes = $minutes;
$this->seconds = (int) $seconds;
$this->milliseconds = (int) $milliseconds;
}

public function asNanoseconds(): float
{
return $this->nanoseconds;
}

public function asMicroseconds(): float
{
return $this->nanoseconds / 1000;
}

public function asMilliseconds(): float
{
return $this->nanoseconds / 1000000;
}

public function asSeconds(): float
{
return $this->nanoseconds / 1000000000;
}

public function asString(): string
{
$result = '';

if ($this->hours > 0) {
$result = sprintf('%02d', $this->hours) . ':';
}

$result .= sprintf('%02d', $this->minutes) . ':';
$result .= sprintf('%02d', $this->seconds);

if ($this->milliseconds > 0) {
$result .= '.' . sprintf('%03d', $this->milliseconds);
}

return $result;
}
}
<?php declare(strict_types=1);








namespace PHPUnit;

use Throwable;




interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use const DEBUG_BACKTRACE_IGNORE_ARGS;
use const DIRECTORY_SEPARATOR;
use function array_merge;
use function basename;
use function debug_backtrace;
use function defined;
use function dirname;
use function explode;
use function extension_loaded;
use function file;
use function file_get_contents;
use function file_put_contents;
use function is_array;
use function is_file;
use function is_readable;
use function is_string;
use function ltrim;
use function phpversion;
use function preg_match;
use function preg_replace;
use function preg_split;
use function realpath;
use function rtrim;
use function sprintf;
use function str_replace;
use function strncasecmp;
use function strpos;
use function substr;
use function trim;
use function unlink;
use function unserialize;
use function var_export;
use function version_compare;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExecutionOrderDependency;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\IncompleteTestError;
use PHPUnit\Framework\PHPTAssertionFailedError;
use PHPUnit\Framework\Reorderable;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\SkippedTestError;
use PHPUnit\Framework\SyntheticSkippedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestResult;
use PHPUnit\Util\PHP\AbstractPhpProcess;
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;
use SebastianBergmann\Template\Template;
use SebastianBergmann\Timer\Timer;
use Throwable;




final class PhptTestCase implements Reorderable, SelfDescribing, Test
{



private $filename;




private $phpUtil;




private $output = '';






public function __construct(string $filename, AbstractPhpProcess $phpUtil = null)
{
if (!is_file($filename)) {
throw new Exception(
sprintf(
'File "%s" does not exist.',
$filename,
),
);
}

$this->filename = $filename;
$this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory();
}




public function count(): int
{
return 1;
}









public function run(TestResult $result = null): TestResult
{
if ($result === null) {
$result = new TestResult;
}

try {
$sections = $this->parse();
} catch (Exception $e) {
$result->startTest($this);
$result->addFailure($this, new SkippedTestError($e->getMessage()), 0);
$result->endTest($this, 0);

return $result;
}

$code = $this->render($sections['FILE']);
$xfail = false;
$settings = $this->parseIniSection($this->settings($result->getCollectCodeCoverageInformation()));

$result->startTest($this);

if (isset($sections['INI'])) {
$settings = $this->parseIniSection($sections['INI'], $settings);
}

if (isset($sections['ENV'])) {
$env = $this->parseEnvSection($sections['ENV']);
$this->phpUtil->setEnv($env);
}

$this->phpUtil->setUseStderrRedirection(true);

if ($result->enforcesTimeLimit()) {
$this->phpUtil->setTimeout($result->getTimeoutForLargeTests());
}

$skip = $this->runSkip($sections, $result, $settings);

if ($skip) {
return $result;
}

if (isset($sections['XFAIL'])) {
$xfail = trim($sections['XFAIL']);
}

if (isset($sections['STDIN'])) {
$this->phpUtil->setStdin($sections['STDIN']);
}

if (isset($sections['ARGS'])) {
$this->phpUtil->setArgs($sections['ARGS']);
}

if ($result->getCollectCodeCoverageInformation()) {
$codeCoverageCacheDirectory = null;
$pathCoverage = false;

$codeCoverage = $result->getCodeCoverage();

if ($codeCoverage) {
if ($codeCoverage->cachesStaticAnalysis()) {
$codeCoverageCacheDirectory = $codeCoverage->cacheDirectory();
}

$pathCoverage = $codeCoverage->collectsBranchAndPathCoverage();
}

$this->renderForCoverage($code, $pathCoverage, $codeCoverageCacheDirectory);
}

$timer = new Timer;
$timer->start();

$jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings));
$time = $timer->stop()->asSeconds();
$this->output = $jobResult['stdout'] ?? '';

if (isset($codeCoverage) && ($coverage = $this->cleanupForCoverage())) {
$codeCoverage->append($coverage, $this, true, [], []);
}

try {
$this->assertPhptExpectation($sections, $this->output);
} catch (AssertionFailedError $e) {
$failure = $e;

if ($xfail !== false) {
$failure = new IncompleteTestError($xfail, 0, $e);
} elseif ($e instanceof ExpectationFailedException) {
$comparisonFailure = $e->getComparisonFailure();

if ($comparisonFailure) {
$diff = $comparisonFailure->getDiff();
} else {
$diff = $e->getMessage();
}

$hint = $this->getLocationHintFromDiff($diff, $sections);
$trace = array_merge($hint, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
$failure = new PHPTAssertionFailedError(
$e->getMessage(),
0,
$trace[0]['file'],
$trace[0]['line'],
$trace,
$comparisonFailure ? $diff : '',
);
}

$result->addFailure($this, $failure, $time);
} catch (Throwable $t) {
$result->addError($this, $t, $time);
}

if ($xfail !== false && $result->allCompletelyImplemented()) {
$result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time);
}

$this->runClean($sections, $result->getCollectCodeCoverageInformation());

$result->endTest($this, $time);

return $result;
}




public function getName(): string
{
return $this->toString();
}




public function toString(): string
{
return $this->filename;
}

public function usesDataProvider(): bool
{
return false;
}

public function getNumAssertions(): int
{
return 1;
}

public function getActualOutput(): string
{
return $this->output;
}

public function hasOutput(): bool
{
return !empty($this->output);
}

public function sortId(): string
{
return $this->filename;
}




public function provides(): array
{
return [];
}




public function requires(): array
{
return [];
}






private function parseIniSection($content, array $ini = []): array
{
if (is_string($content)) {
$content = explode("\n", trim($content));
}

foreach ($content as $setting) {
if (strpos($setting, '=') === false) {
continue;
}

$setting = explode('=', $setting, 2);
$name = trim($setting[0]);
$value = trim($setting[1]);

if ($name === 'extension' || $name === 'zend_extension') {
if (!isset($ini[$name])) {
$ini[$name] = [];
}

$ini[$name][] = $value;

continue;
}

$ini[$name] = $value;
}

return $ini;
}

private function parseEnvSection(string $content): array
{
$env = [];

foreach (explode("\n", trim($content)) as $e) {
$e = explode('=', trim($e), 2);

if (!empty($e[0]) && isset($e[1])) {
$env[$e[0]] = $e[1];
}
}

return $env;
}






private function assertPhptExpectation(array $sections, string $output): void
{
$assertions = [
'EXPECT' => 'assertEquals',
'EXPECTF' => 'assertStringMatchesFormat',
'EXPECTREGEX' => 'assertMatchesRegularExpression',
];

$actual = preg_replace('/\r\n/', "\n", trim($output));

foreach ($assertions as $sectionName => $sectionAssertion) {
if (isset($sections[$sectionName])) {
$sectionContent = preg_replace('/\r\n/', "\n", trim($sections[$sectionName]));
$expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent;

if ($expected === '') {
throw new Exception('No PHPT expectation found');
}

Assert::$sectionAssertion($expected, $actual);

return;
}
}

throw new Exception('No PHPT assertion found');
}




private function runSkip(array &$sections, TestResult $result, array $settings): bool
{
if (!isset($sections['SKIPIF'])) {
return false;
}

$skipif = $this->render($sections['SKIPIF']);
$jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings));

if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) {
$message = '';

if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $skipMatch)) {
$message = substr($skipMatch[1], 2);
}

$hint = $this->getLocationHint($message, $sections, 'SKIPIF');
$trace = array_merge($hint, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
$result->addFailure(
$this,
new SyntheticSkippedError($message, 0, $trace[0]['file'], $trace[0]['line'], $trace),
0,
);
$result->endTest($this, 0);

return true;
}

return false;
}

private function runClean(array &$sections, bool $collectCoverage): void
{
$this->phpUtil->setStdin('');
$this->phpUtil->setArgs('');

if (isset($sections['CLEAN'])) {
$cleanCode = $this->render($sections['CLEAN']);

$this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage));
}
}




private function parse(): array
{
$sections = [];
$section = '';

$unsupportedSections = [
'CGI',
'COOKIE',
'DEFLATE_POST',
'EXPECTHEADERS',
'EXTENSIONS',
'GET',
'GZIP_POST',
'HEADERS',
'PHPDBG',
'POST',
'POST_RAW',
'PUT',
'REDIRECTTEST',
'REQUEST',
];

$lineNr = 0;

foreach (file($this->filename) as $line) {
$lineNr++;

if (preg_match('/^--([_A-Z]+)--/', $line, $result)) {
$section = $result[1];
$sections[$section] = '';
$sections[$section . '_offset'] = $lineNr;

continue;
}

if (empty($section)) {
throw new Exception('Invalid PHPT file: empty section header');
}

$sections[$section] .= $line;
}

if (isset($sections['FILEEOF'])) {
$sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n");
unset($sections['FILEEOF']);
}

$this->parseExternal($sections);

if (!$this->validate($sections)) {
throw new Exception('Invalid PHPT file');
}

foreach ($unsupportedSections as $section) {
if (isset($sections[$section])) {
throw new Exception(
"PHPUnit does not support PHPT {$section} sections",
);
}
}

return $sections;
}




private function parseExternal(array &$sections): void
{
$allowSections = [
'FILE',
'EXPECT',
'EXPECTF',
'EXPECTREGEX',
];
$testDirectory = dirname($this->filename) . DIRECTORY_SEPARATOR;

foreach ($allowSections as $section) {
if (isset($sections[$section . '_EXTERNAL'])) {
$externalFilename = trim($sections[$section . '_EXTERNAL']);

if (!is_file($testDirectory . $externalFilename) ||
!is_readable($testDirectory . $externalFilename)) {
throw new Exception(
sprintf(
'Could not load --%s-- %s for PHPT file',
$section . '_EXTERNAL',
$testDirectory . $externalFilename,
),
);
}

$sections[$section] = file_get_contents($testDirectory . $externalFilename);
}
}
}

private function validate(array &$sections): bool
{
$requiredSections = [
'FILE',
[
'EXPECT',
'EXPECTF',
'EXPECTREGEX',
],
];

foreach ($requiredSections as $section) {
if (is_array($section)) {
$foundSection = false;

foreach ($section as $anySection) {
if (isset($sections[$anySection])) {
$foundSection = true;

break;
}
}

if (!$foundSection) {
return false;
}

continue;
}

if (!isset($sections[$section])) {
return false;
}
}

return true;
}

private function render(string $code): string
{
return str_replace(
[
'__DIR__',
'__FILE__',
],
[
"'" . dirname($this->filename) . "'",
"'" . $this->filename . "'",
],
$code,
);
}

private function getCoverageFiles(): array
{
$baseDir = dirname(realpath($this->filename)) . DIRECTORY_SEPARATOR;
$basename = basename($this->filename, 'phpt');

return [
'coverage' => $baseDir . $basename . 'coverage',
'job' => $baseDir . $basename . 'php',
];
}

private function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory): void
{
$files = $this->getCoverageFiles();

$template = new Template(
__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl',
);

$composerAutoload = '\'\'';

if (defined('PHPUNIT_COMPOSER_INSTALL')) {
$composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true);
}

$phar = '\'\'';

if (defined('__PHPUNIT_PHAR__')) {
$phar = var_export(__PHPUNIT_PHAR__, true);
}

$globals = '';

if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
$globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export(
$GLOBALS['__PHPUNIT_BOOTSTRAP'],
true,
) . ";\n";
}

if ($codeCoverageCacheDirectory === null) {
$codeCoverageCacheDirectory = 'null';
} else {
$codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'";
}

$template->setVar(
[
'composerAutoload' => $composerAutoload,
'phar' => $phar,
'globals' => $globals,
'job' => $files['job'],
'coverageFile' => $files['coverage'],
'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage',
'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory,
],
);

file_put_contents($files['job'], $job);

$job = $template->render();
}

private function cleanupForCoverage(): RawCodeCoverageData
{
$coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]);
$files = $this->getCoverageFiles();

if (is_file($files['coverage'])) {
$buffer = @file_get_contents($files['coverage']);

if ($buffer !== false) {
$coverage = @unserialize($buffer);

if ($coverage === false) {
$coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]);
}
}
}

foreach ($files as $file) {
@unlink($file);
}

return $coverage;
}

private function stringifyIni(array $ini): array
{
$settings = [];

foreach ($ini as $key => $value) {
if (is_array($value)) {
foreach ($value as $val) {
$settings[] = $key . '=' . $val;
}

continue;
}

$settings[] = $key . '=' . $value;
}

return $settings;
}

private function getLocationHintFromDiff(string $message, array $sections): array
{
$needle = '';
$previousLine = '';
$block = 'message';

foreach (preg_split('/\r\n|\r|\n/', $message) as $line) {
$line = trim($line);

if ($block === 'message' && $line === '--- Expected') {
$block = 'expected';
}

if ($block === 'expected' && $line === '@@ @@') {
$block = 'diff';
}

if ($block === 'diff') {
if (strpos($line, '+') === 0) {
$needle = $this->getCleanDiffLine($previousLine);

break;
}

if (strpos($line, '-') === 0) {
$needle = $this->getCleanDiffLine($line);

break;
}
}

if (!empty($line)) {
$previousLine = $line;
}
}

return $this->getLocationHint($needle, $sections);
}

private function getCleanDiffLine(string $line): string
{
if (preg_match('/^[\-+]([\'\"]?)(.*)\1$/', $line, $matches)) {
$line = $matches[2];
}

return $line;
}

private function getLocationHint(string $needle, array $sections, ?string $sectionName = null): array
{
$needle = trim($needle);

if (empty($needle)) {
return [[
'file' => realpath($this->filename),
'line' => 1,
]];
}

if ($sectionName) {
$search = [$sectionName];
} else {
$search = [

'EXPECT',
'EXPECTF',
'EXPECTREGEX',
];
}

$sectionOffset = null;

foreach ($search as $section) {
if (!isset($sections[$section])) {
continue;
}

if (isset($sections[$section . '_EXTERNAL'])) {
$externalFile = trim($sections[$section . '_EXTERNAL']);

return [
[
'file' => realpath(dirname($this->filename) . DIRECTORY_SEPARATOR . $externalFile),
'line' => 1,
],
[
'file' => realpath($this->filename),
'line' => ($sections[$section . '_EXTERNAL_offset'] ?? 0) + 1,
],
];
}

$sectionOffset = $sections[$section . '_offset'] ?? 0;
$offset = $sectionOffset + 1;

foreach (preg_split('/\r\n|\r|\n/', $sections[$section]) as $line) {
if (strpos($line, $needle) !== false) {
return [[
'file' => realpath($this->filename),
'line' => $offset,
]];
}
$offset++;
}
}

if ($sectionName) {

return [[
'file' => realpath($this->filename),
'line' => $sectionOffset,
]];
}


return [[
'file' => realpath($this->filename),
'line' => 1,
]];
}




private function settings(bool $collectCoverage): array
{
$settings = [
'allow_url_fopen=1',
'auto_append_file=',
'auto_prepend_file=',
'disable_functions=',
'display_errors=1',
'docref_ext=.html',
'docref_root=',
'error_append_string=',
'error_prepend_string=',
'error_reporting=-1',
'html_errors=0',
'log_errors=0',
'open_basedir=',
'output_buffering=Off',
'output_handler=',
'report_memleaks=0',
'report_zend_debug=0',
];

if (extension_loaded('pcov')) {
if ($collectCoverage) {
$settings[] = 'pcov.enabled=1';
} else {
$settings[] = 'pcov.enabled=0';
}
}

if (extension_loaded('xdebug')) {
if (version_compare(phpversion('xdebug'), '3', '>=')) {
if ($collectCoverage) {
$settings[] = 'xdebug.mode=coverage';
} else {
$settings[] = 'xdebug.mode=off';
}
} else {
$settings[] = 'xdebug.default_enable=0';

if ($collectCoverage) {
$settings[] = 'xdebug.coverage_enable=1';
}
}
}

return $settings;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use function preg_match;
use function round;




final class ResultCacheExtension implements AfterIncompleteTestHook, AfterLastTestHook, AfterRiskyTestHook, AfterSkippedTestHook, AfterSuccessfulTestHook, AfterTestErrorHook, AfterTestFailureHook, AfterTestWarningHook
{



private $cache;

public function __construct(TestResultCache $cache)
{
$this->cache = $cache;
}

public function flush(): void
{
$this->cache->persist();
}

public function executeAfterSuccessfulTest(string $test, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
}

public function executeAfterIncompleteTest(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_INCOMPLETE);
}

public function executeAfterRiskyTest(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_RISKY);
}

public function executeAfterSkippedTest(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_SKIPPED);
}

public function executeAfterTestError(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_ERROR);
}

public function executeAfterTestFailure(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_FAILURE);
}

public function executeAfterTestWarning(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);

$this->cache->setTime($testName, round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_WARNING);
}

public function executeAfterLastTest(): void
{
$this->flush();
}






private function getTestName(string $test): string
{
$matches = [];

if (preg_match('/^(?<name>\S+::\S+)(?:(?<dataname> with data set (?:#\d+|"[^"]+"))\s\()?/', $test, $matches)) {
$test = $matches['name'] . ($matches['dataname'] ?? '');
}

return $test;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use ReflectionClass;






interface TestSuiteLoader
{
public function load(string $suiteClassFile): ReflectionClass;

public function reload(ReflectionClass $aClass): ReflectionClass;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;




interface TestResultCache
{
public function setState(string $testName, int $state): void;

public function getState(string $testName): int;

public function setTime(string $testName, float $time): void;

public function getTime(string $testName): float;

public function load(): void;

public function persist(): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use function array_diff;
use function array_values;
use function basename;
use function class_exists;
use function get_declared_classes;
use function sprintf;
use function stripos;
use function strlen;
use function substr;
use PHPUnit\Framework\TestCase;
use PHPUnit\Util\FileLoader;
use ReflectionClass;
use ReflectionException;






final class StandardTestSuiteLoader implements TestSuiteLoader
{



public function load(string $suiteClassFile): ReflectionClass
{
$suiteClassName = basename($suiteClassFile, '.php');
$loadedClasses = get_declared_classes();

if (!class_exists($suiteClassName, false)) {

FileLoader::checkAndLoad($suiteClassFile);

$loadedClasses = array_values(
array_diff(get_declared_classes(), $loadedClasses),
);

if (empty($loadedClasses)) {
throw new Exception(
sprintf(
'Class %s could not be found in %s',
$suiteClassName,
$suiteClassFile,
),
);
}
}

if (!class_exists($suiteClassName, false)) {
$offset = 0 - strlen($suiteClassName);

foreach ($loadedClasses as $loadedClass) {

if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0 ||
stripos(substr($loadedClass, $offset - 1), '_' . $suiteClassName) === 0) {
$suiteClassName = $loadedClass;

break;
}
}
}

if (!class_exists($suiteClassName, false)) {
throw new Exception(
sprintf(
'Class %s could not be found in %s',
$suiteClassName,
$suiteClassFile,
),
);
}

try {
$class = new ReflectionClass($suiteClassName);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($class->isSubclassOf(TestCase::class)) {
if ($class->isAbstract()) {
throw new Exception(
sprintf(
'Class %s declared in %s is abstract',
$suiteClassName,
$suiteClassFile,
),
);
}

return $class;
}

if ($class->hasMethod('suite')) {
try {
$method = $class->getMethod('suite');

} catch (ReflectionException $e) {
throw new Exception(
sprintf(
'Method %s::suite() declared in %s is abstract',
$suiteClassName,
$suiteClassFile,
),
);
}

if (!$method->isPublic()) {
throw new Exception(
sprintf(
'Method %s::suite() declared in %s is not public',
$suiteClassName,
$suiteClassFile,
),
);
}

if (!$method->isStatic()) {
throw new Exception(
sprintf(
'Method %s::suite() declared in %s is not static',
$suiteClassName,
$suiteClassFile,
),
);
}
}

return $class;
}

public function reload(ReflectionClass $aClass): ReflectionClass
{
return $aClass;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use function array_diff;
use function array_merge;
use function array_reverse;
use function array_splice;
use function count;
use function in_array;
use function max;
use function shuffle;
use function usort;
use PHPUnit\Framework\DataProviderTestSuite;
use PHPUnit\Framework\Reorderable;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Util\Test as TestUtil;




final class TestSuiteSorter
{



public const ORDER_DEFAULT = 0;




public const ORDER_RANDOMIZED = 1;




public const ORDER_REVERSED = 2;




public const ORDER_DEFECTS_FIRST = 3;




public const ORDER_DURATION = 4;






public const ORDER_SIZE = 5;




private const DEFECT_SORT_WEIGHT = [
BaseTestRunner::STATUS_ERROR => 6,
BaseTestRunner::STATUS_FAILURE => 5,
BaseTestRunner::STATUS_WARNING => 4,
BaseTestRunner::STATUS_INCOMPLETE => 3,
BaseTestRunner::STATUS_RISKY => 2,
BaseTestRunner::STATUS_SKIPPED => 1,
BaseTestRunner::STATUS_UNKNOWN => 0,
];

private const SIZE_SORT_WEIGHT = [
TestUtil::SMALL => 1,
TestUtil::MEDIUM => 2,
TestUtil::LARGE => 3,
TestUtil::UNKNOWN => 4,
];




private $defectSortOrder = [];




private $cache;




private $originalExecutionOrder = [];




private $executionOrder = [];

public function __construct(?TestResultCache $cache = null)
{
$this->cache = $cache ?? new NullTestResultCache;
}





public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = true): void
{
$allowedOrders = [
self::ORDER_DEFAULT,
self::ORDER_REVERSED,
self::ORDER_RANDOMIZED,
self::ORDER_DURATION,
self::ORDER_SIZE,
];

if (!in_array($order, $allowedOrders, true)) {
throw new Exception(
'$order must be one of TestSuiteSorter::ORDER_[DEFAULT|REVERSED|RANDOMIZED|DURATION|SIZE]',
);
}

$allowedOrderDefects = [
self::ORDER_DEFAULT,
self::ORDER_DEFECTS_FIRST,
];

if (!in_array($orderDefects, $allowedOrderDefects, true)) {
throw new Exception(
'$orderDefects must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_DEFECTS_FIRST',
);
}

if ($isRootTestSuite) {
$this->originalExecutionOrder = $this->calculateTestExecutionOrder($suite);
}

if ($suite instanceof TestSuite) {
foreach ($suite as $_suite) {
$this->reorderTestsInSuite($_suite, $order, $resolveDependencies, $orderDefects, false);
}

if ($orderDefects === self::ORDER_DEFECTS_FIRST) {
$this->addSuiteToDefectSortOrder($suite);
}

$this->sort($suite, $order, $resolveDependencies, $orderDefects);
}

if ($isRootTestSuite) {
$this->executionOrder = $this->calculateTestExecutionOrder($suite);
}
}

public function getOriginalExecutionOrder(): array
{
return $this->originalExecutionOrder;
}

public function getExecutionOrder(): array
{
return $this->executionOrder;
}

private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects): void
{
if (empty($suite->tests())) {
return;
}

if ($order === self::ORDER_REVERSED) {
$suite->setTests($this->reverse($suite->tests()));
} elseif ($order === self::ORDER_RANDOMIZED) {
$suite->setTests($this->randomize($suite->tests()));
} elseif ($order === self::ORDER_DURATION && $this->cache !== null) {
$suite->setTests($this->sortByDuration($suite->tests()));
} elseif ($order === self::ORDER_SIZE) {
$suite->setTests($this->sortBySize($suite->tests()));
}

if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) {
$suite->setTests($this->sortDefectsFirst($suite->tests()));
}

if ($resolveDependencies && !($suite instanceof DataProviderTestSuite)) {

$tests = $suite->tests();

$suite->setTests($this->resolveDependencies($tests));
}
}




private function addSuiteToDefectSortOrder(TestSuite $suite): void
{
$max = 0;

foreach ($suite->tests() as $test) {
if (!$test instanceof Reorderable) {
continue;
}

if (!isset($this->defectSortOrder[$test->sortId()])) {
$this->defectSortOrder[$test->sortId()] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($test->sortId())];
$max = max($max, $this->defectSortOrder[$test->sortId()]);
}
}

$this->defectSortOrder[$suite->sortId()] = $max;
}

private function reverse(array $tests): array
{
return array_reverse($tests);
}

private function randomize(array $tests): array
{
shuffle($tests);

return $tests;
}

private function sortDefectsFirst(array $tests): array
{
usort(
$tests,



function ($left, $right)
{
return $this->cmpDefectPriorityAndTime($left, $right);
},
);

return $tests;
}

private function sortByDuration(array $tests): array
{
usort(
$tests,



function ($left, $right)
{
return $this->cmpDuration($left, $right);
},
);

return $tests;
}

private function sortBySize(array $tests): array
{
usort(
$tests,



function ($left, $right)
{
return $this->cmpSize($left, $right);
},
);

return $tests;
}










private function cmpDefectPriorityAndTime(Test $a, Test $b): int
{
if (!($a instanceof Reorderable && $b instanceof Reorderable)) {
return 0;
}

$priorityA = $this->defectSortOrder[$a->sortId()] ?? 0;
$priorityB = $this->defectSortOrder[$b->sortId()] ?? 0;

if ($priorityB <=> $priorityA) {

return $priorityB <=> $priorityA;
}

if ($priorityA || $priorityB) {
return $this->cmpDuration($a, $b);
}


return 0;
}






private function cmpDuration(Test $a, Test $b): int
{
if (!($a instanceof Reorderable && $b instanceof Reorderable)) {
return 0;
}

return $this->cache->getTime($a->sortId()) <=> $this->cache->getTime($b->sortId());
}




private function cmpSize(Test $a, Test $b): int
{
$sizeA = ($a instanceof TestCase || $a instanceof DataProviderTestSuite)
? $a->getSize()
: TestUtil::UNKNOWN;
$sizeB = ($b instanceof TestCase || $b instanceof DataProviderTestSuite)
? $b->getSize()
: TestUtil::UNKNOWN;

return self::SIZE_SORT_WEIGHT[$sizeA] <=> self::SIZE_SORT_WEIGHT[$sizeB];
}
















private function resolveDependencies(array $tests): array
{
$newTestOrder = [];
$i = 0;
$provided = [];

do {
if ([] === array_diff($tests[$i]->requires(), $provided)) {
$provided = array_merge($provided, $tests[$i]->provides());
$newTestOrder = array_merge($newTestOrder, array_splice($tests, $i, 1));
$i = 0;
} else {
$i++;
}
} while (!empty($tests) && ($i < count($tests)));

return array_merge($newTestOrder, $tests);
}




private function calculateTestExecutionOrder(Test $suite): array
{
$tests = [];

if ($suite instanceof TestSuite) {
foreach ($suite->tests() as $test) {
if (!$test instanceof TestSuite && $test instanceof Reorderable) {
$tests[] = $test->sortId();
} else {
$tests = array_merge($tests, $this->calculateTestExecutionOrder($test));
}
}
}

return $tests;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use function array_slice;
use function assert;
use function dirname;
use function explode;
use function implode;
use function strpos;
use SebastianBergmann\Version as VersionId;




final class Version
{



private static $pharVersion = '';




private static $version = '';






public static function id(): string
{
if (self::$pharVersion !== '') {
return self::$pharVersion;
}

if (self::$version === '') {
self::$version = (new VersionId('9.6.17', dirname(__DIR__, 2)))->getVersion();

assert(!empty(self::$version));
}

return self::$version;
}




public static function series(): string
{
if (strpos(self::id(), '-')) {
$version = explode('-', self::id())[0];
} else {
$version = self::id();
}

return implode('.', array_slice(explode('.', $version), 0, 2));
}




public static function getVersionString(): string
{
return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use RuntimeException;




final class Exception extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Extension;

use function class_exists;
use function sprintf;
use PHPUnit\Framework\TestListener;
use PHPUnit\Runner\Exception;
use PHPUnit\Runner\Hook;
use PHPUnit\TextUI\TestRunner;
use PHPUnit\TextUI\XmlConfiguration\Extension;
use ReflectionClass;
use ReflectionException;




final class ExtensionHandler
{



public function registerExtension(Extension $extensionConfiguration, TestRunner $runner): void
{
$extension = $this->createInstance($extensionConfiguration);

if (!$extension instanceof Hook) {
throw new Exception(
sprintf(
'Class "%s" does not implement a PHPUnit\Runner\Hook interface',
$extensionConfiguration->className(),
),
);
}

$runner->addExtension($extension);
}






public function createTestListenerInstance(Extension $listenerConfiguration): TestListener
{
$listener = $this->createInstance($listenerConfiguration);

if (!$listener instanceof TestListener) {
throw new Exception(
sprintf(
'Class "%s" does not implement the PHPUnit\Framework\TestListener interface',
$listenerConfiguration->className(),
),
);
}

return $listener;
}




private function createInstance(Extension $extensionConfiguration): object
{
$this->ensureClassExists($extensionConfiguration);

try {
$reflector = new ReflectionClass($extensionConfiguration->className());
} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

if (!$extensionConfiguration->hasArguments()) {
return $reflector->newInstance();
}

return $reflector->newInstanceArgs($extensionConfiguration->arguments());
}




private function ensureClassExists(Extension $extensionConfiguration): void
{
if (class_exists($extensionConfiguration->className(), false)) {
return;
}

if ($extensionConfiguration->hasSourceFile()) {





require_once $extensionConfiguration->sourceFile();
}

if (!class_exists($extensionConfiguration->className())) {
throw new Exception(
sprintf(
'Class "%s" does not exist',
$extensionConfiguration->className(),
),
);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Extension;

use function count;
use function explode;
use function implode;
use function is_file;
use function strpos;
use PharIo\Manifest\ApplicationName;
use PharIo\Manifest\Exception as ManifestException;
use PharIo\Manifest\ManifestLoader;
use PharIo\Version\Version as PharIoVersion;
use PHPUnit\Runner\Version;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;




final class PharLoader
{



public function loadPharExtensionsInDirectory(string $directory): array
{
$loadedExtensions = [];
$notLoadedExtensions = [];

foreach ((new FileIteratorFacade)->getFilesAsArray($directory, '.phar') as $file) {
if (!is_file('phar://' . $file . '/manifest.xml')) {
$notLoadedExtensions[] = $file . ' is not an extension for PHPUnit';

continue;
}

try {
$applicationName = new ApplicationName('phpunit/phpunit');
$version = new PharIoVersion($this->phpunitVersion());
$manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml');

if (!$manifest->isExtensionFor($applicationName)) {
$notLoadedExtensions[] = $file . ' is not an extension for PHPUnit';

continue;
}

if (!$manifest->isExtensionFor($applicationName, $version)) {
$notLoadedExtensions[] = $file . ' is not compatible with this version of PHPUnit';

continue;
}
} catch (ManifestException $e) {
$notLoadedExtensions[] = $file . ': ' . $e->getMessage();

continue;
}






require $file;

$loadedExtensions[] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString();
}

return [
'loadedExtensions' => $loadedExtensions,
'notLoadedExtensions' => $notLoadedExtensions,
];
}

private function phpunitVersion(): string
{
$version = Version::id();

if (strpos($version, '-') === false) {
return $version;
}

$parts = explode('.', explode('-', $version)[0]);

if (count($parts) === 2) {
$parts[] = 0;
}

return implode('.', $parts);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use const DIRECTORY_SEPARATOR;
use const LOCK_EX;
use function assert;
use function dirname;
use function file_get_contents;
use function file_put_contents;
use function in_array;
use function is_array;
use function is_dir;
use function is_file;
use function json_decode;
use function json_encode;
use function sprintf;
use PHPUnit\Util\Filesystem;




final class DefaultTestResultCache implements TestResultCache
{



private const VERSION = 1;




private const ALLOWED_TEST_STATUSES = [
BaseTestRunner::STATUS_SKIPPED,
BaseTestRunner::STATUS_INCOMPLETE,
BaseTestRunner::STATUS_FAILURE,
BaseTestRunner::STATUS_ERROR,
BaseTestRunner::STATUS_RISKY,
BaseTestRunner::STATUS_WARNING,
];




private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache';




private $cacheFilename;




private $defects = [];




private $times = [];

public function __construct(?string $filepath = null)
{
if ($filepath !== null && is_dir($filepath)) {
$filepath .= DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME;
}

$this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME;
}

public function setState(string $testName, int $state): void
{
if (!in_array($state, self::ALLOWED_TEST_STATUSES, true)) {
return;
}

$this->defects[$testName] = $state;
}

public function getState(string $testName): int
{
return $this->defects[$testName] ?? BaseTestRunner::STATUS_UNKNOWN;
}

public function setTime(string $testName, float $time): void
{
$this->times[$testName] = $time;
}

public function getTime(string $testName): float
{
return $this->times[$testName] ?? 0.0;
}

public function load(): void
{
if (!is_file($this->cacheFilename)) {
return;
}

$data = json_decode(
file_get_contents($this->cacheFilename),
true,
);

if ($data === null) {
return;
}

if (!isset($data['version'])) {
return;
}

if ($data['version'] !== self::VERSION) {
return;
}

assert(isset($data['defects']) && is_array($data['defects']));
assert(isset($data['times']) && is_array($data['times']));

$this->defects = $data['defects'];
$this->times = $data['times'];
}




public function persist(): void
{
if (!Filesystem::createDirectory(dirname($this->cacheFilename))) {
throw new Exception(
sprintf(
'Cannot create directory "%s" for result cache file',
$this->cacheFilename,
),
);
}

file_put_contents(
$this->cacheFilename,
json_encode(
[
'version' => self::VERSION,
'defects' => $this->defects,
'times' => $this->times,
],
),
LOCK_EX,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterIncompleteTestHook extends TestHook
{
public function executeAfterIncompleteTest(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface TestHook extends Hook
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface BeforeFirstTestHook extends Hook
{
public function executeBeforeFirstTest(): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterTestFailureHook extends TestHook
{
public function executeAfterTestFailure(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterTestHook extends TestHook
{






public function executeAfterTest(string $test, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface BeforeTestHook extends TestHook
{
public function executeBeforeTest(string $test): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterTestErrorHook extends TestHook
{
public function executeAfterTestError(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterSuccessfulTestHook extends TestHook
{
public function executeAfterSuccessfulTest(string $test, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterSkippedTestHook extends TestHook
{
public function executeAfterSkippedTest(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface Hook
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterLastTestHook extends Hook
{
public function executeAfterLastTest(): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Test as TestUtil;
use Throwable;




final class TestListenerAdapter implements TestListener
{



private $hooks = [];




private $lastTestWasNotSuccessful;

public function add(TestHook $hook): void
{
$this->hooks[] = $hook;
}

public function startTest(Test $test): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof BeforeTestHook) {
$hook->executeBeforeTest(TestUtil::describeAsString($test));
}
}

$this->lastTestWasNotSuccessful = false;
}

public function addError(Test $test, Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestErrorHook) {
$hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}

$this->lastTestWasNotSuccessful = true;
}

public function addWarning(Test $test, Warning $e, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestWarningHook) {
$hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time);
}
}

$this->lastTestWasNotSuccessful = true;
}

public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestFailureHook) {
$hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time);
}
}

$this->lastTestWasNotSuccessful = true;
}

public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterIncompleteTestHook) {
$hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}

$this->lastTestWasNotSuccessful = true;
}

public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterRiskyTestHook) {
$hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}

$this->lastTestWasNotSuccessful = true;
}

public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterSkippedTestHook) {
$hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}

$this->lastTestWasNotSuccessful = true;
}

public function endTest(Test $test, float $time): void
{
if (!$this->lastTestWasNotSuccessful) {
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterSuccessfulTestHook) {
$hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time);
}
}
}

foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestHook) {
$hook->executeAfterTest(TestUtil::describeAsString($test), $time);
}
}
}

public function startTestSuite(TestSuite $suite): void
{
}

public function endTestSuite(TestSuite $suite): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterRiskyTestHook extends TestHook
{
public function executeAfterRiskyTest(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;










interface AfterTestWarningHook extends TestHook
{
public function executeAfterTestWarning(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;

use function is_dir;
use function is_file;
use function substr;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestSuite;
use ReflectionClass;
use ReflectionException;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;




abstract class BaseTestRunner
{



public const STATUS_UNKNOWN = -1;




public const STATUS_PASSED = 0;




public const STATUS_SKIPPED = 1;




public const STATUS_INCOMPLETE = 2;




public const STATUS_FAILURE = 3;




public const STATUS_ERROR = 4;




public const STATUS_RISKY = 5;




public const STATUS_WARNING = 6;




public const SUITE_METHODNAME = 'suite';




public function getLoader(): TestSuiteLoader
{
return new StandardTestSuiteLoader;
}










public function getTest(string $suiteClassFile, $suffixes = ''): ?TestSuite
{
if (is_dir($suiteClassFile)) {

$files = (new FileIteratorFacade)->getFilesAsArray(
$suiteClassFile,
$suffixes,
);

$suite = new TestSuite($suiteClassFile);
$suite->addTestFiles($files);

return $suite;
}

if (is_file($suiteClassFile) && substr($suiteClassFile, -5, 5) === '.phpt') {
$suite = new TestSuite;
$suite->addTestFile($suiteClassFile);

return $suite;
}

try {
$testClass = $this->loadSuiteClass(
$suiteClassFile,
);
} catch (\PHPUnit\Exception $e) {
$this->runFailed($e->getMessage());

return null;
}

try {
$suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME);

if (!$suiteMethod->isStatic()) {
$this->runFailed(
'suite() method must be static.',
);

return null;
}

$test = $suiteMethod->invoke(null, $testClass->getName());
} catch (ReflectionException $e) {
$test = new TestSuite($testClass);
}

$this->clearStatus();

return $test;
}




protected function loadSuiteClass(string $suiteClassFile): ReflectionClass
{
return $this->getLoader()->load($suiteClassFile);
}




protected function clearStatus(): void
{
}





abstract protected function runFailed(string $message): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner;




final class NullTestResultCache implements TestResultCache
{
public function setState(string $testName, int $state): void
{
}

public function getState(string $testName): int
{
return BaseTestRunner::STATUS_UNKNOWN;
}

public function setTime(string $testName, float $time): void
{
}

public function getTime(string $testName): float
{
return 0;
}

public function load(): void
{
}

public function persist(): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Filter;

use function assert;
use function sprintf;
use FilterIterator;
use Iterator;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\Exception;
use RecursiveFilterIterator;
use ReflectionClass;




final class Factory
{



private $filters = [];






public function addFilter(ReflectionClass $filter, $args): void
{
if (!$filter->isSubclassOf(RecursiveFilterIterator::class)) {
throw new Exception(
sprintf(
'Class "%s" does not extend RecursiveFilterIterator',
$filter->name,
),
);
}

$this->filters[] = [$filter, $args];
}

public function factory(Iterator $iterator, TestSuite $suite): FilterIterator
{
foreach ($this->filters as $filter) {
[$class, $args] = $filter;
$iterator = $class->newInstance($iterator, $args, $suite);
}

assert($iterator instanceof FilterIterator);

return $iterator;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Filter;

use function in_array;




final class IncludeGroupFilterIterator extends GroupFilterIterator
{
protected function doAccept(string $hash): bool
{
return in_array($hash, $this->groupTests, true);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Filter;

use function in_array;




final class ExcludeGroupFilterIterator extends GroupFilterIterator
{
protected function doAccept(string $hash): bool
{
return !in_array($hash, $this->groupTests, true);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Filter;

use function end;
use function implode;
use function preg_match;
use function sprintf;
use function str_replace;
use Exception;
use PHPUnit\Framework\ErrorTestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Util\RegularExpression;
use RecursiveFilterIterator;
use RecursiveIterator;




final class NameFilterIterator extends RecursiveFilterIterator
{



private $filter;




private $filterMin;




private $filterMax;




public function __construct(RecursiveIterator $iterator, string $filter)
{
parent::__construct($iterator);

$this->setFilter($filter);
}




public function accept(): bool
{
$test = $this->getInnerIterator()->current();

if ($test instanceof TestSuite) {
return true;
}

$tmp = \PHPUnit\Util\Test::describe($test);

if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) {
$name = $test->getMessage();
} elseif ($tmp[0] !== '') {
$name = implode('::', $tmp);
} else {
$name = $tmp[1];
}

$accepted = @preg_match($this->filter, $name, $matches);

if ($accepted && isset($this->filterMax)) {
$set = end($matches);
$accepted = $set >= $this->filterMin && $set <= $this->filterMax;
}

return (bool) $accepted;
}




private function setFilter(string $filter): void
{
if (RegularExpression::safeMatch($filter, '') === false) {



if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) {
if (isset($matches[3]) && $matches[2] < $matches[3]) {
$filter = sprintf(
'%s.*with data set #(\d+)$',
$matches[1],
);

$this->filterMin = (int) $matches[2];
$this->filterMax = (int) $matches[3];
} else {
$filter = sprintf(
'%s.*with data set #%s$',
$matches[1],
$matches[2],
);
}
} 


elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) {
$filter = sprintf(
'%s.*with data set "%s"$',
$matches[1],
$matches[2],
);
}



$filter = sprintf(
'/%s/i',
str_replace(
'/',
'\\/',
$filter,
),
);
}

$this->filter = $filter;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Runner\Filter;

use function array_map;
use function array_merge;
use function in_array;
use function spl_object_hash;
use PHPUnit\Framework\TestSuite;
use RecursiveFilterIterator;
use RecursiveIterator;




abstract class GroupFilterIterator extends RecursiveFilterIterator
{



protected $groupTests = [];

public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite)
{
parent::__construct($iterator);

foreach ($suite->getGroupDetails() as $group => $tests) {
if (in_array((string) $group, $groups, true)) {
$testHashes = array_map(
'spl_object_hash',
$tests,
);

$this->groupTests = array_merge($this->groupTests, $testHashes);
}
}
}

public function accept(): bool
{
$test = $this->getInnerIterator()->current();

if ($test instanceof TestSuite) {
return true;
}

return $this->doAccept(spl_object_hash($test));
}

abstract protected function doAccept(string $hash);
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class WarningTestCase extends TestCase
{



protected $backupGlobals = false;




protected $backupStaticAttributes = false;




protected $runTestInSeparateProcess = false;




private $message;

public function __construct(string $message = '')
{
$this->message = $message;

parent::__construct('Warning');
}

public function getMessage(): string
{
return $this->message;
}




public function toString(): string
{
return 'Warning';
}






protected function runTest(): void
{
throw new Warning($this->message);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use Throwable;




interface IncompleteTest extends Throwable
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class ErrorTestCase extends TestCase
{



protected $backupGlobals = false;




protected $backupStaticAttributes = false;




protected $runTestInSeparateProcess = false;




private $message;

public function __construct(string $message = '')
{
$this->message = $message;

parent::__construct('Error');
}

public function getMessage(): string
{
return $this->message;
}




public function toString(): string
{
return 'Error';
}






protected function runTest(): void
{
throw new Error($this->message);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function class_exists;
use function count;
use function extension_loaded;
use function function_exists;
use function get_class;
use function sprintf;
use function xdebug_get_monitored_functions;
use function xdebug_is_debugger_active;
use function xdebug_start_function_monitor;
use function xdebug_stop_function_monitor;
use AssertionError;
use Countable;
use Error;
use PHPUnit\Util\ErrorHandler;
use PHPUnit\Util\ExcludeList;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Test as TestUtil;
use ReflectionClass;
use ReflectionException;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException;
use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
use SebastianBergmann\Invoker\Invoker;
use SebastianBergmann\Invoker\TimeoutException;
use SebastianBergmann\ResourceOperations\ResourceOperations;
use SebastianBergmann\Timer\Timer;
use Throwable;




final class TestResult implements Countable
{



private $passed = [];




private $passedTestClasses = [];




private $currentTestSuiteFailed = false;




private $errors = [];




private $failures = [];




private $warnings = [];




private $notImplemented = [];




private $risky = [];




private $skipped = [];






private $listeners = [];




private $runTests = 0;




private $time = 0;






private $codeCoverage;




private $convertDeprecationsToExceptions = false;




private $convertErrorsToExceptions = true;




private $convertNoticesToExceptions = true;




private $convertWarningsToExceptions = true;




private $stop = false;




private $stopOnError = false;




private $stopOnFailure = false;




private $stopOnWarning = false;




private $beStrictAboutTestsThatDoNotTestAnything = true;




private $beStrictAboutOutputDuringTests = false;




private $beStrictAboutTodoAnnotatedTests = false;




private $beStrictAboutResourceUsageDuringSmallTests = false;




private $enforceTimeLimit = false;




private $forceCoversAnnotation = false;




private $timeoutForSmallTests = 1;




private $timeoutForMediumTests = 10;




private $timeoutForLargeTests = 60;




private $stopOnRisky = false;




private $stopOnIncomplete = false;




private $stopOnSkipped = false;




private $lastTestFailed = false;




private $defaultTimeLimit = 0;




private $stopOnDefect = false;




private $registerMockObjectsFromTestArgumentsRecursively = false;








public function addListener(TestListener $listener): void
{
$this->listeners[] = $listener;
}








public function removeListener(TestListener $listener): void
{
foreach ($this->listeners as $key => $_listener) {
if ($listener === $_listener) {
unset($this->listeners[$key]);
}
}
}








public function flushListeners(): void
{
foreach ($this->listeners as $listener) {
if ($listener instanceof Printer) {
$listener->flush();
}
}
}




public function addError(Test $test, Throwable $t, float $time): void
{
if ($t instanceof RiskyTestError) {
$this->recordRisky($test, $t);

$notifyMethod = 'addRiskyTest';

if ($test instanceof TestCase) {
$test->markAsRisky();
}

if ($this->stopOnRisky || $this->stopOnDefect) {
$this->stop();
}
} elseif ($t instanceof IncompleteTest) {
$this->recordNotImplemented($test, $t);

$notifyMethod = 'addIncompleteTest';

if ($this->stopOnIncomplete) {
$this->stop();
}
} elseif ($t instanceof SkippedTest) {
$this->recordSkipped($test, $t);

$notifyMethod = 'addSkippedTest';

if ($this->stopOnSkipped) {
$this->stop();
}
} else {
$this->recordError($test, $t);

$notifyMethod = 'addError';

if ($this->stopOnError || $this->stopOnFailure) {
$this->stop();
}
}


if ($t instanceof Error) {
$t = new ExceptionWrapper($t);
}

foreach ($this->listeners as $listener) {
$listener->{$notifyMethod}($test, $t, $time);
}

$this->lastTestFailed = true;
$this->time += $time;
}





public function addWarning(Test $test, Warning $e, float $time): void
{
if ($this->stopOnWarning || $this->stopOnDefect) {
$this->stop();
}

$this->recordWarning($test, $e);

foreach ($this->listeners as $listener) {
$listener->addWarning($test, $e, $time);
}

$this->time += $time;
}





public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
if ($e instanceof RiskyTestError || $e instanceof OutputError) {
$this->recordRisky($test, $e);

$notifyMethod = 'addRiskyTest';

if ($test instanceof TestCase) {
$test->markAsRisky();
}

if ($this->stopOnRisky || $this->stopOnDefect) {
$this->stop();
}
} elseif ($e instanceof IncompleteTest) {
$this->recordNotImplemented($test, $e);

$notifyMethod = 'addIncompleteTest';

if ($this->stopOnIncomplete) {
$this->stop();
}
} elseif ($e instanceof SkippedTest) {
$this->recordSkipped($test, $e);

$notifyMethod = 'addSkippedTest';

if ($this->stopOnSkipped) {
$this->stop();
}
} else {
$this->failures[] = new TestFailure($test, $e);
$notifyMethod = 'addFailure';

if ($this->stopOnFailure || $this->stopOnDefect) {
$this->stop();
}
}

foreach ($this->listeners as $listener) {
$listener->{$notifyMethod}($test, $e, $time);
}

$this->lastTestFailed = true;
$this->time += $time;
}




public function startTestSuite(TestSuite $suite): void
{
$this->currentTestSuiteFailed = false;

foreach ($this->listeners as $listener) {
$listener->startTestSuite($suite);
}
}




public function endTestSuite(TestSuite $suite): void
{
if (!$this->currentTestSuiteFailed) {
$this->passedTestClasses[] = $suite->getName();
}

foreach ($this->listeners as $listener) {
$listener->endTestSuite($suite);
}
}




public function startTest(Test $test): void
{
$this->lastTestFailed = false;
$this->runTests += count($test);

foreach ($this->listeners as $listener) {
$listener->startTest($test);
}
}






public function endTest(Test $test, float $time): void
{
foreach ($this->listeners as $listener) {
$listener->endTest($test, $time);
}

if (!$this->lastTestFailed && $test instanceof TestCase) {
$class = get_class($test);
$key = $class . '::' . $test->getName();

$this->passed[$key] = [
'result' => $test->getResult(),
'size' => TestUtil::getSize(
$class,
$test->getName(false),
),
];

$this->time += $time;
}

if ($this->lastTestFailed && $test instanceof TestCase) {
$this->currentTestSuiteFailed = true;
}
}




public function allHarmless(): bool
{
return $this->riskyCount() === 0;
}




public function riskyCount(): int
{
return count($this->risky);
}




public function allCompletelyImplemented(): bool
{
return $this->notImplementedCount() === 0;
}




public function notImplementedCount(): int
{
return count($this->notImplemented);
}






public function risky(): array
{
return $this->risky;
}






public function notImplemented(): array
{
return $this->notImplemented;
}




public function noneSkipped(): bool
{
return $this->skippedCount() === 0;
}




public function skippedCount(): int
{
return count($this->skipped);
}






public function skipped(): array
{
return $this->skipped;
}




public function errorCount(): int
{
return count($this->errors);
}






public function errors(): array
{
return $this->errors;
}




public function failureCount(): int
{
return count($this->failures);
}






public function failures(): array
{
return $this->failures;
}




public function warningCount(): int
{
return count($this->warnings);
}






public function warnings(): array
{
return $this->warnings;
}




public function passed(): array
{
return $this->passed;
}






public function passedClasses(): array
{
return $this->passedTestClasses;
}




public function getCollectCodeCoverageInformation(): bool
{
return $this->codeCoverage !== null;
}









public function run(Test $test): void
{
Assert::resetCount();

$size = TestUtil::UNKNOWN;

if ($test instanceof TestCase) {
$test->setRegisterMockObjectsFromTestArgumentsRecursively(
$this->registerMockObjectsFromTestArgumentsRecursively,
);

$isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test);
$size = $test->getSize();
}

$error = false;
$failure = false;
$warning = false;
$incomplete = false;
$risky = false;
$skipped = false;

$this->startTest($test);

if ($this->convertDeprecationsToExceptions || $this->convertErrorsToExceptions || $this->convertNoticesToExceptions || $this->convertWarningsToExceptions) {
$errorHandler = new ErrorHandler(
$this->convertDeprecationsToExceptions,
$this->convertErrorsToExceptions,
$this->convertNoticesToExceptions,
$this->convertWarningsToExceptions,
);

$errorHandler->register();
}

$collectCodeCoverage = $this->codeCoverage !== null &&
!$test instanceof ErrorTestCase &&
!$test instanceof WarningTestCase &&
$isAnyCoverageRequired;

if ($collectCodeCoverage) {
$this->codeCoverage->start($test);
}

$monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests &&
!$test instanceof ErrorTestCase &&
!$test instanceof WarningTestCase &&
$size === TestUtil::SMALL &&
function_exists('xdebug_start_function_monitor');

if ($monitorFunctions) {

xdebug_start_function_monitor(ResourceOperations::getFunctions());
}

$timer = new Timer;
$timer->start();

try {
$invoker = new Invoker;

if (!$test instanceof ErrorTestCase &&
!$test instanceof WarningTestCase &&
$this->shouldTimeLimitBeEnforced($size) &&
$invoker->canInvokeWithTimeout()) {
switch ($size) {
case TestUtil::SMALL:
$_timeout = $this->timeoutForSmallTests;

break;

case TestUtil::MEDIUM:
$_timeout = $this->timeoutForMediumTests;

break;

case TestUtil::LARGE:
$_timeout = $this->timeoutForLargeTests;

break;

default:
$_timeout = $this->defaultTimeLimit;
}

$invoker->invoke([$test, 'runBare'], [], $_timeout);
} else {
$test->runBare();
}
} catch (TimeoutException $e) {
$this->addFailure(
$test,
new RiskyTestError(
$e->getMessage(),
),
$_timeout,
);

$risky = true;
} catch (AssertionFailedError $e) {
$failure = true;

if ($e instanceof RiskyTestError) {
$risky = true;
} elseif ($e instanceof IncompleteTestError) {
$incomplete = true;
} elseif ($e instanceof SkippedTestError) {
$skipped = true;
}
} catch (AssertionError $e) {
$test->addToAssertionCount(1);

$failure = true;
$frame = $e->getTrace()[0];

$e = new AssertionFailedError(
sprintf(
'%s in %s:%s',
$e->getMessage(),
$frame['file'] ?? $e->getFile(),
$frame['line'] ?? $e->getLine(),
),
0,
$e,
);
} catch (Warning $e) {
$warning = true;
} catch (Exception $e) {
$error = true;
} catch (Throwable $e) {
$e = new ExceptionWrapper($e);
$error = true;
}

$time = $timer->stop()->asSeconds();

$test->addToAssertionCount(Assert::getCount());

if ($monitorFunctions) {
$excludeList = new ExcludeList;


$functions = xdebug_get_monitored_functions();


xdebug_stop_function_monitor();

foreach ($functions as $function) {
if (!$excludeList->isExcluded($function['filename'])) {
$this->addFailure(
$test,
new RiskyTestError(
sprintf(
'%s() used in %s:%s',
$function['function'],
$function['filename'],
$function['lineno'],
),
),
$time,
);
}
}
}

if ($this->beStrictAboutTestsThatDoNotTestAnything &&
!$test->doesNotPerformAssertions() &&
$test->getNumAssertions() === 0) {
$risky = true;
}

if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) {
$annotations = TestUtil::parseTestMethodAnnotations(
get_class($test),
$test->getName(false),
);

if (!isset($annotations['class']['covers']) &&
!isset($annotations['method']['covers']) &&
!isset($annotations['class']['coversNothing']) &&
!isset($annotations['method']['coversNothing'])) {
$this->addFailure(
$test,
new MissingCoversAnnotationException(
'This test does not have a @covers annotation but is expected to have one',
),
$time,
);

$risky = true;
}
}

if ($collectCodeCoverage) {
$append = !$risky && !$incomplete && !$skipped;
$linesToBeCovered = [];
$linesToBeUsed = [];

if ($append && $test instanceof TestCase) {
try {
$linesToBeCovered = TestUtil::getLinesToBeCovered(
get_class($test),
$test->getName(false),
);

$linesToBeUsed = TestUtil::getLinesToBeUsed(
get_class($test),
$test->getName(false),
);
} catch (InvalidCoversTargetException $cce) {
$this->addWarning(
$test,
new Warning(
$cce->getMessage(),
),
$time,
);
}
}

try {
$this->codeCoverage->stop(
$append,
$linesToBeCovered,
$linesToBeUsed,
);
} catch (UnintentionallyCoveredCodeException $cce) {
$unintentionallyCoveredCodeError = new UnintentionallyCoveredCodeError(
'This test executed code that is not listed as code to be covered or used:' .
PHP_EOL . $cce->getMessage(),
);
} catch (OriginalCodeCoverageException $cce) {
$error = true;

$e = $e ?? $cce;
}
}

if (isset($errorHandler)) {
$errorHandler->unregister();

unset($errorHandler);
}

if ($error) {
$this->addError($test, $e, $time);
} elseif ($failure) {
$this->addFailure($test, $e, $time);
} elseif ($warning) {
$this->addWarning($test, $e, $time);
} elseif (isset($unintentionallyCoveredCodeError)) {
$this->addFailure(
$test,
$unintentionallyCoveredCodeError,
$time,
);
} elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
!$test->doesNotPerformAssertions() &&
$test->getNumAssertions() === 0) {
try {
$reflected = new ReflectionClass($test);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$name = $test->getName(false);

if ($name && $reflected->hasMethod($name)) {
try {
$reflected = $reflected->getMethod($name);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}

$this->addFailure(
$test,
new RiskyTestError(
sprintf(
"This test did not perform any assertions\n\n%s:%d",
$reflected->getFileName(),
$reflected->getStartLine(),
),
),
$time,
);
} elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
$test->doesNotPerformAssertions() &&
$test->getNumAssertions() > 0) {
$this->addFailure(
$test,
new RiskyTestError(
sprintf(
'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions',
$test->getNumAssertions(),
),
),
$time,
);
} elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
$this->addFailure(
$test,
new OutputError(
sprintf(
'This test printed output: %s',
$test->getActualOutput(),
),
),
$time,
);
} elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) {
$annotations = TestUtil::parseTestMethodAnnotations(
get_class($test),
$test->getName(false),
);

if (isset($annotations['method']['todo'])) {
$this->addFailure(
$test,
new RiskyTestError(
'Test method is annotated with @todo',
),
$time,
);
}
}

$this->endTest($test, $time);
}




public function count(): int
{
return $this->runTests;
}




public function shouldStop(): bool
{
return $this->stop;
}




public function stop(): void
{
$this->stop = true;
}




public function getCodeCoverage(): ?CodeCoverage
{
return $this->codeCoverage;
}




public function setCodeCoverage(CodeCoverage $codeCoverage): void
{
$this->codeCoverage = $codeCoverage;
}




public function convertDeprecationsToExceptions(bool $flag): void
{
$this->convertDeprecationsToExceptions = $flag;
}




public function getConvertDeprecationsToExceptions(): bool
{
return $this->convertDeprecationsToExceptions;
}




public function convertErrorsToExceptions(bool $flag): void
{
$this->convertErrorsToExceptions = $flag;
}




public function getConvertErrorsToExceptions(): bool
{
return $this->convertErrorsToExceptions;
}




public function convertNoticesToExceptions(bool $flag): void
{
$this->convertNoticesToExceptions = $flag;
}




public function getConvertNoticesToExceptions(): bool
{
return $this->convertNoticesToExceptions;
}




public function convertWarningsToExceptions(bool $flag): void
{
$this->convertWarningsToExceptions = $flag;
}




public function getConvertWarningsToExceptions(): bool
{
return $this->convertWarningsToExceptions;
}




public function stopOnError(bool $flag): void
{
$this->stopOnError = $flag;
}




public function stopOnFailure(bool $flag): void
{
$this->stopOnFailure = $flag;
}




public function stopOnWarning(bool $flag): void
{
$this->stopOnWarning = $flag;
}

public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void
{
$this->beStrictAboutTestsThatDoNotTestAnything = $flag;
}

public function isStrictAboutTestsThatDoNotTestAnything(): bool
{
return $this->beStrictAboutTestsThatDoNotTestAnything;
}

public function beStrictAboutOutputDuringTests(bool $flag): void
{
$this->beStrictAboutOutputDuringTests = $flag;
}

public function isStrictAboutOutputDuringTests(): bool
{
return $this->beStrictAboutOutputDuringTests;
}

public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void
{
$this->beStrictAboutResourceUsageDuringSmallTests = $flag;
}

public function isStrictAboutResourceUsageDuringSmallTests(): bool
{
return $this->beStrictAboutResourceUsageDuringSmallTests;
}

public function enforceTimeLimit(bool $flag): void
{
$this->enforceTimeLimit = $flag;
}

public function enforcesTimeLimit(): bool
{
return $this->enforceTimeLimit;
}

public function beStrictAboutTodoAnnotatedTests(bool $flag): void
{
$this->beStrictAboutTodoAnnotatedTests = $flag;
}

public function isStrictAboutTodoAnnotatedTests(): bool
{
return $this->beStrictAboutTodoAnnotatedTests;
}

public function forceCoversAnnotation(): void
{
$this->forceCoversAnnotation = true;
}

public function forcesCoversAnnotation(): bool
{
return $this->forceCoversAnnotation;
}




public function stopOnRisky(bool $flag): void
{
$this->stopOnRisky = $flag;
}




public function stopOnIncomplete(bool $flag): void
{
$this->stopOnIncomplete = $flag;
}




public function stopOnSkipped(bool $flag): void
{
$this->stopOnSkipped = $flag;
}




public function stopOnDefect(bool $flag): void
{
$this->stopOnDefect = $flag;
}




public function time(): float
{
return $this->time;
}




public function wasSuccessful(): bool
{
return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings);
}

public function wasSuccessfulIgnoringWarnings(): bool
{
return empty($this->errors) && empty($this->failures);
}

public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete(): bool
{
return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped();
}




public function setDefaultTimeLimit(int $timeout): void
{
$this->defaultTimeLimit = $timeout;
}




public function setTimeoutForSmallTests(int $timeout): void
{
$this->timeoutForSmallTests = $timeout;
}




public function setTimeoutForMediumTests(int $timeout): void
{
$this->timeoutForMediumTests = $timeout;
}




public function setTimeoutForLargeTests(int $timeout): void
{
$this->timeoutForLargeTests = $timeout;
}




public function getTimeoutForLargeTests(): int
{
return $this->timeoutForLargeTests;
}

public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
{
$this->registerMockObjectsFromTestArgumentsRecursively = $flag;
}

private function recordError(Test $test, Throwable $t): void
{
$this->errors[] = new TestFailure($test, $t);
}

private function recordNotImplemented(Test $test, Throwable $t): void
{
$this->notImplemented[] = new TestFailure($test, $t);
}

private function recordRisky(Test $test, Throwable $t): void
{
$this->risky[] = new TestFailure($test, $t);
}

private function recordSkipped(Test $test, Throwable $t): void
{
$this->skipped[] = new TestFailure($test, $t);
}

private function recordWarning(Test $test, Throwable $t): void
{
$this->warnings[] = new TestFailure($test, $t);
}

private function shouldTimeLimitBeEnforced(int $size): bool
{
if (!$this->enforceTimeLimit) {
return false;
}

if (!(($this->defaultTimeLimit || $size !== TestUtil::UNKNOWN))) {
return false;
}

if (!extension_loaded('pcntl')) {
return false;
}

if (!class_exists(Invoker::class)) {
return false;
}

if (extension_loaded('xdebug') && xdebug_is_debugger_active()) {
return false;
}

return true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class SkippedTestCase extends TestCase
{



protected $backupGlobals = false;




protected $backupStaticAttributes = false;




protected $runTestInSeparateProcess = false;




private $message;

public function __construct(string $className, string $methodName, string $message = '')
{
parent::__construct($className . '::' . $methodName);

$this->message = $message;
}

public function getMessage(): string
{
return $this->message;
}






public function toString(): string
{
return $this->getName();
}




protected function runTest(): void
{
$this->markTestSkipped($this->message);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function explode;
use PHPUnit\Util\Test as TestUtil;




final class DataProviderTestSuite extends TestSuite
{



private $dependencies = [];




public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;

foreach ($this->tests as $test) {
if (!$test instanceof TestCase) {

continue;

}
$test->setDependencies($dependencies);
}
}




public function provides(): array
{
if ($this->providedTests === null) {
$this->providedTests = [new ExecutionOrderDependency($this->getName())];
}

return $this->providedTests;
}




public function requires(): array
{


return $this->dependencies;
}






public function getSize(): int
{
[$className, $methodName] = explode('::', $this->getName());

return TestUtil::getSize($className, $methodName);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const LC_ALL;
use const LC_COLLATE;
use const LC_CTYPE;
use const LC_MONETARY;
use const LC_NUMERIC;
use const LC_TIME;
use const PATHINFO_FILENAME;
use const PHP_EOL;
use const PHP_URL_PATH;
use function array_filter;
use function array_flip;
use function array_keys;
use function array_merge;
use function array_pop;
use function array_search;
use function array_unique;
use function array_values;
use function basename;
use function call_user_func;
use function chdir;
use function class_exists;
use function clearstatcache;
use function count;
use function debug_backtrace;
use function defined;
use function explode;
use function get_class;
use function get_include_path;
use function getcwd;
use function implode;
use function in_array;
use function ini_set;
use function is_array;
use function is_callable;
use function is_int;
use function is_object;
use function is_string;
use function libxml_clear_errors;
use function method_exists;
use function ob_end_clean;
use function ob_get_contents;
use function ob_get_level;
use function ob_start;
use function parse_url;
use function pathinfo;
use function preg_replace;
use function serialize;
use function setlocale;
use function sprintf;
use function strpos;
use function substr;
use function sys_get_temp_dir;
use function tempnam;
use function trim;
use function var_export;
use DeepCopy\DeepCopy;
use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint;
use PHPUnit\Framework\Constraint\ExceptionCode;
use PHPUnit\Framework\Constraint\ExceptionMessage;
use PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression;
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning as WarningError;
use PHPUnit\Framework\MockObject\Generator as MockGenerator;
use PHPUnit\Framework\MockObject\MockBuilder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub;
use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub;
use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub;
use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Cloner;
use PHPUnit\Util\Exception as UtilException;
use PHPUnit\Util\GlobalState;
use PHPUnit\Util\PHP\AbstractPhpProcess;
use PHPUnit\Util\Test as TestUtil;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophet;
use ReflectionClass;
use ReflectionException;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\GlobalState\ExcludeList;
use SebastianBergmann\GlobalState\Restorer;
use SebastianBergmann\GlobalState\Snapshot;
use SebastianBergmann\ObjectEnumerator\Enumerator;
use SebastianBergmann\Template\Template;
use SoapClient;
use Throwable;




abstract class TestCase extends Assert implements Reorderable, SelfDescribing, Test
{
private const LOCALE_CATEGORIES = [LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME];




protected $backupGlobals;




protected $backupGlobalsExcludeList = [];






protected $backupGlobalsBlacklist = [];




protected $backupStaticAttributes;




protected $backupStaticAttributesExcludeList = [];






protected $backupStaticAttributesBlacklist = [];




protected $runTestInSeparateProcess;




protected $preserveGlobalState = true;




protected $providedTests = [];




private $runClassInSeparateProcess;




private $inIsolation = false;




private $data;




private $dataName;




private $expectedException;




private $expectedExceptionMessage;




private $expectedExceptionMessageRegExp;




private $expectedExceptionCode;




private $name = '';




private $dependencies = [];




private $dependencyInput = [];




private $iniSettings = [];




private $locale = [];




private $mockObjects = [];




private $mockObjectGenerator;




private $status = BaseTestRunner::STATUS_UNKNOWN;




private $statusMessage = '';




private $numAssertions = 0;




private $result;




private $testResult;




private $output = '';




private $outputExpectedRegex;




private $outputExpectedString;




private $outputCallback = false;




private $outputBufferingActive = false;




private $outputBufferingLevel;




private $outputRetrievedForAssertion = false;




private $snapshot;




private $prophet;




private $beStrictAboutChangesToGlobalState = false;




private $registerMockObjectsFromTestArgumentsRecursively = false;




private $warnings = [];




private $groups = [];




private $doesNotPerformAssertions = false;




private $customComparators = [];




private $doubledTypes = [];





public static function any(): AnyInvokedCountMatcher
{
return new AnyInvokedCountMatcher;
}




public static function never(): InvokedCountMatcher
{
return new InvokedCountMatcher(0);
}





public static function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher
{
return new InvokedAtLeastCountMatcher(
$requiredInvocations,
);
}




public static function atLeastOnce(): InvokedAtLeastOnceMatcher
{
return new InvokedAtLeastOnceMatcher;
}




public static function once(): InvokedCountMatcher
{
return new InvokedCountMatcher(1);
}





public static function exactly(int $count): InvokedCountMatcher
{
return new InvokedCountMatcher($count);
}





public static function atMost(int $allowedInvocations): InvokedAtMostCountMatcher
{
return new InvokedAtMostCountMatcher($allowedInvocations);
}









public static function at(int $index): InvokedAtIndexMatcher
{
$stack = debug_backtrace();

while (!empty($stack)) {
$frame = array_pop($stack);

if (isset($frame['object']) && $frame['object'] instanceof self) {
$frame['object']->addWarning(
'The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.',
);

break;
}
}

return new InvokedAtIndexMatcher($index);
}

public static function returnValue($value): ReturnStub
{
return new ReturnStub($value);
}

public static function returnValueMap(array $valueMap): ReturnValueMapStub
{
return new ReturnValueMapStub($valueMap);
}

public static function returnArgument(int $argumentIndex): ReturnArgumentStub
{
return new ReturnArgumentStub($argumentIndex);
}

public static function returnCallback($callback): ReturnCallbackStub
{
return new ReturnCallbackStub($callback);
}






public static function returnSelf(): ReturnSelfStub
{
return new ReturnSelfStub;
}

public static function throwException(Throwable $exception): ExceptionStub
{
return new ExceptionStub($exception);
}

public static function onConsecutiveCalls(...$args): ConsecutiveCallsStub
{
return new ConsecutiveCallsStub($args);
}






public function __construct(?string $name = null, array $data = [], $dataName = '')
{
if ($name !== null) {
$this->setName($name);
}

$this->data = $data;
$this->dataName = $dataName;
}




public static function setUpBeforeClass(): void
{
}




public static function tearDownAfterClass(): void
{
}




protected function setUp(): void
{
}






protected function assertPreConditions(): void
{
}






protected function assertPostConditions(): void
{
}




protected function tearDown(): void
{
}







public function toString(): string
{
try {
$class = new ReflectionClass($this);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$buffer = sprintf(
'%s::%s',
$class->name,
$this->getName(false),
);

return $buffer . $this->getDataSetAsString();
}

public function count(): int
{
return 1;
}

public function getActualOutputForAssertion(): string
{
$this->outputRetrievedForAssertion = true;

return $this->getActualOutput();
}

public function expectOutputRegex(string $expectedRegex): void
{
$this->outputExpectedRegex = $expectedRegex;
}

public function expectOutputString(string $expectedString): void
{
$this->outputExpectedString = $expectedString;
}




public function expectException(string $exception): void
{

switch ($exception) {
case Deprecated::class:
$this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');

break;

case Error::class:
$this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');

break;

case Notice::class:
$this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');

break;

case WarningError::class:
$this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');

break;
}


$this->expectedException = $exception;
}




public function expectExceptionCode($code): void
{
$this->expectedExceptionCode = $code;
}

public function expectExceptionMessage(string $message): void
{
$this->expectedExceptionMessage = $message;
}

public function expectExceptionMessageMatches(string $regularExpression): void
{
$this->expectedExceptionMessageRegExp = $regularExpression;
}






public function expectExceptionObject(\Exception $exception): void
{
$this->expectException(get_class($exception));
$this->expectExceptionMessage($exception->getMessage());
$this->expectExceptionCode($exception->getCode());
}

public function expectNotToPerformAssertions(): void
{
$this->doesNotPerformAssertions = true;
}




public function expectDeprecation(): void
{
$this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectedException = Deprecated::class;
}




public function expectDeprecationMessage(string $message): void
{
$this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessage($message);
}




public function expectDeprecationMessageMatches(string $regularExpression): void
{
$this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessageMatches($regularExpression);
}




public function expectNotice(): void
{
$this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectedException = Notice::class;
}




public function expectNoticeMessage(string $message): void
{
$this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessage($message);
}




public function expectNoticeMessageMatches(string $regularExpression): void
{
$this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessageMatches($regularExpression);
}




public function expectWarning(): void
{
$this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectedException = WarningError::class;
}




public function expectWarningMessage(string $message): void
{
$this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessage($message);
}




public function expectWarningMessageMatches(string $regularExpression): void
{
$this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessageMatches($regularExpression);
}




public function expectError(): void
{
$this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectedException = Error::class;
}




public function expectErrorMessage(string $message): void
{
$this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessage($message);
}




public function expectErrorMessageMatches(string $regularExpression): void
{
$this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');

$this->expectExceptionMessageMatches($regularExpression);
}

public function getStatus(): int
{
return $this->status;
}

public function markAsRisky(): void
{
$this->status = BaseTestRunner::STATUS_RISKY;
}

public function getStatusMessage(): string
{
return $this->statusMessage;
}

public function hasFailed(): bool
{
$status = $this->getStatus();

return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR;
}











public function run(TestResult $result = null): TestResult
{
if ($result === null) {
$result = $this->createResult();
}

if (!$this instanceof ErrorTestCase && !$this instanceof WarningTestCase) {
$this->setTestResultObject($result);
}

if (!$this instanceof ErrorTestCase &&
!$this instanceof WarningTestCase &&
!$this instanceof SkippedTestCase &&
!$this->handleDependencies()) {
return $result;
}

if ($this->runInSeparateProcess()) {
$runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess;

try {
$class = new ReflectionClass($this);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($runEntireClass) {
$template = new Template(
__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl',
);
} else {
$template = new Template(
__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl',
);
}

if ($this->preserveGlobalState) {
$constants = GlobalState::getConstantsAsString();
$globals = GlobalState::getGlobalsAsString();
$includedFiles = GlobalState::getIncludedFilesAsString();
$iniSettings = GlobalState::getIniSettingsAsString();
} else {
$constants = '';

if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
$globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n";
} else {
$globals = '';
}

$includedFiles = '';
$iniSettings = '';
}

$coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false';
$isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false';
$isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false';
$enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false';
$isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false';
$isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false';

if (defined('PHPUNIT_COMPOSER_INSTALL')) {
$composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true);
} else {
$composerAutoload = '\'\'';
}

if (defined('__PHPUNIT_PHAR__')) {
$phar = var_export(__PHPUNIT_PHAR__, true);
} else {
$phar = '\'\'';
}

$codeCoverage = $result->getCodeCoverage();
$codeCoverageFilter = null;
$cachesStaticAnalysis = 'false';
$codeCoverageCacheDirectory = null;
$driverMethod = 'forLineCoverage';

if ($codeCoverage) {
$codeCoverageFilter = $codeCoverage->filter();

if ($codeCoverage->collectsBranchAndPathCoverage()) {
$driverMethod = 'forLineAndPathCoverage';
}

if ($codeCoverage->cachesStaticAnalysis()) {
$cachesStaticAnalysis = 'true';
$codeCoverageCacheDirectory = $codeCoverage->cacheDirectory();
}
}

$data = var_export(serialize($this->data), true);
$dataName = var_export($this->dataName, true);
$dependencyInput = var_export(serialize($this->dependencyInput), true);
$includePath = var_export(get_include_path(), true);
$codeCoverageFilter = var_export(serialize($codeCoverageFilter), true);
$codeCoverageCacheDirectory = var_export(serialize($codeCoverageCacheDirectory), true);


$data = "'." . $data . ".'";
$dataName = "'.(" . $dataName . ").'";
$dependencyInput = "'." . $dependencyInput . ".'";
$includePath = "'." . $includePath . ".'";
$codeCoverageFilter = "'." . $codeCoverageFilter . ".'";
$codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'";

$configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? '';
$processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_');

$var = [
'composerAutoload' => $composerAutoload,
'phar' => $phar,
'filename' => $class->getFileName(),
'className' => $class->getName(),
'collectCodeCoverageInformation' => $coverage,
'cachesStaticAnalysis' => $cachesStaticAnalysis,
'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory,
'driverMethod' => $driverMethod,
'data' => $data,
'dataName' => $dataName,
'dependencyInput' => $dependencyInput,
'constants' => $constants,
'globals' => $globals,
'include_path' => $includePath,
'included_files' => $includedFiles,
'iniSettings' => $iniSettings,
'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything,
'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests,
'enforcesTimeLimit' => $enforcesTimeLimit,
'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests,
'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests,
'codeCoverageFilter' => $codeCoverageFilter,
'configurationFilePath' => $configurationFilePath,
'name' => $this->getName(false),
'processResultFile' => $processResultFile,
];

if (!$runEntireClass) {
$var['methodName'] = $this->name;
}

$template->setVar($var);

$php = AbstractPhpProcess::factory();
$php->runTestJob($template->render(), $this, $result, $processResultFile);
} else {
$result->run($this);
}

$this->result = null;

return $result;
}










public function getMockBuilder(string $className): MockBuilder
{
$this->recordDoubledType($className);

return new MockBuilder($this, $className);
}

public function registerComparator(Comparator $comparator): void
{
ComparatorFactory::getInstance()->register($comparator);

$this->customComparators[] = $comparator;
}






public function doubledTypes(): array
{
return array_unique($this->doubledTypes);
}




public function getGroups(): array
{
return $this->groups;
}




public function setGroups(array $groups): void
{
$this->groups = $groups;
}






public function getName(bool $withDataSet = true): string
{
if ($withDataSet) {
return $this->name . $this->getDataSetAsString(false);
}

return $this->name;
}








public function getSize(): int
{
return TestUtil::getSize(
static::class,
$this->getName(false),
);
}






public function hasSize(): bool
{
return $this->getSize() !== TestUtil::UNKNOWN;
}






public function isSmall(): bool
{
return $this->getSize() === TestUtil::SMALL;
}






public function isMedium(): bool
{
return $this->getSize() === TestUtil::MEDIUM;
}






public function isLarge(): bool
{
return $this->getSize() === TestUtil::LARGE;
}




public function getActualOutput(): string
{
if (!$this->outputBufferingActive) {
return $this->output;
}

return (string) ob_get_contents();
}




public function hasOutput(): bool
{
if ($this->output === '') {
return false;
}

if ($this->hasExpectationOnOutput()) {
return false;
}

return true;
}




public function doesNotPerformAssertions(): bool
{
return $this->doesNotPerformAssertions;
}




public function hasExpectationOnOutput(): bool
{
return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion;
}




public function getExpectedException(): ?string
{
return $this->expectedException;
}






public function getExpectedExceptionCode()
{
return $this->expectedExceptionCode;
}




public function getExpectedExceptionMessage(): ?string
{
return $this->expectedExceptionMessage;
}




public function getExpectedExceptionMessageRegExp(): ?string
{
return $this->expectedExceptionMessageRegExp;
}




public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
{
$this->registerMockObjectsFromTestArgumentsRecursively = $flag;
}






public function runBare(): void
{
$this->numAssertions = 0;

$this->snapshotGlobalState();
$this->startOutputBuffering();
clearstatcache();
$currentWorkingDirectory = getcwd();

$hookMethods = TestUtil::getHookMethods(static::class);

$hasMetRequirements = false;

try {
$this->checkRequirements();
$hasMetRequirements = true;

if ($this->inIsolation) {
foreach ($hookMethods['beforeClass'] as $method) {
$this->{$method}();
}
}

$this->setDoesNotPerformAssertionsFromAnnotation();

foreach ($hookMethods['before'] as $method) {
$this->{$method}();
}

foreach ($hookMethods['preCondition'] as $method) {
$this->{$method}();
}

$this->testResult = $this->runTest();
$this->verifyMockObjects();

foreach ($hookMethods['postCondition'] as $method) {
$this->{$method}();
}

if (!empty($this->warnings)) {
throw new Warning(
implode(
"\n",
array_unique($this->warnings),
),
);
}

$this->status = BaseTestRunner::STATUS_PASSED;
} catch (IncompleteTest $e) {
$this->status = BaseTestRunner::STATUS_INCOMPLETE;
$this->statusMessage = $e->getMessage();
} catch (SkippedTest $e) {
$this->status = BaseTestRunner::STATUS_SKIPPED;
$this->statusMessage = $e->getMessage();
} catch (Warning $e) {
$this->status = BaseTestRunner::STATUS_WARNING;
$this->statusMessage = $e->getMessage();
} catch (AssertionFailedError $e) {
$this->status = BaseTestRunner::STATUS_FAILURE;
$this->statusMessage = $e->getMessage();
} catch (PredictionException $e) {
$this->status = BaseTestRunner::STATUS_FAILURE;
$this->statusMessage = $e->getMessage();
} catch (Throwable $_e) {
$e = $_e;
$this->status = BaseTestRunner::STATUS_ERROR;
$this->statusMessage = $_e->getMessage();
}

$this->mockObjects = [];
$this->prophet = null;



try {
if ($hasMetRequirements) {
foreach ($hookMethods['after'] as $method) {
$this->{$method}();
}

if ($this->inIsolation) {
foreach ($hookMethods['afterClass'] as $method) {
$this->{$method}();
}
}
}
} catch (Throwable $_e) {
$e = $e ?? $_e;
}

try {
$this->stopOutputBuffering();
} catch (RiskyTestError $_e) {
$e = $e ?? $_e;
}

if (isset($_e)) {
$this->status = BaseTestRunner::STATUS_ERROR;
$this->statusMessage = $_e->getMessage();
}

clearstatcache();

if ($currentWorkingDirectory !== getcwd()) {
chdir($currentWorkingDirectory);
}

$this->restoreGlobalState();
$this->unregisterCustomComparators();
$this->cleanupIniSettings();
$this->cleanupLocaleSettings();
libxml_clear_errors();


if (!isset($e)) {
try {
if ($this->outputExpectedRegex !== null) {
$this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output);
} elseif ($this->outputExpectedString !== null) {
$this->assertEquals($this->outputExpectedString, $this->output);
}
} catch (Throwable $_e) {
$e = $_e;
}
}


if (isset($e)) {
if ($e instanceof PredictionException) {
$e = new AssertionFailedError($e->getMessage());
}

$this->onNotSuccessfulTest($e);
}
}




public function setName(string $name): void
{
$this->name = $name;

if (is_callable($this->sortId(), true)) {
$this->providedTests = [new ExecutionOrderDependency($this->sortId())];
}
}






public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;
}




public function setDependencyInput(array $dependencyInput): void
{
$this->dependencyInput = $dependencyInput;
}




public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState): void
{
$this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
}




public function setBackupGlobals(?bool $backupGlobals): void
{
if ($this->backupGlobals === null && $backupGlobals !== null) {
$this->backupGlobals = $backupGlobals;
}
}




public function setBackupStaticAttributes(?bool $backupStaticAttributes): void
{
if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) {
$this->backupStaticAttributes = $backupStaticAttributes;
}
}




public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void
{
if ($this->runTestInSeparateProcess === null) {
$this->runTestInSeparateProcess = $runTestInSeparateProcess;
}
}




public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void
{
if ($this->runClassInSeparateProcess === null) {
$this->runClassInSeparateProcess = $runClassInSeparateProcess;
}
}




public function setPreserveGlobalState(bool $preserveGlobalState): void
{
$this->preserveGlobalState = $preserveGlobalState;
}




public function setInIsolation(bool $inIsolation): void
{
$this->inIsolation = $inIsolation;
}




public function isInIsolation(): bool
{
return $this->inIsolation;
}




public function getResult()
{
return $this->testResult;
}




public function setResult($result): void
{
$this->testResult = $result;
}




public function setOutputCallback(callable $callback): void
{
$this->outputCallback = $callback;
}




public function getTestResultObject(): ?TestResult
{
return $this->result;
}




public function setTestResultObject(TestResult $result): void
{
$this->result = $result;
}




public function registerMockObject(MockObject $mockObject): void
{
$this->mockObjects[] = $mockObject;
}




public function addToAssertionCount(int $count): void
{
$this->numAssertions += $count;
}






public function getNumAssertions(): int
{
return $this->numAssertions;
}




public function usesDataProvider(): bool
{
return !empty($this->data);
}






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




public function getDataSetAsString(bool $includeData = true): string
{
$buffer = '';

if (!empty($this->data)) {
if (is_int($this->dataName)) {
$buffer .= sprintf(' with data set #%d', $this->dataName);
} else {
$buffer .= sprintf(' with data set "%s"', $this->dataName);
}

if ($includeData) {
$exporter = new Exporter;

$buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data));
}
}

return $buffer;
}






public function getProvidedData(): array
{
return $this->data;
}




public function addWarning(string $warning): void
{
$this->warnings[] = $warning;
}

public function sortId(): string
{
$id = $this->name;

if (strpos($id, '::') === false) {
$id = static::class . '::' . $id;
}

if ($this->usesDataProvider()) {
$id .= $this->getDataSetAsString(false);
}

return $id;
}






public function provides(): array
{
return $this->providedTests;
}










public function requires(): array
{
return $this->dependencies;
}










protected function runTest()
{
if (trim($this->name) === '') {
throw new Exception(
'PHPUnit\Framework\TestCase::$name must be a non-blank string.',
);
}

$testArguments = array_merge($this->data, $this->dependencyInput);

$this->registerMockObjectsFromTestArguments($testArguments);

try {
$testResult = $this->{$this->name}(...array_values($testArguments));
} catch (Throwable $exception) {
if (!$this->checkExceptionExpectations($exception)) {
throw $exception;
}

if ($this->expectedException !== null) {
if ($this->expectedException === Error::class) {
$this->assertThat(
$exception,
LogicalOr::fromConstraints(
new ExceptionConstraint(Error::class),
new ExceptionConstraint(\Error::class),
),
);
} else {
$this->assertThat(
$exception,
new ExceptionConstraint(
$this->expectedException,
),
);
}
}

if ($this->expectedExceptionMessage !== null) {
$this->assertThat(
$exception,
new ExceptionMessage(
$this->expectedExceptionMessage,
),
);
}

if ($this->expectedExceptionMessageRegExp !== null) {
$this->assertThat(
$exception,
new ExceptionMessageRegularExpression(
$this->expectedExceptionMessageRegExp,
),
);
}

if ($this->expectedExceptionCode !== null) {
$this->assertThat(
$exception,
new ExceptionCode(
$this->expectedExceptionCode,
),
);
}

return;
}

if ($this->expectedException !== null) {
$this->assertThat(
null,
new ExceptionConstraint(
$this->expectedException,
),
);
} elseif ($this->expectedExceptionMessage !== null) {
$this->numAssertions++;

throw new AssertionFailedError(
sprintf(
'Failed asserting that exception with message "%s" is thrown',
$this->expectedExceptionMessage,
),
);
} elseif ($this->expectedExceptionMessageRegExp !== null) {
$this->numAssertions++;

throw new AssertionFailedError(
sprintf(
'Failed asserting that exception with message matching "%s" is thrown',
$this->expectedExceptionMessageRegExp,
),
);
} elseif ($this->expectedExceptionCode !== null) {
$this->numAssertions++;

throw new AssertionFailedError(
sprintf(
'Failed asserting that exception with code "%s" is thrown',
$this->expectedExceptionCode,
),
);
}

return $testResult;
}








protected function iniSet(string $varName, string $newValue): void
{
$currentValue = ini_set($varName, $newValue);

if ($currentValue !== false) {
$this->iniSettings[$varName] = $currentValue;
} else {
throw new Exception(
sprintf(
'INI setting "%s" could not be set to "%s".',
$varName,
$newValue,
),
);
}
}







protected function setLocale(...$args): void
{
if (count($args) < 2) {
throw new Exception;
}

[$category, $locale] = $args;

if (!in_array($category, self::LOCALE_CATEGORIES, true)) {
throw new Exception;
}

if (!is_array($locale) && !is_string($locale)) {
throw new Exception;
}

$this->locale[$category] = setlocale($category, 0);

$result = setlocale(...$args);

if ($result === false) {
throw new Exception(
'The locale functionality is not implemented on your platform, ' .
'the specified locale does not exist or the category name is ' .
'invalid.',
);
}
}










protected function createStub(string $originalClassName): Stub
{
return $this->createMockObject($originalClassName);
}










protected function createMock(string $originalClassName): MockObject
{
return $this->createMockObject($originalClassName);
}










protected function createConfiguredMock(string $originalClassName, array $configuration): MockObject
{
$o = $this->createMockObject($originalClassName);

foreach ($configuration as $method => $return) {
$o->method($method)->willReturn($return);
}

return $o;
}












protected function createPartialMock(string $originalClassName, array $methods): MockObject
{
try {
$reflector = new ReflectionClass($originalClassName);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$mockedMethodsThatDontExist = array_filter(
$methods,
static function (string $method) use ($reflector)
{
return !$reflector->hasMethod($method);
},
);

if ($mockedMethodsThatDontExist) {
$this->addWarning(
sprintf(
'createPartialMock() called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.',
implode(', ', $mockedMethodsThatDontExist),
$originalClassName,
),
);
}

return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->setMethods(empty($methods) ? null : $methods)
->getMock();
}










protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject
{
return $this->getMockBuilder($originalClassName)
->setConstructorArgs($constructorArguments)
->enableProxyingToOriginalMethods()
->getMock();
}














protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = false, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = false): string
{
$this->addWarning('PHPUnit\Framework\TestCase::getMockClass() is deprecated and will be removed in PHPUnit 10.');

$this->recordDoubledType($originalClassName);

$mock = $this->getMockObjectGenerator()->getMock(
$originalClassName,
$methods,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$cloneArguments,
);

return get_class($mock);
}












protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject
{
$this->recordDoubledType($originalClassName);

$mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass(
$originalClassName,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$mockedMethods,
$cloneArguments,
);

$this->registerMockObject($mockObject);

return $mockObject;
}










protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = true, array $options = []): MockObject
{
$this->recordDoubledType(SoapClient::class);

if ($originalClassName === '') {
$fileName = pathinfo(basename(parse_url($wsdlFile, PHP_URL_PATH)), PATHINFO_FILENAME);
$originalClassName = preg_replace('/\W/', '', $fileName);
}

if (!class_exists($originalClassName)) {
eval(
$this->getMockObjectGenerator()->generateClassFromWsdl(
$wsdlFile,
$originalClassName,
$methods,
$options,
)
);
}

$mockObject = $this->getMockObjectGenerator()->getMock(
$originalClassName,
$methods,
['', $options],
$mockClassName,
$callOriginalConstructor,
false,
false,
);

$this->registerMockObject($mockObject);

return $mockObject;
}








protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject
{
$this->recordDoubledType($traitName);

$mockObject = $this->getMockObjectGenerator()->getMockForTrait(
$traitName,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$mockedMethods,
$cloneArguments,
);

$this->registerMockObject($mockObject);

return $mockObject;
}






protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true): object
{
$this->recordDoubledType($traitName);

return $this->getMockObjectGenerator()->getObjectForTrait(
$traitName,
$traitClassName,
$callAutoload,
$callOriginalConstructor,
$arguments,
);
}










protected function prophesize(?string $classOrInterface = null): ObjectProphecy
{
if (!class_exists(Prophet::class)) {
throw new Exception('This test uses TestCase::prophesize(), but phpspec/prophecy is not installed. Please run "composer require --dev phpspec/prophecy".');
}

$this->addWarning('PHPUnit\Framework\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.');

if (is_string($classOrInterface)) {
$this->recordDoubledType($classOrInterface);
}

return $this->getProphet()->prophesize($classOrInterface);
}






protected function createResult(): TestResult
{
return new TestResult;
}






protected function onNotSuccessfulTest(Throwable $t): void
{
throw $t;
}

protected function recordDoubledType(string $originalClassName): void
{
$this->doubledTypes[] = $originalClassName;
}




private function verifyMockObjects(): void
{
foreach ($this->mockObjects as $mockObject) {
if ($mockObject->__phpunit_hasMatchers()) {
$this->numAssertions++;
}

$mockObject->__phpunit_verify(
$this->shouldInvocationMockerBeReset($mockObject),
);
}

if ($this->prophet !== null) {
try {
$this->prophet->checkPredictions();
} finally {
foreach ($this->prophet->getProphecies() as $objectProphecy) {
foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) {
foreach ($methodProphecies as $methodProphecy) {

$this->numAssertions += count($methodProphecy->getCheckedPredictions());
}
}
}
}
}
}






private function checkRequirements(): void
{
if (!$this->name || !method_exists($this, $this->name)) {
return;
}

$missingRequirements = TestUtil::getMissingRequirements(
static::class,
$this->name,
);

if (!empty($missingRequirements)) {
$this->markTestSkipped(implode(PHP_EOL, $missingRequirements));
}
}

private function handleDependencies(): bool
{
if ([] === $this->dependencies || $this->inIsolation) {
return true;
}

$passed = $this->result->passed();
$passedKeys = array_keys($passed);
$numKeys = count($passedKeys);

for ($i = 0; $i < $numKeys; $i++) {
$pos = strpos($passedKeys[$i], ' with data set');

if ($pos !== false) {
$passedKeys[$i] = substr($passedKeys[$i], 0, $pos);
}
}

$passedKeys = array_flip(array_unique($passedKeys));

foreach ($this->dependencies as $dependency) {
if (!$dependency->isValid()) {
$this->markSkippedForNotSpecifyingDependency();

return false;
}

if ($dependency->targetIsClass()) {
$dependencyClassName = $dependency->getTargetClassName();

if (array_search($dependencyClassName, $this->result->passedClasses(), true) === false) {
$this->markSkippedForMissingDependency($dependency);

return false;
}

continue;
}

$dependencyTarget = $dependency->getTarget();

if (!isset($passedKeys[$dependencyTarget])) {
if (!$this->isCallableTestMethod($dependencyTarget)) {
$this->markWarningForUncallableDependency($dependency);
} else {
$this->markSkippedForMissingDependency($dependency);
}

return false;
}

if (isset($passed[$dependencyTarget])) {
if ($passed[$dependencyTarget]['size'] != TestUtil::UNKNOWN &&
$this->getSize() != TestUtil::UNKNOWN &&
$passed[$dependencyTarget]['size'] > $this->getSize()) {
$this->result->addError(
$this,
new SkippedTestError(
'This test depends on a test that is larger than itself.',
),
0,
);

return false;
}

if ($dependency->useDeepClone()) {
$deepCopy = new DeepCopy;
$deepCopy->skipUncloneable(false);

$this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']);
} elseif ($dependency->useShallowClone()) {
$this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result'];
} else {
$this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result'];
}
} else {
$this->dependencyInput[$dependencyTarget] = null;
}
}

return true;
}

private function markSkippedForNotSpecifyingDependency(): void
{
$this->status = BaseTestRunner::STATUS_SKIPPED;

$this->result->startTest($this);

$this->result->addError(
$this,
new SkippedTestError(
'This method has an invalid @depends annotation.',
),
0,
);

$this->result->endTest($this, 0);
}

private function markSkippedForMissingDependency(ExecutionOrderDependency $dependency): void
{
$this->status = BaseTestRunner::STATUS_SKIPPED;

$this->result->startTest($this);

$this->result->addError(
$this,
new SkippedTestError(
sprintf(
'This test depends on "%s" to pass.',
$dependency->getTarget(),
),
),
0,
);

$this->result->endTest($this, 0);
}

private function markWarningForUncallableDependency(ExecutionOrderDependency $dependency): void
{
$this->status = BaseTestRunner::STATUS_WARNING;

$this->result->startTest($this);

$this->result->addWarning(
$this,
new Warning(
sprintf(
'This test depends on "%s" which does not exist.',
$dependency->getTarget(),
),
),
0,
);

$this->result->endTest($this, 0);
}




private function getMockObjectGenerator(): MockGenerator
{
if ($this->mockObjectGenerator === null) {
$this->mockObjectGenerator = new MockGenerator;
}

return $this->mockObjectGenerator;
}

private function startOutputBuffering(): void
{
ob_start();

$this->outputBufferingActive = true;
$this->outputBufferingLevel = ob_get_level();
}




private function stopOutputBuffering(): void
{
if (ob_get_level() !== $this->outputBufferingLevel) {
while (ob_get_level() >= $this->outputBufferingLevel) {
ob_end_clean();
}

throw new RiskyTestError(
'Test code or tested code did not (only) close its own output buffers',
);
}

$this->output = ob_get_contents();

if ($this->outputCallback !== false) {
$this->output = (string) call_user_func($this->outputCallback, $this->output);
}

ob_end_clean();

$this->outputBufferingActive = false;
$this->outputBufferingLevel = ob_get_level();
}

private function snapshotGlobalState(): void
{
if ($this->runTestInSeparateProcess || $this->inIsolation ||
(!$this->backupGlobals && !$this->backupStaticAttributes)) {
return;
}

$this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === true);
}





private function restoreGlobalState(): void
{
if (!$this->snapshot instanceof Snapshot) {
return;
}

if ($this->beStrictAboutChangesToGlobalState) {
try {
$this->compareGlobalStateSnapshots(
$this->snapshot,
$this->createGlobalStateSnapshot($this->backupGlobals === true),
);
} catch (RiskyTestError $rte) {

}
}

$restorer = new Restorer;

if ($this->backupGlobals) {
$restorer->restoreGlobalVariables($this->snapshot);
}

if ($this->backupStaticAttributes) {
$restorer->restoreStaticAttributes($this->snapshot);
}

$this->snapshot = null;

if (isset($rte)) {
throw $rte;
}
}

private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot
{
$excludeList = new ExcludeList;

foreach ($this->backupGlobalsExcludeList as $globalVariable) {
$excludeList->addGlobalVariable($globalVariable);
}

if (!empty($this->backupGlobalsBlacklist)) {
$this->addWarning('PHPUnit\Framework\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\Framework\TestCase::$backupGlobalsExcludeList instead.');

foreach ($this->backupGlobalsBlacklist as $globalVariable) {
$excludeList->addGlobalVariable($globalVariable);
}
}

if (!defined('PHPUNIT_TESTSUITE')) {
$excludeList->addClassNamePrefix('PHPUnit');
$excludeList->addClassNamePrefix('SebastianBergmann\CodeCoverage');
$excludeList->addClassNamePrefix('SebastianBergmann\FileIterator');
$excludeList->addClassNamePrefix('SebastianBergmann\Invoker');
$excludeList->addClassNamePrefix('SebastianBergmann\Template');
$excludeList->addClassNamePrefix('SebastianBergmann\Timer');
$excludeList->addClassNamePrefix('Doctrine\Instantiator');
$excludeList->addClassNamePrefix('Prophecy');
$excludeList->addStaticAttribute(ComparatorFactory::class, 'instance');

foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) {
foreach ($attributes as $attribute) {
$excludeList->addStaticAttribute($class, $attribute);
}
}

if (!empty($this->backupStaticAttributesBlacklist)) {
$this->addWarning('PHPUnit\Framework\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\Framework\TestCase::$backupStaticAttributesExcludeList instead.');

foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) {
foreach ($attributes as $attribute) {
$excludeList->addStaticAttribute($class, $attribute);
}
}
}
}

return new Snapshot(
$excludeList,
$backupGlobals,
(bool) $this->backupStaticAttributes,
false,
false,
false,
false,
false,
false,
false,
);
}





private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void
{
$backupGlobals = $this->backupGlobals === null || $this->backupGlobals;

if ($backupGlobals) {
$this->compareGlobalStateSnapshotPart(
$before->globalVariables(),
$after->globalVariables(),
"--- Global variables before the test\n+++ Global variables after the test\n",
);

$this->compareGlobalStateSnapshotPart(
$before->superGlobalVariables(),
$after->superGlobalVariables(),
"--- Super-global variables before the test\n+++ Super-global variables after the test\n",
);
}

if ($this->backupStaticAttributes) {
$this->compareGlobalStateSnapshotPart(
$before->staticAttributes(),
$after->staticAttributes(),
"--- Static attributes before the test\n+++ Static attributes after the test\n",
);
}
}




private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void
{
if ($before != $after) {
$differ = new Differ($header);
$exporter = new Exporter;

$diff = $differ->diff(
$exporter->export($before),
$exporter->export($after),
);

throw new RiskyTestError(
$diff,
);
}
}

private function getProphet(): Prophet
{
if ($this->prophet === null) {
$this->prophet = new Prophet;
}

return $this->prophet;
}




private function shouldInvocationMockerBeReset(MockObject $mock): bool
{
$enumerator = new Enumerator;

foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
if ($mock === $object) {
return false;
}
}

if (!is_array($this->testResult) && !is_object($this->testResult)) {
return true;
}

return !in_array($mock, $enumerator->enumerate($this->testResult), true);
}






private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void
{
if ($this->registerMockObjectsFromTestArgumentsRecursively) {
foreach ((new Enumerator)->enumerate($testArguments) as $object) {
if ($object instanceof MockObject) {
$this->registerMockObject($object);
}
}
} else {
foreach ($testArguments as $testArgument) {
if ($testArgument instanceof MockObject) {
$testArgument = Cloner::clone($testArgument);

$this->registerMockObject($testArgument);
} elseif (is_array($testArgument) && !in_array($testArgument, $visited, true)) {
$visited[] = $testArgument;

$this->registerMockObjectsFromTestArguments(
$testArgument,
$visited,
);
}
}
}
}

private function setDoesNotPerformAssertionsFromAnnotation(): void
{
$annotations = TestUtil::parseTestMethodAnnotations(
static::class,
$this->name,
);

if (isset($annotations['method']['doesNotPerformAssertions'])) {
$this->doesNotPerformAssertions = true;
}
}

private function unregisterCustomComparators(): void
{
$factory = ComparatorFactory::getInstance();

foreach ($this->customComparators as $comparator) {
$factory->unregister($comparator);
}

$this->customComparators = [];
}

private function cleanupIniSettings(): void
{
foreach ($this->iniSettings as $varName => $oldValue) {
ini_set($varName, $oldValue);
}

$this->iniSettings = [];
}

private function cleanupLocaleSettings(): void
{
foreach ($this->locale as $category => $locale) {
setlocale($category, $locale);
}

$this->locale = [];
}




private function checkExceptionExpectations(Throwable $throwable): bool
{
$result = false;

if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) {
$result = true;
}

if ($throwable instanceof Exception) {
$result = false;
}

if (is_string($this->expectedException)) {
try {
$reflector = new ReflectionClass($this->expectedException);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($this->expectedException === 'PHPUnit\Framework\Exception' ||
$this->expectedException === '\PHPUnit\Framework\Exception' ||
$reflector->isSubclassOf(Exception::class)) {
$result = true;
}
}

return $result;
}

private function runInSeparateProcess(): bool
{
return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) &&
!$this->inIsolation && !$this instanceof PhptTestCase;
}

private function isCallableTestMethod(string $dependency): bool
{
[$className, $methodName] = explode('::', $dependency);

if (!class_exists($className)) {
return false;
}

try {
$class = new ReflectionClass($className);
} catch (ReflectionException $e) {
return false;
}

if (!$class->isSubclassOf(__CLASS__)) {
return false;
}

if (!$class->hasMethod($methodName)) {
return false;
}

try {
$method = $class->getMethod($methodName);
} catch (ReflectionException $e) {
return false;
}

return TestUtil::isTestMethod($method);
}








private function createMockObject(string $originalClassName): MockObject
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use PHPUnit\Framework\MockObject\Builder\InvocationMocker as InvocationMockerBuilder;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;




trait Api
{



private static $__phpunit_configurableMethods;




private $__phpunit_originalObject;




private $__phpunit_returnValueGeneration = true;




private $__phpunit_invocationMocker;


public static function __phpunit_initConfigurableMethods(ConfigurableMethod ...$configurableMethods): void
{
if (isset(static::$__phpunit_configurableMethods)) {
throw new ConfigurableMethodsAlreadyInitializedException(
'Configurable methods is already initialized and can not be reinitialized',
);
}

static::$__phpunit_configurableMethods = $configurableMethods;
}


public function __phpunit_setOriginalObject($originalObject): void
{
$this->__phpunit_originalObject = $originalObject;
}


public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void
{
$this->__phpunit_returnValueGeneration = $returnValueGeneration;
}


public function __phpunit_getInvocationHandler(): InvocationHandler
{
if ($this->__phpunit_invocationMocker === null) {
$this->__phpunit_invocationMocker = new InvocationHandler(
static::$__phpunit_configurableMethods,
$this->__phpunit_returnValueGeneration,
);
}

return $this->__phpunit_invocationMocker;
}


public function __phpunit_hasMatchers(): bool
{
return $this->__phpunit_getInvocationHandler()->hasMatchers();
}


public function __phpunit_verify(bool $unsetInvocationMocker = true): void
{
$this->__phpunit_getInvocationHandler()->verify();

if ($unsetInvocationMocker) {
$this->__phpunit_invocationMocker = null;
}
}

public function expects(InvocationOrder $matcher): InvocationMockerBuilder
{
return $this->__phpunit_getInvocationHandler()->expects($matcher);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function call_user_func_array;
use function func_get_args;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;




trait Method
{
public function method()
{
$expects = $this->expects(new AnyInvokedCount);

return call_user_func_array(
[$expects, 'method'],
func_get_args(),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class InvokedAtLeastOnce extends InvocationOrder
{
public function toString(): string
{
return 'invoked at least once';
}







public function verify(): void
{
$count = $this->getInvocationCount();

if ($count < 1) {
throw new ExpectationFailedException(
'Expected invocation at least once but it never occurred.',
);
}
}

public function matches(BaseInvocation $invocation): bool
{
return true;
}

protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class InvokedAtMostCount extends InvocationOrder
{



private $allowedInvocations;




public function __construct($allowedInvocations)
{
$this->allowedInvocations = $allowedInvocations;
}

public function toString(): string
{
return 'invoked at most ' . $this->allowedInvocations . ' times';
}







public function verify(): void
{
$count = $this->getInvocationCount();

if ($count > $this->allowedInvocations) {
throw new ExpectationFailedException(
'Expected invocation at most ' . $this->allowedInvocations .
' times but it occurred ' . $count . ' time(s).',
);
}
}

public function matches(BaseInvocation $invocation): bool
{
return true;
}

protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;








final class InvokedAtIndex extends InvocationOrder
{



private $sequenceIndex;




private $currentIndex = -1;




public function __construct($sequenceIndex)
{
$this->sequenceIndex = $sequenceIndex;
}

public function toString(): string
{
return 'invoked at sequence index ' . $this->sequenceIndex;
}

public function matches(BaseInvocation $invocation): bool
{
$this->currentIndex++;

return $this->currentIndex == $this->sequenceIndex;
}







public function verify(): void
{
if ($this->currentIndex < $this->sequenceIndex) {
throw new ExpectationFailedException(
sprintf(
'The expected invocation at index %s was never reached.',
$this->sequenceIndex,
),
);
}
}

protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use function is_string;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\InvalidArgumentException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\MethodNameConstraint;




final class MethodName
{



private $constraint;






public function __construct($constraint)
{
if (is_string($constraint)) {
$constraint = new MethodNameConstraint($constraint);
}

if (!$constraint instanceof Constraint) {
throw InvalidArgumentException::create(1, 'PHPUnit\Framework\Constraint\Constraint object or string');
}

$this->constraint = $constraint;
}

public function toString(): string
{
return 'method name ' . $this->constraint->toString();
}





public function matches(BaseInvocation $invocation): bool
{
return $this->matchesName($invocation->getMethodName());
}





public function matchesName(string $methodName): bool
{
return (bool) $this->constraint->evaluate($methodName, '', true);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use function count;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\Verifiable;
use PHPUnit\Framework\SelfDescribing;




abstract class InvocationOrder implements SelfDescribing, Verifiable
{



private $invocations = [];

public function getInvocationCount(): int
{
return count($this->invocations);
}

public function hasBeenInvoked(): bool
{
return count($this->invocations) > 0;
}

final public function invoked(BaseInvocation $invocation)
{
$this->invocations[] = $invocation;

return $this->invokedDo($invocation);
}

abstract public function matches(BaseInvocation $invocation): bool;

abstract protected function invokedDo(BaseInvocation $invocation);
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use function count;
use function get_class;
use function sprintf;
use Exception;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\IsAnything;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class Parameters implements ParametersRule
{



private $parameters = [];




private $invocation;




private $parameterVerificationResult;




public function __construct(array $parameters)
{
foreach ($parameters as $parameter) {
if (!($parameter instanceof Constraint)) {
$parameter = new IsEqual(
$parameter,
);
}

$this->parameters[] = $parameter;
}
}

public function toString(): string
{
$text = 'with parameter';

foreach ($this->parameters as $index => $parameter) {
if ($index > 0) {
$text .= ' and';
}

$text .= ' ' . $index . ' ' . $parameter->toString();
}

return $text;
}




public function apply(BaseInvocation $invocation): void
{
$this->invocation = $invocation;
$this->parameterVerificationResult = null;

try {
$this->parameterVerificationResult = $this->doVerify();
} catch (ExpectationFailedException $e) {
$this->parameterVerificationResult = $e;

throw $this->parameterVerificationResult;
}
}









public function verify(): void
{
$this->doVerify();
}





private function doVerify(): bool
{
if (isset($this->parameterVerificationResult)) {
return $this->guardAgainstDuplicateEvaluationOfParameterConstraints();
}

if ($this->invocation === null) {
throw new ExpectationFailedException('Mocked method does not exist.');
}

if (count($this->invocation->getParameters()) < count($this->parameters)) {
$message = 'Parameter count for invocation %s is too low.';





if (count($this->parameters) === 1 &&
get_class($this->parameters[0]) === IsAnything::class) {
$message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead.";
}

throw new ExpectationFailedException(
sprintf($message, $this->invocation->toString()),
);
}

foreach ($this->parameters as $i => $parameter) {
$parameter->evaluate(
$this->invocation->getParameters()[$i],
sprintf(
'Parameter %s for invocation %s does not match expected ' .
'value.',
$i,
$this->invocation->toString(),
),
);
}

return true;
}




private function guardAgainstDuplicateEvaluationOfParameterConstraints(): bool
{
if ($this->parameterVerificationResult instanceof ExpectationFailedException) {
throw $this->parameterVerificationResult;
}

return (bool) $this->parameterVerificationResult;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use function count;
use function gettype;
use function is_iterable;
use function sprintf;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\InvalidParameterGroupException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;






final class ConsecutiveParameters implements ParametersRule
{



private $parameterGroups = [];




private $invocations = [];




public function __construct(array $parameterGroups)
{
foreach ($parameterGroups as $index => $parameters) {
if (!is_iterable($parameters)) {
throw new InvalidParameterGroupException(
sprintf(
'Parameter group #%d must be an array or Traversable, got %s',
$index,
gettype($parameters),
),
);
}

foreach ($parameters as $parameter) {
if (!$parameter instanceof Constraint) {
$parameter = new IsEqual($parameter);
}

$this->parameterGroups[$index][] = $parameter;
}
}
}

public function toString(): string
{
return 'with consecutive parameters';
}





public function apply(BaseInvocation $invocation): void
{
$this->invocations[] = $invocation;
$callIndex = count($this->invocations) - 1;

$this->verifyInvocation($invocation, $callIndex);
}





public function verify(): void
{
foreach ($this->invocations as $callIndex => $invocation) {
$this->verifyInvocation($invocation, $callIndex);
}
}









private function verifyInvocation(BaseInvocation $invocation, $callIndex): void
{
if (!isset($this->parameterGroups[$callIndex])) {

return;
}

$parameters = $this->parameterGroups[$callIndex];

if (count($invocation->getParameters()) < count($parameters)) {
throw new ExpectationFailedException(
sprintf(
'Parameter count for invocation %s is too low.',
$invocation->toString(),
),
);
}

foreach ($parameters as $i => $parameter) {
$parameter->evaluate(
$invocation->getParameters()[$i],
sprintf(
'Parameter %s for invocation #%d %s does not match expected ' .
'value.',
$i,
$callIndex,
$invocation->toString(),
),
);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class InvokedCount extends InvocationOrder
{



private $expectedCount;




public function __construct($expectedCount)
{
$this->expectedCount = $expectedCount;
}

public function isNever(): bool
{
return $this->expectedCount === 0;
}

public function toString(): string
{
return 'invoked ' . $this->expectedCount . ' time(s)';
}

public function matches(BaseInvocation $invocation): bool
{
return true;
}







public function verify(): void
{
$count = $this->getInvocationCount();

if ($count !== $this->expectedCount) {
throw new ExpectationFailedException(
sprintf(
'Method was expected to be called %d times, ' .
'actually called %d times.',
$this->expectedCount,
$count,
),
);
}
}




protected function invokedDo(BaseInvocation $invocation): void
{
$count = $this->getInvocationCount();

if ($count > $this->expectedCount) {
$message = $invocation->toString() . ' ';

switch ($this->expectedCount) {
case 0:
$message .= 'was not expected to be called.';

break;

case 1:
$message .= 'was not expected to be called more than once.';

break;

default:
$message .= sprintf(
'was not expected to be called more than %d times.',
$this->expectedCount,
);
}

throw new ExpectationFailedException($message);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class AnyInvokedCount extends InvocationOrder
{
public function toString(): string
{
return 'invoked zero or more times';
}

public function verify(): void
{
}

public function matches(BaseInvocation $invocation): bool
{
return true;
}

protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class AnyParameters implements ParametersRule
{
public function toString(): string
{
return 'with any parameters';
}

public function apply(BaseInvocation $invocation): void
{
}

public function verify(): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;




final class InvokedAtLeastCount extends InvocationOrder
{



private $requiredInvocations;




public function __construct($requiredInvocations)
{
$this->requiredInvocations = $requiredInvocations;
}

public function toString(): string
{
return 'invoked at least ' . $this->requiredInvocations . ' times';
}







public function verify(): void
{
$count = $this->getInvocationCount();

if ($count < $this->requiredInvocations) {
throw new ExpectationFailedException(
'Expected invocation at least ' . $this->requiredInvocations .
' times but it occurred ' . $count . ' time(s).',
);
}
}

public function matches(BaseInvocation $invocation): bool
{
return true;
}

protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Rule;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\Verifiable;
use PHPUnit\Framework\SelfDescribing;




interface ParametersRule extends SelfDescribing, Verifiable
{



public function apply(BaseInvocation $invocation): void;

public function verify(): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use const DIRECTORY_SEPARATOR;
use function explode;
use function implode;
use function is_object;
use function is_string;
use function preg_match;
use function preg_replace;
use function sprintf;
use function strlen;
use function strpos;
use function substr;
use function substr_count;
use function trim;
use function var_export;
use ReflectionMethod;
use ReflectionParameter;
use SebastianBergmann\Template\Exception as TemplateException;
use SebastianBergmann\Template\Template;
use SebastianBergmann\Type\ReflectionMapper;
use SebastianBergmann\Type\Type;
use SebastianBergmann\Type\UnknownType;




final class MockMethod
{



private static $templates = [];




private $className;




private $methodName;




private $cloneArguments;




private $modifier;




private $argumentsForDeclaration;




private $argumentsForCall;




private $returnType;




private $reference;




private $callOriginalMethod;




private $static;




private $deprecation;





public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self
{
if ($method->isPrivate()) {
$modifier = 'private';
} elseif ($method->isProtected()) {
$modifier = 'protected';
} else {
$modifier = 'public';
}

if ($method->isStatic()) {
$modifier .= ' static';
}

if ($method->returnsReference()) {
$reference = '&';
} else {
$reference = '';
}

$docComment = $method->getDocComment();

if (is_string($docComment) &&
preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation)) {
$deprecation = trim(preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
} else {
$deprecation = null;
}

return new self(
$method->getDeclaringClass()->getName(),
$method->getName(),
$cloneArguments,
$modifier,
self::getMethodParametersForDeclaration($method),
self::getMethodParametersForCall($method),
(new ReflectionMapper)->fromReturnType($method),
$reference,
$callOriginalMethod,
$method->isStatic(),
$deprecation,
);
}

public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self
{
return new self(
$fullClassName,
$methodName,
$cloneArguments,
'public',
'',
'',
new UnknownType,
'',
false,
false,
null,
);
}

public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation)
{
$this->className = $className;
$this->methodName = $methodName;
$this->cloneArguments = $cloneArguments;
$this->modifier = $modifier;
$this->argumentsForDeclaration = $argumentsForDeclaration;
$this->argumentsForCall = $argumentsForCall;
$this->returnType = $returnType;
$this->reference = $reference;
$this->callOriginalMethod = $callOriginalMethod;
$this->static = $static;
$this->deprecation = $deprecation;
}

public function getName(): string
{
return $this->methodName;
}




public function generateCode(): string
{
if ($this->static) {
$templateFile = 'mocked_static_method.tpl';
} elseif ($this->returnType->isNever() || $this->returnType->isVoid()) {
$templateFile = sprintf(
'%s_method_never_or_void.tpl',
$this->callOriginalMethod ? 'proxied' : 'mocked',
);
} else {
$templateFile = sprintf(
'%s_method.tpl',
$this->callOriginalMethod ? 'proxied' : 'mocked',
);
}

$deprecation = $this->deprecation;

if (null !== $this->deprecation) {
$deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation}).";
$deprecationTemplate = $this->getTemplate('deprecation.tpl');

$deprecationTemplate->setVar(
[
'deprecation' => var_export($deprecation, true),
],
);

$deprecation = $deprecationTemplate->render();
}

$template = $this->getTemplate($templateFile);

$template->setVar(
[
'arguments_decl' => $this->argumentsForDeclaration,
'arguments_call' => $this->argumentsForCall,
'return_declaration' => !empty($this->returnType->asString()) ? (': ' . $this->returnType->asString()) : '',
'return_type' => $this->returnType->asString(),
'arguments_count' => !empty($this->argumentsForCall) ? substr_count($this->argumentsForCall, ',') + 1 : 0,
'class_name' => $this->className,
'method_name' => $this->methodName,
'modifier' => $this->modifier,
'reference' => $this->reference,
'clone_arguments' => $this->cloneArguments ? 'true' : 'false',
'deprecation' => $deprecation,
],
);

return $template->render();
}

public function getReturnType(): Type
{
return $this->returnType;
}




private function getTemplate(string $template): Template
{
$filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template;

if (!isset(self::$templates[$filename])) {
try {
self::$templates[$filename] = new Template($filename);
} catch (TemplateException $e) {
throw new RuntimeException(
$e->getMessage(),
$e->getCode(),
$e,
);
}
}

return self::$templates[$filename];
}






private static function getMethodParametersForDeclaration(ReflectionMethod $method): string
{
$parameters = [];
$types = (new ReflectionMapper)->fromParameterTypes($method);

foreach ($method->getParameters() as $i => $parameter) {
$name = '$' . $parameter->getName();




if ($name === '$' || $name === '$...') {
$name = '$arg' . $i;
}

$default = '';
$reference = '';
$typeDeclaration = '';

if (!$types[$i]->type()->isUnknown()) {
$typeDeclaration = $types[$i]->type()->asString() . ' ';
}

if ($parameter->isPassedByReference()) {
$reference = '&';
}

if ($parameter->isVariadic()) {
$name = '...' . $name;
} elseif ($parameter->isDefaultValueAvailable()) {
$default = ' = ' . self::exportDefaultValue($parameter);
} elseif ($parameter->isOptional()) {
$default = ' = null';
}

$parameters[] = $typeDeclaration . $reference . $name . $default;
}

return implode(', ', $parameters);
}






private static function getMethodParametersForCall(ReflectionMethod $method): string
{
$parameters = [];

foreach ($method->getParameters() as $i => $parameter) {
$name = '$' . $parameter->getName();




if ($name === '$' || $name === '$...') {
$name = '$arg' . $i;
}

if ($parameter->isVariadic()) {
continue;
}

if ($parameter->isPassedByReference()) {
$parameters[] = '&' . $name;
} else {
$parameters[] = $name;
}
}

return implode(', ', $parameters);
}




private static function exportDefaultValue(ReflectionParameter $parameter): string
{
try {
$defaultValue = $parameter->getDefaultValue();

if (!is_object($defaultValue)) {
return (string) var_export($defaultValue, true);
}

$parameterAsString = $parameter->__toString();

return (string) explode(
' = ',
substr(
substr(
$parameterAsString,
strpos($parameterAsString, '<optional> ') + strlen('<optional> '),
),
0,
-2,
),
)[1];

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
use const PHP_MAJOR_VERSION;
use const PREG_OFFSET_CAPTURE;
use const WSDL_CACHE_NONE;
use function array_merge;
use function array_pop;
use function array_unique;
use function class_exists;
use function count;
use function explode;
use function extension_loaded;
use function implode;
use function in_array;
use function interface_exists;
use function is_array;
use function is_object;
use function md5;
use function method_exists;
use function mt_rand;
use function preg_match;
use function preg_match_all;
use function range;
use function serialize;
use function sort;
use function sprintf;
use function str_replace;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
use function trait_exists;
use Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException;
use Doctrine\Instantiator\Instantiator;
use Exception;
use Iterator;
use IteratorAggregate;
use PHPUnit\Framework\InvalidArgumentException;
use ReflectionClass;
use ReflectionMethod;
use SebastianBergmann\Template\Exception as TemplateException;
use SebastianBergmann\Template\Template;
use SoapClient;
use SoapFault;
use Throwable;
use Traversable;




final class Generator
{
private const MOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT'
namespace PHPUnit\Framework\MockObject;

trait MockedCloneMethodWithVoidReturnType
{
    public function __clone(): void
    {
        $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler();
    }
}
EOT;
private const MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT'
namespace PHPUnit\Framework\MockObject;

trait MockedCloneMethodWithoutReturnType
{
    public function __clone()
    {
        $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler();
    }
}
EOT;
private const UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT'
namespace PHPUnit\Framework\MockObject;

trait UnmockedCloneMethodWithVoidReturnType
{
    public function __clone(): void
    {
        $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler();

        parent::__clone();
    }
}
EOT;
private const UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT'
namespace PHPUnit\Framework\MockObject;

trait UnmockedCloneMethodWithoutReturnType
{
    public function __clone()
    {
        $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler();

        parent::__clone();
    }
}
EOT;




private const EXCLUDED_METHOD_NAMES = [
'__CLASS__' => true,
'__DIR__' => true,
'__FILE__' => true,
'__FUNCTION__' => true,
'__LINE__' => true,
'__METHOD__' => true,
'__NAMESPACE__' => true,
'__TRAIT__' => true,
'__clone' => true,
'__halt_compiler' => true,
];




private static $cache = [];




private static $templates = [];

















public function getMock(string $type, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false, object $proxyTarget = null, bool $allowMockingUnknownTypes = true, bool $returnValueGeneration = true): MockObject
{
if (!is_array($methods) && null !== $methods) {
throw InvalidArgumentException::create(2, 'array');
}

if ($type === 'Traversable' || $type === '\\Traversable') {
$type = 'Iterator';
}

if (!$allowMockingUnknownTypes && !class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) {
throw new UnknownTypeException($type);
}

if (null !== $methods) {
foreach ($methods as $method) {
if (!preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', (string) $method)) {
throw new InvalidMethodNameException((string) $method);
}
}

if ($methods !== array_unique($methods)) {
throw new DuplicateMethodException($methods);
}
}

if ($mockClassName !== '' && class_exists($mockClassName, false)) {
try {
$reflector = new ReflectionClass($mockClassName);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if (!$reflector->implementsInterface(MockObject::class)) {
throw new ClassAlreadyExistsException($mockClassName);
}
}

if (!$callOriginalConstructor && $callOriginalMethods) {
throw new OriginalConstructorInvocationRequiredException;
}

$mock = $this->generate(
$type,
$methods,
$mockClassName,
$callOriginalClone,
$callAutoload,
$cloneArguments,
$callOriginalMethods,
);

return $this->getObject(
$mock,
$type,
$callOriginalConstructor,
$callAutoload,
$arguments,
$callOriginalMethods,
$proxyTarget,
$returnValueGeneration,
);
}







public function getMockForInterfaces(array $interfaces, bool $callAutoload = true): MockObject
{
if (count($interfaces) < 2) {
throw new RuntimeException('At least two interfaces must be specified');
}

foreach ($interfaces as $interface) {
if (!interface_exists($interface, $callAutoload)) {
throw new UnknownTypeException($interface);
}
}

sort($interfaces);

$methods = [];

foreach ($interfaces as $interface) {
$methods = array_merge($methods, $this->getClassMethods($interface));
}

if (count(array_unique($methods)) < count($methods)) {
throw new RuntimeException('Interfaces must not declare the same method');
}

$unqualifiedNames = [];

foreach ($interfaces as $interface) {
$parts = explode('\\', $interface);
$unqualifiedNames[] = array_pop($parts);
}

sort($unqualifiedNames);

do {
$intersectionName = sprintf(
'Intersection_%s_%s',
implode('_', $unqualifiedNames),
substr(md5((string) mt_rand()), 0, 8),
);
} while (interface_exists($intersectionName, false));

$template = $this->getTemplate('intersection.tpl');

$template->setVar(
[
'intersection' => $intersectionName,
'interfaces' => implode(', ', $interfaces),
],
);

eval($template->render());

return $this->getMock($intersectionName);
}

























public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = null, bool $cloneArguments = true): MockObject
{
if (class_exists($originalClassName, $callAutoload) ||
interface_exists($originalClassName, $callAutoload)) {
try {
$reflector = new ReflectionClass($originalClassName);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$methods = $mockedMethods;

foreach ($reflector->getMethods() as $method) {
if ($method->isAbstract() && !in_array($method->getName(), $methods ?? [], true)) {
$methods[] = $method->getName();
}
}

if (empty($methods)) {
$methods = null;
}

return $this->getMock(
$originalClassName,
$methods,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$cloneArguments,
);
}

throw new UnknownClassException($originalClassName);
}





















public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = null, bool $cloneArguments = true): MockObject
{
if (!trait_exists($traitName, $callAutoload)) {
throw new UnknownTraitException($traitName);
}

$className = $this->generateClassName(
$traitName,
'',
'Trait_',
);

$classTemplate = $this->getTemplate('trait_class.tpl');

$classTemplate->setVar(
[
'prologue' => 'abstract ',
'class_name' => $className['className'],
'trait_name' => $traitName,
],
);

$mockTrait = new MockTrait($classTemplate->render(), $className['className']);
$mockTrait->generate();

return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
}










public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = true, bool $callOriginalConstructor = false, array $arguments = []): object
{
if (!trait_exists($traitName, $callAutoload)) {
throw new UnknownTraitException($traitName);
}

$className = $this->generateClassName(
$traitName,
$traitClassName,
'Trait_',
);

$classTemplate = $this->getTemplate('trait_class.tpl');

$classTemplate->setVar(
[
'prologue' => '',
'class_name' => $className['className'],
'trait_name' => $traitName,
],
);

return $this->getObject(
new MockTrait(
$classTemplate->render(),
$className['className'],
),
'',
$callOriginalConstructor,
$callAutoload,
$arguments,
);
}







public function generate(string $type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): MockClass
{
if ($mockClassName !== '') {
return $this->generateMock(
$type,
$methods,
$mockClassName,
$callOriginalClone,
$callAutoload,
$cloneArguments,
$callOriginalMethods,
);
}

$key = md5(
$type .
serialize($methods) .
serialize($callOriginalClone) .
serialize($cloneArguments) .
serialize($callOriginalMethods),
);

if (!isset(self::$cache[$key])) {
self::$cache[$key] = $this->generateMock(
$type,
$methods,
$mockClassName,
$callOriginalClone,
$callAutoload,
$cloneArguments,
$callOriginalMethods,
);
}

return self::$cache[$key];
}





public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []): string
{
if (!extension_loaded('soap')) {
throw new SoapExtensionNotAvailableException;
}

$options = array_merge($options, ['cache_wsdl' => WSDL_CACHE_NONE]);

try {
$client = new SoapClient($wsdlFile, $options);
$_methods = array_unique($client->__getFunctions());
unset($client);
} catch (SoapFault $e) {
throw new RuntimeException(
$e->getMessage(),
$e->getCode(),
$e,
);
}

sort($_methods);

$methodTemplate = $this->getTemplate('wsdl_method.tpl');
$methodsBuffer = '';

foreach ($_methods as $method) {
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(/', $method, $matches, PREG_OFFSET_CAPTURE);
$lastFunction = array_pop($matches[0]);
$nameStart = $lastFunction[1];
$nameEnd = $nameStart + strlen($lastFunction[0]) - 1;
$name = str_replace('(', '', $lastFunction[0]);

if (empty($methods) || in_array($name, $methods, true)) {
$args = explode(
',',
str_replace(')', '', substr($method, $nameEnd + 1)),
);

foreach (range(0, count($args) - 1) as $i) {
$parameterStart = strpos($args[$i], '$');

if (!$parameterStart) {
continue;
}

$args[$i] = substr($args[$i], $parameterStart);
}

$methodTemplate->setVar(
[
'method_name' => $name,
'arguments' => implode(', ', $args),
],
);

$methodsBuffer .= $methodTemplate->render();
}
}

$optionsBuffer = '[';

foreach ($options as $key => $value) {
$optionsBuffer .= $key . ' => ' . $value;
}

$optionsBuffer .= ']';

$classTemplate = $this->getTemplate('wsdl_class.tpl');
$namespace = '';

if (strpos($className, '\\') !== false) {
$parts = explode('\\', $className);
$className = array_pop($parts);
$namespace = 'namespace ' . implode('\\', $parts) . ';' . "\n\n";
}

$classTemplate->setVar(
[
'namespace' => $namespace,
'class_name' => $className,
'wsdl' => $wsdlFile,
'options' => $optionsBuffer,
'methods' => $methodsBuffer,
],
);

return $classTemplate->render();
}






public function getClassMethods(string $className): array
{
try {
$class = new ReflectionClass($className);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$methods = [];

foreach ($class->getMethods() as $method) {
if ($method->isPublic() || $method->isAbstract()) {
$methods[] = $method->getName();
}
}

return $methods;
}






public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments): array
{
try {
$class = new ReflectionClass($className);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$methods = [];

foreach ($class->getMethods() as $method) {
if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) {
$methods[] = MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments);
}
}

return $methods;
}






public function mockInterfaceMethods(string $interfaceName, bool $cloneArguments): array
{
try {
$class = new ReflectionClass($interfaceName);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$methods = [];

foreach ($class->getMethods() as $method) {
$methods[] = MockMethod::fromReflection($method, false, $cloneArguments);
}

return $methods;
}








private function userDefinedInterfaceMethods(string $interfaceName): array
{
try {

$interface = new ReflectionClass($interfaceName);
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$methods = [];

foreach ($interface->getMethods() as $method) {
if (!$method->isUserDefined()) {
continue;
}

$methods[] = $method;
}

return $methods;
}





private function getObject(MockType $mockClass, $type = '', bool $callOriginalConstructor = false, bool $callAutoload = false, array $arguments = [], bool $callOriginalMethods = false, object $proxyTarget = null, bool $returnValueGeneration = true)
{
$className = $mockClass->generate();

if ($callOriginalConstructor) {
if (count($arguments) === 0) {
$object = new $className;
} else {
try {
$class = new ReflectionClass($className);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$object = $class->newInstanceArgs($arguments);
}
} else {
try {
$object = (new Instantiator)->instantiate($className);
} catch (InstantiatorException $e) {
throw new RuntimeException($e->getMessage());
}
}

if ($callOriginalMethods) {
if (!is_object($proxyTarget)) {
if (count($arguments) === 0) {
$proxyTarget = new $type;
} else {
try {
$class = new ReflectionClass($type);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$proxyTarget = $class->newInstanceArgs($arguments);
}
}

$object->__phpunit_setOriginalObject($proxyTarget);
}

if ($object instanceof MockObject) {
$object->__phpunit_setReturnValueGeneration($returnValueGeneration);
}

return $object;
}







private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): MockClass
{
$classTemplate = $this->getTemplate('mocked_class.tpl');
$additionalInterfaces = [];
$mockedCloneMethod = false;
$unmockedCloneMethod = false;
$isClass = false;
$isInterface = false;
$class = null;
$mockMethods = new MockMethodSet;

$_mockClassName = $this->generateClassName(
$type,
$mockClassName,
'Mock_',
);

if (class_exists($_mockClassName['fullClassName'], $callAutoload)) {
$isClass = true;
} elseif (interface_exists($_mockClassName['fullClassName'], $callAutoload)) {
$isInterface = true;
}

if (!$isClass && !$isInterface) {
$prologue = 'class ' . $_mockClassName['originalClassName'] . "\n{\n}\n\n";

if (!empty($_mockClassName['namespaceName'])) {
$prologue = 'namespace ' . $_mockClassName['namespaceName'] .
" {\n\n" . $prologue . "}\n\n" .
"namespace {\n\n";

$epilogue = "\n\n}";
}

$mockedCloneMethod = true;
} else {
try {
$class = new ReflectionClass($_mockClassName['fullClassName']);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($class->isFinal()) {
throw new ClassIsFinalException($_mockClassName['fullClassName']);
}

if (method_exists($class, 'isReadOnly') && $class->isReadOnly()) {
throw new ClassIsReadonlyException($_mockClassName['fullClassName']);
}


if ($isInterface && $class->implementsInterface(Throwable::class)) {
$actualClassName = Exception::class;
$additionalInterfaces[] = $class->getName();
$isInterface = false;

try {
$class = new ReflectionClass($actualClassName);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) {
$methodName = $method->getName();

if ($class->hasMethod($methodName)) {
try {
$classMethod = $class->getMethod($methodName);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if (!$this->canMockMethod($classMethod)) {
continue;
}
}

$mockMethods->addMethods(
MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments),
);
}

$_mockClassName = $this->generateClassName(
$actualClassName,
$_mockClassName['className'],
'Mock_',
);
}


if ($isInterface && $class->implementsInterface(Traversable::class) &&
!$class->implementsInterface(Iterator::class) &&
!$class->implementsInterface(IteratorAggregate::class)) {
$additionalInterfaces[] = Iterator::class;

$mockMethods->addMethods(
...$this->mockClassMethods(Iterator::class, $callOriginalMethods, $cloneArguments),
);
}

if ($class->hasMethod('__clone')) {
try {
$cloneMethod = $class->getMethod('__clone');

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if (!$cloneMethod->isFinal()) {
if ($callOriginalClone && !$isInterface) {
$unmockedCloneMethod = true;
} else {
$mockedCloneMethod = true;
}
}
} else {
$mockedCloneMethod = true;
}
}

if ($isClass && $explicitMethods === []) {
$mockMethods->addMethods(
...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments),
);
}

if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) {
$mockMethods->addMethods(
...$this->mockInterfaceMethods($_mockClassName['fullClassName'], $cloneArguments),
);
}

if (is_array($explicitMethods)) {
foreach ($explicitMethods as $methodName) {
if ($class !== null && $class->hasMethod($methodName)) {
try {
$method = $class->getMethod($methodName);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($this->canMockMethod($method)) {
$mockMethods->addMethods(
MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments),
);
}
} else {
$mockMethods->addMethods(
MockMethod::fromName(
$_mockClassName['fullClassName'],
$methodName,
$cloneArguments,
),
);
}
}
}

$mockedMethods = '';
$configurable = [];

foreach ($mockMethods->asArray() as $mockMethod) {
$mockedMethods .= $mockMethod->generateCode();
$configurable[] = new ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType());
}

$method = '';

if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) {
$method = PHP_EOL . '    use \PHPUnit\Framework\MockObject\Method;';
}

$cloneTrait = '';

if ($mockedCloneMethod) {
$cloneTrait = $this->mockedCloneMethod();
}

if ($unmockedCloneMethod) {
$cloneTrait = $this->unmockedCloneMethod();
}

$classTemplate->setVar(
[
'prologue' => $prologue ?? '',
'epilogue' => $epilogue ?? '',
'class_declaration' => $this->generateMockClassDeclaration(
$_mockClassName,
$isInterface,
$additionalInterfaces,
),
'clone' => $cloneTrait,
'mock_class_name' => $_mockClassName['className'],
'mocked_methods' => $mockedMethods,
'method' => $method,
],
);

return new MockClass(
$classTemplate->render(),
$_mockClassName['className'],
$configurable,
);
}

private function generateClassName(string $type, string $className, string $prefix): array
{
if ($type[0] === '\\') {
$type = substr($type, 1);
}

$classNameParts = explode('\\', $type);

if (count($classNameParts) > 1) {
$type = array_pop($classNameParts);
$namespaceName = implode('\\', $classNameParts);
$fullClassName = $namespaceName . '\\' . $type;
} else {
$namespaceName = '';
$fullClassName = $type;
}

if ($className === '') {
do {
$className = $prefix . $type . '_' .
substr(md5((string) mt_rand()), 0, 8);
} while (class_exists($className, false));
}

return [
'className' => $className,
'originalClassName' => $type,
'fullClassName' => $fullClassName,
'namespaceName' => $namespaceName,
];
}

private function generateMockClassDeclaration(array $mockClassName, bool $isInterface, array $additionalInterfaces = []): string
{
$buffer = 'class ';

$additionalInterfaces[] = MockObject::class;
$interfaces = implode(', ', $additionalInterfaces);

if ($isInterface) {
$buffer .= sprintf(
'%s implements %s',
$mockClassName['className'],
$interfaces,
);

if (!in_array($mockClassName['originalClassName'], $additionalInterfaces, true)) {
$buffer .= ', ';

if (!empty($mockClassName['namespaceName'])) {
$buffer .= $mockClassName['namespaceName'] . '\\';
}

$buffer .= $mockClassName['originalClassName'];
}
} else {
$buffer .= sprintf(
'%s extends %s%s implements %s',
$mockClassName['className'],
!empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
$mockClassName['originalClassName'],
$interfaces,
);
}

return $buffer;
}

private function canMockMethod(ReflectionMethod $method): bool
{
return !($this->isConstructor($method) || $method->isFinal() || $method->isPrivate() || $this->isMethodNameExcluded($method->getName()));
}

private function isMethodNameExcluded(string $name): bool
{
return isset(self::EXCLUDED_METHOD_NAMES[$name]);
}




private function getTemplate(string $template): Template
{
$filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template;

if (!isset(self::$templates[$filename])) {
try {
self::$templates[$filename] = new Template($filename);
} catch (TemplateException $e) {
throw new RuntimeException(
$e->getMessage(),
$e->getCode(),
$e,
);
}
}

return self::$templates[$filename];
}




private function isConstructor(ReflectionMethod $method): bool
{
$methodName = strtolower($method->getName());

if ($methodName === '__construct') {
return true;
}

if (PHP_MAJOR_VERSION >= 8) {
return false;
}

$className = strtolower($method->getDeclaringClass()->getName());

return $methodName === $className;
}

private function mockedCloneMethod(): string
{
if (PHP_MAJOR_VERSION >= 8) {
if (!trait_exists('\PHPUnit\Framework\MockObject\MockedCloneMethodWithVoidReturnType')) {
eval(self::MOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT);
}

return PHP_EOL . '    use \PHPUnit\Framework\MockObject\MockedCloneMethodWithVoidReturnType;';
}

if (!trait_exists('\PHPUnit\Framework\MockObject\MockedCloneMethodWithoutReturnType')) {
eval(self::MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT);
}

return PHP_EOL . '    use \PHPUnit\Framework\MockObject\MockedCloneMethodWithoutReturnType;';
}

private function unmockedCloneMethod(): string
{
if (PHP_MAJOR_VERSION >= 8) {
if (!trait_exists('\PHPUnit\Framework\MockObject\UnmockedCloneMethodWithVoidReturnType')) {
eval(self::UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT);
}

return PHP_EOL . '    use \PHPUnit\Framework\MockObject\UnmockedCloneMethodWithVoidReturnType;';
}

if (!trait_exists('\PHPUnit\Framework\MockObject\UnmockedCloneMethodWithoutReturnType')) {
eval(self::UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT);
}

return PHP_EOL . '    use \PHPUnit\Framework\MockObject\UnmockedCloneMethodWithoutReturnType;';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use PHPUnit\Framework\MockObject\Builder\InvocationStubber;






interface Stub
{
public function __phpunit_getInvocationHandler(): InvocationHandler;

public function __phpunit_hasMatchers(): bool;

public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function sprintf;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;




final class ReturnReference implements Stub
{



private $reference;

public function __construct(&$reference)
{
$this->reference = &$reference;
}

public function invoke(Invocation $invocation)
{
return $this->reference;
}

public function toString(): string
{
$exporter = new Exporter;

return sprintf(
'return user-specified reference %s',
$exporter->export($this->reference),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use PHPUnit\Framework\MockObject\Invocation;
use PHPUnit\Framework\MockObject\RuntimeException;




final class ReturnSelf implements Stub
{



public function invoke(Invocation $invocation)
{
return $invocation->getObject();
}

public function toString(): string
{
return 'return the current object';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use PHPUnit\Framework\MockObject\Invocation;
use PHPUnit\Framework\SelfDescribing;




interface Stub extends SelfDescribing
{






public function invoke(Invocation $invocation);
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function array_pop;
use function count;
use function is_array;
use PHPUnit\Framework\MockObject\Invocation;




final class ReturnValueMap implements Stub
{



private $valueMap;

public function __construct(array $valueMap)
{
$this->valueMap = $valueMap;
}

public function invoke(Invocation $invocation)
{
$parameterCount = count($invocation->getParameters());

foreach ($this->valueMap as $map) {
if (!is_array($map) || $parameterCount !== (count($map) - 1)) {
continue;
}

$return = array_pop($map);

if ($invocation->getParameters() === $map) {
return $return;
}
}
}

public function toString(): string
{
return 'return value from a map';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function array_shift;
use function sprintf;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;




final class ConsecutiveCalls implements Stub
{



private $stack;




private $value;

public function __construct(array $stack)
{
$this->stack = $stack;
}

public function invoke(Invocation $invocation)
{
$this->value = array_shift($this->stack);

if ($this->value instanceof Stub) {
$this->value = $this->value->invoke($invocation);
}

return $this->value;
}

public function toString(): string
{
$exporter = new Exporter;

return sprintf(
'return user-specified value %s',
$exporter->export($this->value),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function sprintf;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;
use Throwable;




final class Exception implements Stub
{
private $exception;

public function __construct(Throwable $exception)
{
$this->exception = $exception;
}




public function invoke(Invocation $invocation): void
{
throw $this->exception;
}

public function toString(): string
{
$exporter = new Exporter;

return sprintf(
'raise user-specified exception %s',
$exporter->export($this->exception),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function sprintf;
use PHPUnit\Framework\MockObject\Invocation;




final class ReturnArgument implements Stub
{



private $argumentIndex;

public function __construct($argumentIndex)
{
$this->argumentIndex = $argumentIndex;
}

public function invoke(Invocation $invocation)
{
if (isset($invocation->getParameters()[$this->argumentIndex])) {
return $invocation->getParameters()[$this->argumentIndex];
}
}

public function toString(): string
{
return sprintf('return argument #%d', $this->argumentIndex);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function call_user_func_array;
use function get_class;
use function is_array;
use function is_object;
use function sprintf;
use PHPUnit\Framework\MockObject\Invocation;




final class ReturnCallback implements Stub
{
private $callback;

public function __construct($callback)
{
$this->callback = $callback;
}

public function invoke(Invocation $invocation)
{
return call_user_func_array($this->callback, $invocation->getParameters());
}

public function toString(): string
{
if (is_array($this->callback)) {
if (is_object($this->callback[0])) {
$class = get_class($this->callback[0]);
$type = '->';
} else {
$class = $this->callback[0];
$type = '::';
}

return sprintf(
'return result of user defined callback %s%s%s() with the ' .
'passed arguments',
$class,
$type,
$this->callback[1],
);
}

return 'return result of user defined callback ' . $this->callback .
' with the passed arguments';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Stub;

use function sprintf;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;




final class ReturnStub implements Stub
{



private $value;

public function __construct($value)
{
$this->value = $value;
}

public function invoke(Invocation $invocation)
{
return $this->value;
}

public function toString(): string
{
$exporter = new Exporter;

return sprintf(
'return user-specified value %s',
$exporter->export($this->value),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function array_map;
use function explode;
use function get_class;
use function implode;
use function in_array;
use function interface_exists;
use function is_object;
use function sprintf;
use function strpos;
use function strtolower;
use function substr;
use Doctrine\Instantiator\Instantiator;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Util\Cloner;
use SebastianBergmann\Exporter\Exporter;
use stdClass;
use Throwable;




final class Invocation implements SelfDescribing
{



private $className;




private $methodName;




private $parameters;




private $returnType;




private $isReturnTypeNullable = false;




private $proxiedCall;




private $object;

public function __construct(string $className, string $methodName, array $parameters, string $returnType, object $object, bool $cloneObjects = false, bool $proxiedCall = false)
{
$this->className = $className;
$this->methodName = $methodName;
$this->parameters = $parameters;
$this->object = $object;
$this->proxiedCall = $proxiedCall;

if (strtolower($methodName) === '__tostring') {
$returnType = 'string';
}

if (strpos($returnType, '?') === 0) {
$returnType = substr($returnType, 1);
$this->isReturnTypeNullable = true;
}

$this->returnType = $returnType;

if (!$cloneObjects) {
return;
}

foreach ($this->parameters as $key => $value) {
if (is_object($value)) {
$this->parameters[$key] = Cloner::clone($value);
}
}
}

public function getClassName(): string
{
return $this->className;
}

public function getMethodName(): string
{
return $this->methodName;
}

public function getParameters(): array
{
return $this->parameters;
}






public function generateReturnValue()
{
if ($this->isReturnTypeNullable || $this->proxiedCall) {
return null;
}

$intersection = false;
$union = false;
$unionContainsIntersections = false;

if (strpos($this->returnType, '|') !== false) {
$types = explode('|', $this->returnType);
$union = true;

if (strpos($this->returnType, '(') !== false) {
$unionContainsIntersections = true;
}
} elseif (strpos($this->returnType, '&') !== false) {
$types = explode('&', $this->returnType);
$intersection = true;
} else {
$types = [$this->returnType];
}

$types = array_map('strtolower', $types);

if (!$intersection && !$unionContainsIntersections) {
if (in_array('', $types, true) ||
in_array('null', $types, true) ||
in_array('mixed', $types, true) ||
in_array('void', $types, true)) {
return null;
}

if (in_array('true', $types, true)) {
return true;
}

if (in_array('false', $types, true) ||
in_array('bool', $types, true)) {
return false;
}

if (in_array('float', $types, true)) {
return 0.0;
}

if (in_array('int', $types, true)) {
return 0;
}

if (in_array('string', $types, true)) {
return '';
}

if (in_array('array', $types, true)) {
return [];
}

if (in_array('static', $types, true)) {
try {
return (new Instantiator)->instantiate(get_class($this->object));
} catch (Throwable $t) {
throw new RuntimeException(
$t->getMessage(),
(int) $t->getCode(),
$t,
);
}
}

if (in_array('object', $types, true)) {
return new stdClass;
}

if (in_array('callable', $types, true) ||
in_array('closure', $types, true)) {
return static function (): void
{
};
}

if (in_array('traversable', $types, true) ||
in_array('generator', $types, true) ||
in_array('iterable', $types, true)) {
$generator = static function (): \Generator
{
yield from [];
};

return $generator();
}

if (!$union) {
try {
return (new Generator)->getMock($this->returnType, [], [], '', false);
} catch (Throwable $t) {
if ($t instanceof Exception) {
throw $t;
}

throw new RuntimeException(
$t->getMessage(),
(int) $t->getCode(),
$t,
);
}
}
}

if ($intersection && $this->onlyInterfaces($types)) {
try {
return (new Generator)->getMockForInterfaces($types);
} catch (Throwable $t) {
throw new RuntimeException(
sprintf(
'Return value for %s::%s() cannot be generated: %s',
$this->className,
$this->methodName,
$t->getMessage(),
),
(int) $t->getCode(),
);
}
}

$reason = '';

if ($union) {
$reason = ' because the declared return type is a union';
} elseif ($intersection) {
$reason = ' because the declared return type is an intersection';
}

throw new RuntimeException(
sprintf(
'Return value for %s::%s() cannot be generated%s, please configure a return value for this method',
$this->className,
$this->methodName,
$reason,
),
);
}

public function toString(): string
{
$exporter = new Exporter;

return sprintf(
'%s::%s(%s)%s',
$this->className,
$this->methodName,
implode(
', ',
array_map(
[$exporter, 'shortenedExport'],
$this->parameters,
),
),
$this->returnType ? sprintf(': %s', $this->returnType) : '',
);
}

public function getObject(): object
{
return $this->object;
}




private function onlyInterfaces(array $types): bool
{
foreach ($types as $type) {
if (!interface_exists($type)) {
return false;
}
}

return true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function array_key_exists;
use function array_values;
use function strtolower;




final class MockMethodSet
{



private $methods = [];

public function addMethods(MockMethod ...$methods): void
{
foreach ($methods as $method) {
$this->methods[strtolower($method->getName())] = $method;
}
}




public function asArray(): array
{
return array_values($this->methods);
}

public function hasMethod(string $methodName): bool
{
return array_key_exists(strtolower($methodName), $this->methods);
}
}

    {modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
    {
        $__phpunit_arguments = [{arguments_call}];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > {arguments_count}) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true
            )
        );

        return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments);
    }

        @trigger_error({deprecation}, E_USER_DEPRECATED);

    {modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
    {{deprecation}
        $__phpunit_arguments = [{arguments_call}];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > {arguments_count}) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}
            )
        );
    }
declare(strict_types=1);

{prologue}{class_declaration}
{
    use \PHPUnit\Framework\MockObject\Api;{method}{clone}
{mocked_methods}}{epilogue}
declare(strict_types=1);

interface {intersection} extends {interfaces}
{
}

    {modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
    {
        throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object');
    }

    public function {method_name}({arguments})
    {
    }

    {modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
    {
        $__phpunit_arguments = [{arguments_call}];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > {arguments_count}) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true
            )
        );

        call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments);
    }
declare(strict_types=1);

{namespace}class {class_name} extends \SoapClient
{
    public function __construct($wsdl, array $options)
    {
        parent::__construct('{wsdl}', $options);
    }
{methods}}
declare(strict_types=1);

{prologue}class {class_name}
{
    use {trait_name};
}

    {modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
    {{deprecation}
        $__phpunit_arguments = [{arguments_call}];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > {arguments_count}) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}
            )
        );

        return $__phpunit_result;
    }
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function class_exists;




final class MockTrait implements MockType
{



private $classCode;




private $mockName;




public function __construct(string $classCode, string $mockName)
{
$this->classCode = $classCode;
$this->mockName = $mockName;
}




public function generate(): string
{
if (!class_exists($this->mockName, false)) {
eval($this->classCode);
}

return $this->mockName;
}

public function getClassCode(): string
{
return $this->classCode;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




interface MockType
{



public function generate(): string;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function is_string;
use function sprintf;
use function strtolower;
use PHPUnit\Framework\Constraint\Constraint;




final class MethodNameConstraint extends Constraint
{



private $methodName;

public function __construct(string $methodName)
{
$this->methodName = $methodName;
}

public function toString(): string
{
return sprintf(
'is "%s"',
$this->methodName,
);
}

protected function matches($other): bool
{
if (!is_string($other)) {
return false;
}

return strtolower($this->methodName) === strtolower($other);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function array_diff;
use function array_merge;
use PHPUnit\Framework\TestCase;
use ReflectionClass;






final class MockBuilder
{



private $testCase;




private $type;




private $methods = [];




private $emptyMethodsArray = false;




private $mockClassName = '';




private $constructorArgs = [];




private $originalConstructor = true;




private $originalClone = true;




private $autoload = true;




private $cloneArguments = false;




private $callOriginalMethods = false;




private $proxyTarget;




private $allowMockingUnknownTypes = true;




private $returnValueGeneration = true;




private $generator;






public function __construct(TestCase $testCase, $type)
{
$this->testCase = $testCase;
$this->type = $type;
$this->generator = new Generator;
}

















public function getMock(): MockObject
{
$object = $this->generator->getMock(
$this->type,
!$this->emptyMethodsArray ? $this->methods : null,
$this->constructorArgs,
$this->mockClassName,
$this->originalConstructor,
$this->originalClone,
$this->autoload,
$this->cloneArguments,
$this->callOriginalMethods,
$this->proxyTarget,
$this->allowMockingUnknownTypes,
$this->returnValueGeneration,
);

$this->testCase->registerMockObject($object);

return $object;
}










public function getMockForAbstractClass(): MockObject
{
$object = $this->generator->getMockForAbstractClass(
$this->type,
$this->constructorArgs,
$this->mockClassName,
$this->originalConstructor,
$this->originalClone,
$this->autoload,
$this->methods,
$this->cloneArguments,
);

$this->testCase->registerMockObject($object);

return $object;
}










public function getMockForTrait(): MockObject
{
$object = $this->generator->getMockForTrait(
$this->type,
$this->constructorArgs,
$this->mockClassName,
$this->originalConstructor,
$this->originalClone,
$this->autoload,
$this->methods,
$this->cloneArguments,
);

$this->testCase->registerMockObject($object);

return $object;
}








public function setMethods(?array $methods = null): self
{
if ($methods === null) {
$this->methods = $methods;
} else {
$this->methods = array_merge($this->methods ?? [], $methods);
}

return $this;
}











public function onlyMethods(array $methods): self
{
if (empty($methods)) {
$this->emptyMethodsArray = true;

return $this;
}

try {
$reflector = new ReflectionClass($this->type);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


foreach ($methods as $method) {
if (!$reflector->hasMethod($method)) {
throw new CannotUseOnlyMethodsException($this->type, $method);
}
}

$this->methods = array_merge($this->methods ?? [], $methods);

return $this;
}












public function addMethods(array $methods): self
{
if (empty($methods)) {
$this->emptyMethodsArray = true;

return $this;
}

try {
$reflector = new ReflectionClass($this->type);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


foreach ($methods as $method) {
if ($reflector->hasMethod($method)) {
throw new CannotUseAddMethodsException($this->type, $method);
}
}

$this->methods = array_merge($this->methods ?? [], $methods);

return $this;
}








public function setMethodsExcept(array $methods = []): self
{
return $this->setMethods(
array_diff(
$this->generator->getClassMethods($this->type),
$methods,
),
);
}






public function setConstructorArgs(array $args): self
{
$this->constructorArgs = $args;

return $this;
}






public function setMockClassName(string $name): self
{
$this->mockClassName = $name;

return $this;
}






public function disableOriginalConstructor(): self
{
$this->originalConstructor = false;

return $this;
}






public function enableOriginalConstructor(): self
{
$this->originalConstructor = true;

return $this;
}






public function disableOriginalClone(): self
{
$this->originalClone = false;

return $this;
}






public function enableOriginalClone(): self
{
$this->originalClone = true;

return $this;
}






public function disableAutoload(): self
{
$this->autoload = false;

return $this;
}






public function enableAutoload(): self
{
$this->autoload = true;

return $this;
}






public function disableArgumentCloning(): self
{
$this->cloneArguments = false;

return $this;
}






public function enableArgumentCloning(): self
{
$this->cloneArguments = true;

return $this;
}






public function enableProxyingToOriginalMethods(): self
{
$this->callOriginalMethods = true;

return $this;
}






public function disableProxyingToOriginalMethods(): self
{
$this->callOriginalMethods = false;
$this->proxyTarget = null;

return $this;
}






public function setProxyTarget(object $object): self
{
$this->proxyTarget = $object;

return $this;
}




public function allowMockingUnknownTypes(): self
{
$this->allowMockingUnknownTypes = true;

return $this;
}




public function disallowMockingUnknownTypes(): self
{
$this->allowMockingUnknownTypes = false;

return $this;
}




public function enableAutoReturnValueGeneration(): self
{
$this->returnValueGeneration = true;

return $this;
}




public function disableAutoReturnValueGeneration(): self
{
$this->returnValueGeneration = false;

return $this;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function call_user_func;
use function class_exists;




final class MockClass implements MockType
{



private $classCode;




private $mockName;




private $configurableMethods;




public function __construct(string $classCode, string $mockName, array $configurableMethods)
{
$this->classCode = $classCode;
$this->mockName = $mockName;
$this->configurableMethods = $configurableMethods;
}




public function generate(): string
{
if (!class_exists($this->mockName, false)) {
eval($this->classCode);

call_user_func(
[
$this->mockName,
'__phpunit_initConfigurableMethods',
],
...$this->configurableMethods,
);
}

return $this->mockName;
}

public function getClassCode(): string
{
return $this->classCode;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use PHPUnit\Framework\ExpectationFailedException;




interface Verifiable
{






public function verify(): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;






interface MockObject extends Stub
{
public function __phpunit_setOriginalObject($originalObject): void;

public function __phpunit_verify(bool $unsetInvocationMocker = true): void;

public function expects(InvocationOrder $invocationRule): BuilderInvocationMocker;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class CannotUseOnlyMethodsException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $type, string $methodName)
{
parent::__construct(
sprintf(
'Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s". Use addMethods() for methods that do not exist in the class',
$methodName,
$type,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use RuntimeException;




final class ReflectionException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class InvalidMethodNameException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $method)
{
parent::__construct(
sprintf(
'Cannot double method with invalid name "%s"',
$method,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class UnknownClassException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $className)
{
parent::__construct(
sprintf(
'Class "%s" does not exist',
$className,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function get_class;
use function gettype;
use function is_object;
use function sprintf;




final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements Exception
{



public function __construct(ConfigurableMethod $method, $value)
{
parent::__construct(
sprintf(
'Method %s may not return value of type %s, its declared return type is "%s"',
$method->getName(),
is_object($value) ? get_class($value) : gettype($value),
$method->getReturnTypeDeclaration(),
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class SoapExtensionNotAvailableException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct()
{
parent::__construct(
'The SOAP extension is required to generate a test double from WSDL',
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class ClassIsReadonlyException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $className)
{
parent::__construct(
sprintf(
'Class "%s" is declared "readonly" and cannot be doubled',
$className,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class ClassIsFinalException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $className)
{
parent::__construct(
sprintf(
'Class "%s" is declared "final" and cannot be doubled',
$className,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class MatchBuilderNotFoundException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $id)
{
parent::__construct(
sprintf(
'No builder found for match builder identification <%s>',
$id,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function array_diff_assoc;
use function array_unique;
use function implode;
use function sprintf;




final class DuplicateMethodException extends \PHPUnit\Framework\Exception implements Exception
{



public function __construct(array $methods)
{
parent::__construct(
sprintf(
'Cannot double using a method list that contains duplicates: "%s" (duplicate: "%s")',
implode(', ', $methods),
implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods)))),
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class UnknownTraitException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $traitName)
{
parent::__construct(
sprintf(
'Trait "%s" does not exist',
$traitName,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class MethodNameNotConfiguredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct()
{
parent::__construct('Method name is not configured');
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use Throwable;




interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class UnknownTypeException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $type)
{
parent::__construct(
sprintf(
'Class or interface "%s" does not exist',
$type,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class ClassAlreadyExistsException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $className)
{
parent::__construct(
sprintf(
'Class "%s" already exists',
$className,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class ReturnValueNotConfiguredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(Invocation $invocation)
{
parent::__construct(
sprintf(
'Return value inference disabled and no expectation set up for %s::%s()',
$invocation->getClassName(),
$invocation->getMethodName(),
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class MethodNameAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct()
{
parent::__construct('Method name is already configured');
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class BadMethodCallException extends \BadMethodCallException implements Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class MethodParametersAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct()
{
parent::__construct('Method parameters already configured');
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class CannotUseAddMethodsException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $type, string $methodName)
{
parent::__construct(
sprintf(
'Trying to configure method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class',
$methodName,
$type,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class MethodCannotBeConfiguredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $method)
{
parent::__construct(
sprintf(
'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static',
$method,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class OriginalConstructorInvocationRequiredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct()
{
parent::__construct('Proxying to original methods requires invoking the original constructor');
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;




final class ConfigurableMethodsAlreadyInitializedException extends \PHPUnit\Framework\Exception implements Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function sprintf;




final class MatcherAlreadyRegisteredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $id)
{
parent::__construct(
sprintf(
'Matcher with id <%s> is already registered',
$id,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Builder;

use function array_map;
use function array_merge;
use function count;
use function in_array;
use function is_string;
use function strtolower;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\MockObject\ConfigurableMethod;
use PHPUnit\Framework\MockObject\IncompatibleReturnValueException;
use PHPUnit\Framework\MockObject\InvocationHandler;
use PHPUnit\Framework\MockObject\Matcher;
use PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException;
use PHPUnit\Framework\MockObject\MethodCannotBeConfiguredException;
use PHPUnit\Framework\MockObject\MethodNameAlreadyConfiguredException;
use PHPUnit\Framework\MockObject\MethodNameNotConfiguredException;
use PHPUnit\Framework\MockObject\MethodParametersAlreadyConfiguredException;
use PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls;
use PHPUnit\Framework\MockObject\Stub\Exception;
use PHPUnit\Framework\MockObject\Stub\ReturnArgument;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
use PHPUnit\Framework\MockObject\Stub\ReturnReference;
use PHPUnit\Framework\MockObject\Stub\ReturnSelf;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap;
use PHPUnit\Framework\MockObject\Stub\Stub;
use Throwable;




final class InvocationMocker implements InvocationStubber, MethodNameMatch
{



private $invocationHandler;




private $matcher;




private $configurableMethods;

public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods)
{
$this->invocationHandler = $handler;
$this->matcher = $matcher;
$this->configurableMethods = $configurableMethods;
}






public function id($id): self
{
$this->invocationHandler->registerMatcher($id, $this->matcher);

return $this;
}




public function will(Stub $stub): Identity
{
$this->matcher->setStub($stub);

return $this;
}







public function willReturn($value, ...$nextValues): self
{
if (count($nextValues) === 0) {
$this->ensureTypeOfReturnValues([$value]);

$stub = $value instanceof Stub ? $value : new ReturnStub($value);
} else {
$values = array_merge([$value], $nextValues);

$this->ensureTypeOfReturnValues($values);

$stub = new ConsecutiveCalls($values);
}

return $this->will($stub);
}

public function willReturnReference(&$reference): self
{
$stub = new ReturnReference($reference);

return $this->will($stub);
}

public function willReturnMap(array $valueMap): self
{
$stub = new ReturnValueMap($valueMap);

return $this->will($stub);
}

public function willReturnArgument($argumentIndex): self
{
$stub = new ReturnArgument($argumentIndex);

return $this->will($stub);
}

public function willReturnCallback($callback): self
{
$stub = new ReturnCallback($callback);

return $this->will($stub);
}

public function willReturnSelf(): self
{
$stub = new ReturnSelf;

return $this->will($stub);
}

public function willReturnOnConsecutiveCalls(...$values): self
{
$stub = new ConsecutiveCalls($values);

return $this->will($stub);
}

public function willThrowException(Throwable $exception): self
{
$stub = new Exception($exception);

return $this->will($stub);
}




public function after($id): self
{
$this->matcher->setAfterMatchBuilderId($id);

return $this;
}










public function with(...$arguments): self
{
$this->ensureParametersCanBeConfigured();

$this->matcher->setParametersRule(new Rule\Parameters($arguments));

return $this;
}












public function withConsecutive(...$arguments): self
{
$this->ensureParametersCanBeConfigured();

$this->matcher->setParametersRule(new Rule\ConsecutiveParameters($arguments));

return $this;
}







public function withAnyParameters(): self
{
$this->ensureParametersCanBeConfigured();

$this->matcher->setParametersRule(new Rule\AnyParameters);

return $this;
}










public function method($constraint): self
{
if ($this->matcher->hasMethodNameRule()) {
throw new MethodNameAlreadyConfiguredException;
}

$configurableMethodNames = array_map(
static function (ConfigurableMethod $configurable)
{
return strtolower($configurable->getName());
},
$this->configurableMethods,
);

if (is_string($constraint) && !in_array(strtolower($constraint), $configurableMethodNames, true)) {
throw new MethodCannotBeConfiguredException($constraint);
}

$this->matcher->setMethodNameRule(new Rule\MethodName($constraint));

return $this;
}





private function ensureParametersCanBeConfigured(): void
{
if (!$this->matcher->hasMethodNameRule()) {
throw new MethodNameNotConfiguredException;
}

if ($this->matcher->hasParametersRule()) {
throw new MethodParametersAlreadyConfiguredException;
}
}

private function getConfiguredMethod(): ?ConfigurableMethod
{
$configuredMethod = null;

foreach ($this->configurableMethods as $configurableMethod) {
if ($this->matcher->getMethodNameRule()->matchesName($configurableMethod->getName())) {
if ($configuredMethod !== null) {
return null;
}

$configuredMethod = $configurableMethod;
}
}

return $configuredMethod;
}




private function ensureTypeOfReturnValues(array $values): void
{
$configuredMethod = $this->getConfiguredMethod();

if ($configuredMethod === null) {
return;
}

foreach ($values as $value) {
if (!$configuredMethod->mayReturn($value)) {
throw new IncompatibleReturnValueException(
$configuredMethod,
$value,
);
}
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Builder;

use PHPUnit\Framework\MockObject\Stub\Stub as BaseStub;




interface Stub extends Identity
{




public function will(BaseStub $stub): Identity;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Builder;




interface MethodNameMatch extends ParametersMatch
{








public function method($constraint);
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Builder;




interface ParametersMatch extends Stub
{








public function after($id);


















public function with(...$arguments);












public function withAnyParameters();
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Builder;




interface Identity
{







public function id($id);
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject\Builder;

use PHPUnit\Framework\MockObject\Stub\Stub;
use Throwable;




interface InvocationStubber
{
public function will(Stub $stub): Identity;


public function willReturn($value, ...$nextValues);






public function willReturnReference(&$reference);






public function willReturnMap(array $valueMap);






public function willReturnArgument($argumentIndex);






public function willReturnCallback($callback);


public function willReturnSelf();






public function willReturnOnConsecutiveCalls(...$values);


public function willThrowException(Throwable $exception);
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use SebastianBergmann\Type\Type;




final class ConfigurableMethod
{



private $name;




private $returnType;

public function __construct(string $name, Type $returnType)
{
$this->name = $name;
$this->returnType = $returnType;
}

public function getName(): string
{
return $this->name;
}

public function mayReturn($value): bool
{
if ($value === null && $this->returnType->allowsNull()) {
return true;
}

return $this->returnType->isAssignable(Type::fromValue($value, false));
}

public function getReturnTypeDeclaration(): string
{
return $this->returnType->asString();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function assert;
use function implode;
use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
use PHPUnit\Framework\MockObject\Rule\AnyParameters;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount;
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
use PHPUnit\Framework\MockObject\Rule\MethodName;
use PHPUnit\Framework\MockObject\Rule\ParametersRule;
use PHPUnit\Framework\MockObject\Stub\Stub;
use PHPUnit\Framework\TestFailure;




final class Matcher
{



private $invocationRule;




private $afterMatchBuilderId;




private $afterMatchBuilderIsInvoked = false;




private $methodNameRule;




private $parametersRule;




private $stub;

public function __construct(InvocationOrder $rule)
{
$this->invocationRule = $rule;
}

public function hasMatchers(): bool
{
return !$this->invocationRule instanceof AnyInvokedCount;
}

public function hasMethodNameRule(): bool
{
return $this->methodNameRule !== null;
}

public function getMethodNameRule(): MethodName
{
return $this->methodNameRule;
}

public function setMethodNameRule(MethodName $rule): void
{
$this->methodNameRule = $rule;
}

public function hasParametersRule(): bool
{
return $this->parametersRule !== null;
}

public function setParametersRule(ParametersRule $rule): void
{
$this->parametersRule = $rule;
}

public function setStub(Stub $stub): void
{
$this->stub = $stub;
}

public function setAfterMatchBuilderId(string $id): void
{
$this->afterMatchBuilderId = $id;
}







public function invoked(Invocation $invocation)
{
if ($this->methodNameRule === null) {
throw new MethodNameNotConfiguredException;
}

if ($this->afterMatchBuilderId !== null) {
$matcher = $invocation->getObject()
->__phpunit_getInvocationHandler()
->lookupMatcher($this->afterMatchBuilderId);

if (!$matcher) {
throw new MatchBuilderNotFoundException($this->afterMatchBuilderId);
}

assert($matcher instanceof self);

if ($matcher->invocationRule->hasBeenInvoked()) {
$this->afterMatchBuilderIsInvoked = true;
}
}

$this->invocationRule->invoked($invocation);

try {
if ($this->parametersRule !== null) {
$this->parametersRule->apply($invocation);
}
} catch (ExpectationFailedException $e) {
throw new ExpectationFailedException(
sprintf(
"Expectation failed for %s when %s\n%s",
$this->methodNameRule->toString(),
$this->invocationRule->toString(),
$e->getMessage(),
),
$e->getComparisonFailure(),
);
}

if ($this->stub) {
return $this->stub->invoke($invocation);
}

return $invocation->generateReturnValue();
}








public function matches(Invocation $invocation): bool
{
if ($this->afterMatchBuilderId !== null) {
$matcher = $invocation->getObject()
->__phpunit_getInvocationHandler()
->lookupMatcher($this->afterMatchBuilderId);

if (!$matcher) {
throw new MatchBuilderNotFoundException($this->afterMatchBuilderId);
}

assert($matcher instanceof self);

if (!$matcher->invocationRule->hasBeenInvoked()) {
return false;
}
}

if ($this->methodNameRule === null) {
throw new MethodNameNotConfiguredException;
}

if (!$this->invocationRule->matches($invocation)) {
return false;
}

try {
if (!$this->methodNameRule->matches($invocation)) {
return false;
}
} catch (ExpectationFailedException $e) {
throw new ExpectationFailedException(
sprintf(
"Expectation failed for %s when %s\n%s",
$this->methodNameRule->toString(),
$this->invocationRule->toString(),
$e->getMessage(),
),
$e->getComparisonFailure(),
);
}

return true;
}






public function verify(): void
{
if ($this->methodNameRule === null) {
throw new MethodNameNotConfiguredException;
}

try {
$this->invocationRule->verify();

if ($this->parametersRule === null) {
$this->parametersRule = new AnyParameters;
}

$invocationIsAny = $this->invocationRule instanceof AnyInvokedCount;
$invocationIsNever = $this->invocationRule instanceof InvokedCount && $this->invocationRule->isNever();
$invocationIsAtMost = $this->invocationRule instanceof InvokedAtMostCount;

if (!$invocationIsAny && !$invocationIsNever && !$invocationIsAtMost) {
$this->parametersRule->verify();
}
} catch (ExpectationFailedException $e) {
throw new ExpectationFailedException(
sprintf(
"Expectation failed for %s when %s.\n%s",
$this->methodNameRule->toString(),
$this->invocationRule->toString(),
TestFailure::exceptionToString($e),
),
);
}
}

public function toString(): string
{
$list = [];

if ($this->invocationRule !== null) {
$list[] = $this->invocationRule->toString();
}

if ($this->methodNameRule !== null) {
$list[] = 'where ' . $this->methodNameRule->toString();
}

if ($this->parametersRule !== null) {
$list[] = 'and ' . $this->parametersRule->toString();
}

if ($this->afterMatchBuilderId !== null) {
$list[] = 'after ' . $this->afterMatchBuilderId;
}

if ($this->stub !== null) {
$list[] = 'will ' . $this->stub->toString();
}

return implode(' ', $list);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\MockObject;

use function strtolower;
use Exception;
use PHPUnit\Framework\MockObject\Builder\InvocationMocker;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
use Throwable;




final class InvocationHandler
{



private $matchers = [];




private $matcherMap = [];




private $configurableMethods;




private $returnValueGeneration;




private $deferredError;

public function __construct(array $configurableMethods, bool $returnValueGeneration)
{
$this->configurableMethods = $configurableMethods;
$this->returnValueGeneration = $returnValueGeneration;
}

public function hasMatchers(): bool
{
foreach ($this->matchers as $matcher) {
if ($matcher->hasMatchers()) {
return true;
}
}

return false;
}






public function lookupMatcher(string $id): ?Matcher
{
if (isset($this->matcherMap[$id])) {
return $this->matcherMap[$id];
}

return null;
}










public function registerMatcher(string $id, Matcher $matcher): void
{
if (isset($this->matcherMap[$id])) {
throw new MatcherAlreadyRegisteredException($id);
}

$this->matcherMap[$id] = $matcher;
}

public function expects(InvocationOrder $rule): InvocationMocker
{
$matcher = new Matcher($rule);
$this->addMatcher($matcher);

return new InvocationMocker(
$this,
$matcher,
...$this->configurableMethods,
);
}





public function invoke(Invocation $invocation)
{
$exception = null;
$hasReturnValue = false;
$returnValue = null;

foreach ($this->matchers as $match) {
try {
if ($match->matches($invocation)) {
$value = $match->invoked($invocation);

if (!$hasReturnValue) {
$returnValue = $value;
$hasReturnValue = true;
}
}
} catch (Exception $e) {
$exception = $e;
}
}

if ($exception !== null) {
throw $exception;
}

if ($hasReturnValue) {
return $returnValue;
}

if (!$this->returnValueGeneration) {
$exception = new ReturnValueNotConfiguredException($invocation);

if (strtolower($invocation->getMethodName()) === '__tostring') {
$this->deferredError = $exception;

return '';
}

throw $exception;
}

return $invocation->generateReturnValue();
}

public function matches(Invocation $invocation): bool
{
foreach ($this->matchers as $matcher) {
if (!$matcher->matches($invocation)) {
return false;
}
}

return true;
}




public function verify(): void
{
foreach ($this->matchers as $matcher) {
$matcher->verify();
}

if ($this->deferredError) {
throw $this->deferredError;
}
}

private function addMatcher(Matcher $matcher): void
{
$this->matchers[] = $matcher;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class InvalidParameterGroupException extends Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




interface Reorderable
{
public function sortId(): string;




public function provides(): array;




public function requires(): array;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use Countable;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\SelfDescribing;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Exporter\Exporter;




abstract class Constraint implements Countable, SelfDescribing
{



private $exporter;














public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{
$success = false;

if ($this->matches($other)) {
$success = true;
}

if ($returnResult) {
return $success;
}

if (!$success) {
$this->fail($other, $description);
}

return null;
}




public function count(): int
{
return 1;
}

protected function exporter(): Exporter
{
if ($this->exporter === null) {
$this->exporter = new Exporter;
}

return $this->exporter;
}











protected function matches($other): bool
{
return false;
}












protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void
{
$failureDescription = sprintf(
'Failed asserting that %s.',
$this->failureDescription($other),
);

$additionalFailureDescription = $this->additionalFailureDescription($other);

if ($additionalFailureDescription) {
$failureDescription .= "\n" . $additionalFailureDescription;
}

if (!empty($description)) {
$failureDescription = $description . "\n" . $failureDescription;
}

throw new ExpectationFailedException(
$failureDescription,
$comparisonFailure,
);
}









protected function additionalFailureDescription($other): string
{
return '';
}














protected function failureDescription($other): string
{
return $this->exporter()->export($other) . ' ' . $this->toString();
}
















protected function toStringInContext(Operator $operator, $role): string
{
return '';
}

















protected function failureDescriptionInContext(Operator $operator, $role, $other): string
{
$string = $this->toStringInContext($operator, $role);

if ($string === '') {
return '';
}

return $this->exporter()->export($other) . ' ' . $string;
}





























































protected function reduce(): self
{
return $this;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use const JSON_ERROR_CTRL_CHAR;
use const JSON_ERROR_DEPTH;
use const JSON_ERROR_NONE;
use const JSON_ERROR_STATE_MISMATCH;
use const JSON_ERROR_SYNTAX;
use const JSON_ERROR_UTF8;
use function strtolower;




final class JsonMatchesErrorMessageProvider
{



public static function determineJsonError(string $error, string $prefix = ''): ?string
{
switch ($error) {
case JSON_ERROR_NONE:
return null;

case JSON_ERROR_DEPTH:
return $prefix . 'Maximum stack depth exceeded';

case JSON_ERROR_STATE_MISMATCH:
return $prefix . 'Underflow or the modes mismatch';

case JSON_ERROR_CTRL_CHAR:
return $prefix . 'Unexpected control character found';

case JSON_ERROR_SYNTAX:
return $prefix . 'Syntax error, malformed JSON';

case JSON_ERROR_UTF8:
return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded';

default:
return $prefix . 'Unknown error';
}
}




public static function translateTypeToPrefix(string $type): string
{
switch (strtolower($type)) {
case 'expected':
$prefix = 'Expected value JSON decode error - ';

break;

case 'actual':
$prefix = 'Actual value JSON decode error - ';

break;

default:
$prefix = '';

break;
}

return $prefix;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use PHPUnit\Framework\ExpectationFailedException;




final class IsAnything extends Constraint
{












public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{
return $returnResult ? true : null;
}




public function toString(): string
{
return 'is anything';
}




public function count(): int
{
return 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_readable;
use function sprintf;




final class IsReadable extends Constraint
{



public function toString(): string
{
return 'is readable';
}







protected function matches($other): bool
{
return is_readable($other);
}









protected function failureDescription($other): string
{
return sprintf(
'"%s" is readable',
$other,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_dir;
use function sprintf;




final class DirectoryExists extends Constraint
{



public function toString(): string
{
return 'directory exists';
}







protected function matches($other): bool
{
return is_dir($other);
}









protected function failureDescription($other): string
{
return sprintf(
'directory "%s" exists',
$other,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_writable;
use function sprintf;




final class IsWritable extends Constraint
{



public function toString(): string
{
return 'is writable';
}







protected function matches($other): bool
{
return is_writable($other);
}









protected function failureDescription($other): string
{
return sprintf(
'"%s" is writable',
$other,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function file_exists;
use function sprintf;




final class FileExists extends Constraint
{



public function toString(): string
{
return 'file exists';
}







protected function matches($other): bool
{
return file_exists($other);
}









protected function failureDescription($other): string
{
return sprintf(
'file "%s" exists',
$other,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_array;
use function sprintf;




abstract class TraversableContains extends Constraint
{



private $value;

public function __construct($value)
{
$this->value = $value;
}






public function toString(): string
{
return 'contains ' . $this->exporter()->export($this->value);
}











protected function failureDescription($other): string
{
return sprintf(
'%s %s',
is_array($other) ? 'an array' : 'a traversable',
$this->toString(),
);
}

protected function value()
{
return $this->value;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use SplObjectStorage;




final class TraversableContainsEqual extends TraversableContains
{






protected function matches($other): bool
{
if ($other instanceof SplObjectStorage) {
return $other->contains($this->value());
}

foreach ($other as $element) {

if ($this->value() == $element) {
return true;
}
}

return false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use SplObjectStorage;




final class TraversableContainsIdentical extends TraversableContains
{






protected function matches($other): bool
{
if ($other instanceof SplObjectStorage) {
return $other->contains($this->value());
}

foreach ($other as $element) {
if ($this->value() === $element) {
return true;
}
}

return false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use PHPUnit\Framework\ExpectationFailedException;
use Traversable;




final class TraversableContainsOnly extends Constraint
{



private $constraint;




private $type;




public function __construct(string $type, bool $isNativeType = true)
{
if ($isNativeType) {
$this->constraint = new IsType($type);
} else {
$this->constraint = new IsInstanceOf(
$type,
);
}

$this->type = $type;
}
















public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{
$success = true;

foreach ($other as $item) {
if (!$this->constraint->evaluate($item, '', true)) {
$success = false;

break;
}
}

if ($returnResult) {
return $success;
}

if (!$success) {
$this->fail($other, $description);
}

return null;
}




public function toString(): string
{
return 'contains only values of type "' . $this->type . '"';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function array_key_exists;
use function is_array;
use ArrayAccess;




final class ArrayHasKey extends Constraint
{



private $key;




public function __construct($key)
{
$this->key = $key;
}






public function toString(): string
{
return 'has the key ' . $this->exporter()->export($this->key);
}







protected function matches($other): bool
{
if (is_array($other)) {
return array_key_exists($this->key, $other);
}

if ($other instanceof ArrayAccess) {
return $other->offsetExists($this->key);
}

return false;
}











protected function failureDescription($other): string
{
return 'an array ' . $this->toString();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class GreaterThan extends Constraint
{



private $value;




public function __construct($value)
{
$this->value = $value;
}






public function toString(): string
{
return 'is greater than ' . $this->exporter()->export($this->value);
}







protected function matches($other): bool
{
return $this->value < $other;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function count;
use function gettype;
use function sprintf;
use function strpos;
use Countable;
use EmptyIterator;




final class IsEmpty extends Constraint
{



public function toString(): string
{
return 'is empty';
}







protected function matches($other): bool
{
if ($other instanceof EmptyIterator) {
return true;
}

if ($other instanceof Countable) {
return count($other) === 0;
}

return empty($other);
}









protected function failureDescription($other): string
{
$type = gettype($other);

return sprintf(
'%s %s %s',
strpos($type, 'a') === 0 || strpos($type, 'o') === 0 ? 'an' : 'a',
$type,
$this->toString(),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function count;
use function is_array;
use function iterator_count;
use function sprintf;
use Countable;
use EmptyIterator;
use Generator;
use Iterator;
use IteratorAggregate;
use PHPUnit\Framework\Exception;
use Traversable;




class Count extends Constraint
{



private $expectedCount;

public function __construct(int $expected)
{
$this->expectedCount = $expected;
}

public function toString(): string
{
return sprintf(
'count matches %d',
$this->expectedCount,
);
}







protected function matches($other): bool
{
return $this->expectedCount === $this->getCountOf($other);
}




protected function getCountOf($other): ?int
{
if ($other instanceof Countable || is_array($other)) {
return count($other);
}

if ($other instanceof EmptyIterator) {
return 0;
}

if ($other instanceof Traversable) {
while ($other instanceof IteratorAggregate) {
try {
$other = $other->getIterator();
} catch (\Exception $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}
}

$iterator = $other;

if ($iterator instanceof Generator) {
return $this->getCountOfGenerator($iterator);
}

if (!$iterator instanceof Iterator) {
return iterator_count($iterator);
}

$key = $iterator->key();
$count = iterator_count($iterator);



if ($key !== null) {
$iterator->rewind();

while ($iterator->valid() && $key !== $iterator->key()) {
$iterator->next();
}
}

return $count;
}

return null;
}





protected function getCountOfGenerator(Generator $generator): int
{
for ($count = 0; $generator->valid(); $generator->next()) {
$count++;
}

return $count;
}









protected function failureDescription($other): string
{
return sprintf(
'actual size %d matches expected size %d',
(int) $this->getCountOf($other),
$this->expectedCount,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class SameSize extends Count
{
public function __construct(iterable $expected)
{
parent::__construct((int) $this->getCountOf($expected));
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class LessThan extends Constraint
{



private $value;




public function __construct($value)
{
$this->value = $value;
}






public function toString(): string
{
return 'is less than ' . $this->exporter()->export($this->value);
}







protected function matches($other): bool
{
return $this->value > $other;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;






final class Callback extends Constraint
{





private $callback;


public function __construct(callable $callback)
{
$this->callback = $callback;
}




public function toString(): string
{
return 'is accepted by specified callback';
}









protected function matches($other): bool
{
return ($this->callback)($other);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_string;
use function sprintf;
use function strpos;
use function trim;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;




final class IsEqual extends Constraint
{



private $value;




private $delta;




private $canonicalize;




private $ignoreCase;

public function __construct($value, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false)
{
$this->value = $value;
$this->delta = $delta;
$this->canonicalize = $canonicalize;
$this->ignoreCase = $ignoreCase;
}













public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{



if ($this->value === $other) {
return true;
}

$comparatorFactory = ComparatorFactory::getInstance();

try {
$comparator = $comparatorFactory->getComparatorFor(
$this->value,
$other,
);

$comparator->assertEquals(
$this->value,
$other,
$this->delta,
$this->canonicalize,
$this->ignoreCase,
);
} catch (ComparisonFailure $f) {
if ($returnResult) {
return false;
}

throw new ExpectationFailedException(
trim($description . "\n" . $f->getMessage()),
$f,
);
}

return true;
}






public function toString(): string
{
$delta = '';

if (is_string($this->value)) {
if (strpos($this->value, "\n") !== false) {
return 'is equal to <text>';
}

return sprintf(
"is equal to '%s'",
$this->value,
);
}

if ($this->delta != 0) {
$delta = sprintf(
' with delta <%F>',
$this->delta,
);
}

return sprintf(
'is equal to %s%s',
$this->exporter()->export($this->value),
$delta,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_string;
use function sprintf;
use function strpos;
use function trim;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;




final class IsEqualIgnoringCase extends Constraint
{



private $value;

public function __construct($value)
{
$this->value = $value;
}













public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{



if ($this->value === $other) {
return true;
}

$comparatorFactory = ComparatorFactory::getInstance();

try {
$comparator = $comparatorFactory->getComparatorFor(
$this->value,
$other,
);

$comparator->assertEquals(
$this->value,
$other,
0.0,
false,
true,
);
} catch (ComparisonFailure $f) {
if ($returnResult) {
return false;
}

throw new ExpectationFailedException(
trim($description . "\n" . $f->getMessage()),
$f,
);
}

return true;
}






public function toString(): string
{
if (is_string($this->value)) {
if (strpos($this->value, "\n") !== false) {
return 'is equal to <text>';
}

return sprintf(
"is equal to '%s'",
$this->value,
);
}

return sprintf(
'is equal to %s',
$this->exporter()->export($this->value),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_string;
use function sprintf;
use function strpos;
use function trim;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;




final class IsEqualCanonicalizing extends Constraint
{



private $value;

public function __construct($value)
{
$this->value = $value;
}













public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{



if ($this->value === $other) {
return true;
}

$comparatorFactory = ComparatorFactory::getInstance();

try {
$comparator = $comparatorFactory->getComparatorFor(
$this->value,
$other,
);

$comparator->assertEquals(
$this->value,
$other,
0.0,
true,
false,
);
} catch (ComparisonFailure $f) {
if ($returnResult) {
return false;
}

throw new ExpectationFailedException(
trim($description . "\n" . $f->getMessage()),
$f,
);
}

return true;
}






public function toString(): string
{
if (is_string($this->value)) {
if (strpos($this->value, "\n") !== false) {
return 'is equal to <text>';
}

return sprintf(
"is equal to '%s'",
$this->value,
);
}

return sprintf(
'is equal to %s',
$this->exporter()->export($this->value),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use function trim;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;




final class IsEqualWithDelta extends Constraint
{



private $value;




private $delta;

public function __construct($value, float $delta)
{
$this->value = $value;
$this->delta = $delta;
}













public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{



if ($this->value === $other) {
return true;
}

$comparatorFactory = ComparatorFactory::getInstance();

try {
$comparator = $comparatorFactory->getComparatorFor(
$this->value,
$other,
);

$comparator->assertEquals(
$this->value,
$other,
$this->delta,
);
} catch (ComparisonFailure $f) {
if ($returnResult) {
return false;
}

throw new ExpectationFailedException(
trim($description . "\n" . $f->getMessage()),
$f,
);
}

return true;
}






public function toString(): string
{
return sprintf(
'is equal to %s with delta <%F>',
$this->exporter()->export($this->value),
$this->delta,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function preg_match;
use function sprintf;




class RegularExpression extends Constraint
{



private $pattern;

public function __construct(string $pattern)
{
$this->pattern = $pattern;
}




public function toString(): string
{
return sprintf(
'matches PCRE pattern "%s"',
$this->pattern,
);
}







protected function matches($other): bool
{
return preg_match($this->pattern, $other) > 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function json_decode;
use function json_last_error;
use function sprintf;




final class IsJson extends Constraint
{



public function toString(): string
{
return 'is valid JSON';
}







protected function matches($other): bool
{
if ($other === '') {
return false;
}

json_decode($other);

if (json_last_error()) {
return false;
}

return true;
}











protected function failureDescription($other): string
{
if ($other === '') {
return 'an empty string is valid JSON';
}

json_decode($other);
$error = (string) JsonMatchesErrorMessageProvider::determineJsonError(
(string) json_last_error(),
);

return sprintf(
'%s is valid JSON (%s)',
$this->exporter()->shortenedExport($other),
$error,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use const DIRECTORY_SEPARATOR;
use function explode;
use function implode;
use function preg_match;
use function preg_quote;
use function preg_replace;
use function strtr;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;




final class StringMatchesFormatDescription extends RegularExpression
{



private $string;

public function __construct(string $string)
{
parent::__construct(
$this->createPatternFromFormat(
$this->convertNewlines($string),
),
);

$this->string = $string;
}







protected function matches($other): bool
{
return parent::matches(
$this->convertNewlines($other),
);
}

protected function failureDescription($other): string
{
return 'string matches format description';
}

protected function additionalFailureDescription($other): string
{
$from = explode("\n", $this->string);
$to = explode("\n", $this->convertNewlines($other));

foreach ($from as $index => $line) {
if (isset($to[$index]) && $line !== $to[$index]) {
$line = $this->createPatternFromFormat($line);

if (preg_match($line, $to[$index]) > 0) {
$from[$index] = $to[$index];
}
}
}

$this->string = implode("\n", $from);
$other = implode("\n", $to);

return (new Differ(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n")))->diff($this->string, $other);
}

private function createPatternFromFormat(string $string): string
{
$string = strtr(
preg_quote($string, '/'),
[
'%%' => '%',
'%e' => '\\' . DIRECTORY_SEPARATOR,
'%s' => '[^\r\n]+',
'%S' => '[^\r\n]*',
'%a' => '.+',
'%A' => '.*',
'%w' => '\s*',
'%i' => '[+-]?\d+',
'%d' => '\d+',
'%x' => '[0-9a-fA-F]+',
'%f' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
'%c' => '.',
],
);

return '/^' . $string . '$/s';
}

private function convertNewlines(string $text): string
{
return preg_replace('/\r\n/', "\n", $text);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function strlen;
use function substr;




final class StringEndsWith extends Constraint
{



private $suffix;

public function __construct(string $suffix)
{
$this->suffix = $suffix;
}




public function toString(): string
{
return 'ends with "' . $this->suffix . '"';
}







protected function matches($other): bool
{
return substr($other, 0 - strlen($this->suffix)) === $this->suffix;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function mb_stripos;
use function mb_strtolower;
use function sprintf;
use function strpos;




final class StringContains extends Constraint
{



private $string;




private $ignoreCase;

public function __construct(string $string, bool $ignoreCase = false)
{
$this->string = $string;
$this->ignoreCase = $ignoreCase;
}




public function toString(): string
{
if ($this->ignoreCase) {
$string = mb_strtolower($this->string, 'UTF-8');
} else {
$string = $this->string;
}

return sprintf(
'contains "%s"',
$string,
);
}







protected function matches($other): bool
{
if ('' === $this->string) {
return true;
}

if ($this->ignoreCase) {




return mb_stripos($other, $this->string, 0, 'UTF-8') !== false;
}









return strpos($other, $this->string) !== false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function strpos;
use PHPUnit\Framework\InvalidArgumentException;




final class StringStartsWith extends Constraint
{



private $prefix;

public function __construct(string $prefix)
{
if ($prefix === '') {
throw InvalidArgumentException::create(1, 'non-empty string');
}

$this->prefix = $prefix;
}




public function toString(): string
{
return 'starts with "' . $this->prefix . '"';
}







protected function matches($other): bool
{
return strpos((string) $other, $this->prefix) === 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function json_decode;
use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Util\Json;
use SebastianBergmann\Comparator\ComparisonFailure;




final class JsonMatches extends Constraint
{



private $value;

public function __construct(string $value)
{
$this->value = $value;
}




public function toString(): string
{
return sprintf(
'matches JSON string "%s"',
$this->value,
);
}









protected function matches($other): bool
{
[$error, $recodedOther] = Json::canonicalize($other);

if ($error) {
return false;
}

[$error, $recodedValue] = Json::canonicalize($this->value);

if ($error) {
return false;
}

return $recodedOther == $recodedValue;
}













protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void
{
if ($comparisonFailure === null) {
[$error, $recodedOther] = Json::canonicalize($other);

if ($error) {
parent::fail($other, $description);
}

[$error, $recodedValue] = Json::canonicalize($this->value);

if ($error) {
parent::fail($other, $description);
}

$comparisonFailure = new ComparisonFailure(
json_decode($this->value),
json_decode($other),
Json::prettify($recodedValue),
Json::prettify($recodedOther),
false,
'Failed asserting that two json values are equal.',
);
}

parent::fail($other, $description, $comparisonFailure);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function get_class;
use function is_array;
use function is_object;
use function is_string;
use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;




final class IsIdentical extends Constraint
{



private $value;

public function __construct($value)
{
$this->value = $value;
}














public function evaluate($other, string $description = '', bool $returnResult = false): ?bool
{
$success = $this->value === $other;

if ($returnResult) {
return $success;
}

if (!$success) {
$f = null;


if (is_string($this->value) && is_string($other)) {
$f = new ComparisonFailure(
$this->value,
$other,
sprintf("'%s'", $this->value),
sprintf("'%s'", $other),
);
}


if (is_array($this->value) && is_array($other)) {
$f = new ComparisonFailure(
$this->value,
$other,
$this->exporter()->export($this->value),
$this->exporter()->export($other),
);
}

$this->fail($other, $description, $f);
}

return null;
}






public function toString(): string
{
if (is_object($this->value)) {
return 'is identical to an object of class "' .
get_class($this->value) . '"';
}

return 'is identical to ' . $this->exporter()->export($this->value);
}











protected function failureDescription($other): string
{
if (is_object($this->value) && is_object($other)) {
return 'two variables reference the same object';
}

if (is_string($this->value) && is_string($other)) {
return 'two strings are identical';
}

if (is_array($this->value) && is_array($other)) {
return 'two arrays are identical';
}

return parent::failureDescription($other);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function get_class;
use function is_object;
use PHPUnit\Framework\ActualValueIsNotAnObjectException;
use PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException;
use PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException;
use PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException;
use PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException;
use PHPUnit\Framework\ComparisonMethodDoesNotExistException;
use ReflectionNamedType;
use ReflectionObject;




final class ObjectEquals extends Constraint
{



private $expected;




private $method;

public function __construct(object $object, string $method = 'equals')
{
$this->expected = $object;
$this->method = $method;
}

public function toString(): string
{
return 'two objects are equal';
}









protected function matches($other): bool
{
if (!is_object($other)) {
throw new ActualValueIsNotAnObjectException;
}

$object = new ReflectionObject($other);

if (!$object->hasMethod($this->method)) {
throw new ComparisonMethodDoesNotExistException(
get_class($other),
$this->method,
);
}


$method = $object->getMethod($this->method);

if (!$method->hasReturnType()) {
throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(
get_class($other),
$this->method,
);
}

$returnType = $method->getReturnType();

if (!$returnType instanceof ReflectionNamedType) {
throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(
get_class($other),
$this->method,
);
}

if ($returnType->allowsNull()) {
throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(
get_class($other),
$this->method,
);
}

if ($returnType->getName() !== 'bool') {
throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(
get_class($other),
$this->method,
);
}

if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) {
throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException(
get_class($other),
$this->method,
);
}

$parameter = $method->getParameters()[0];

if (!$parameter->hasType()) {
throw new ComparisonMethodDoesNotDeclareParameterTypeException(
get_class($other),
$this->method,
);
}

$type = $parameter->getType();

if (!$type instanceof ReflectionNamedType) {
throw new ComparisonMethodDoesNotDeclareParameterTypeException(
get_class($other),
$this->method,
);
}

$typeName = $type->getName();

if ($typeName === 'self') {
$typeName = get_class($other);
}

if (!$this->expected instanceof $typeName) {
throw new ComparisonMethodDoesNotAcceptParameterTypeException(
get_class($other),
$this->method,
get_class($this->expected),
);
}

return $other->{$this->method}($this->expected);
}

protected function failureDescription($other): string
{
return $this->toString();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function get_class;
use function gettype;
use function is_object;
use function sprintf;
use ReflectionObject;




final class ObjectHasProperty extends Constraint
{



private $propertyName;

public function __construct(string $propertyName)
{
$this->propertyName = $propertyName;
}




public function toString(): string
{
return sprintf(
'has property "%s"',
$this->propertyName,
);
}







protected function matches($other): bool
{
if (!is_object($other)) {
return false;
}

return (new ReflectionObject($other))->hasProperty($this->propertyName);
}









protected function failureDescription($other): string
{
if (is_object($other)) {
return sprintf(
'object of class "%s" %s',
get_class($other),
$this->toString(),
);
}

return sprintf(
'"%s" (%s) %s',
$other,
gettype($other),
$this->toString(),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function get_class;
use function is_object;
use function sprintf;
use PHPUnit\Framework\Exception;
use ReflectionClass;
use ReflectionException;






class ClassHasAttribute extends Constraint
{



private $attributeName;

public function __construct(string $attributeName)
{
$this->attributeName = $attributeName;
}




public function toString(): string
{
return sprintf(
'has attribute "%s"',
$this->attributeName,
);
}







protected function matches($other): bool
{
try {
return (new ReflectionClass($other))->hasProperty($this->attributeName);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}









protected function failureDescription($other): string
{
return sprintf(
'%sclass "%s" %s',
is_object($other) ? 'object of ' : '',
is_object($other) ? get_class($other) : $other,
$this->toString(),
);
}

protected function attributeName(): string
{
return $this->attributeName;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use PHPUnit\Framework\Exception;
use ReflectionClass;
use ReflectionException;






final class ClassHasStaticAttribute extends ClassHasAttribute
{



public function toString(): string
{
return sprintf(
'has static attribute "%s"',
$this->attributeName(),
);
}







protected function matches($other): bool
{
try {
$class = new ReflectionClass($other);

if ($class->hasProperty($this->attributeName())) {
return $class->getProperty($this->attributeName())->isStatic();
}

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


return false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use ReflectionObject;






final class ObjectHasAttribute extends ClassHasAttribute
{






protected function matches($other): bool
{
return (new ReflectionObject($other))->hasProperty($this->attributeName());
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class IsFalse extends Constraint
{



public function toString(): string
{
return 'is false';
}







protected function matches($other): bool
{
return $other === false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class IsTrue extends Constraint
{



public function toString(): string
{
return 'is true';
}







protected function matches($other): bool
{
return $other === true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function gettype;
use function is_array;
use function is_bool;
use function is_callable;
use function is_float;
use function is_int;
use function is_iterable;
use function is_numeric;
use function is_object;
use function is_scalar;
use function is_string;
use function sprintf;




final class IsType extends Constraint
{



public const TYPE_ARRAY = 'array';




public const TYPE_BOOL = 'bool';




public const TYPE_FLOAT = 'float';




public const TYPE_INT = 'int';




public const TYPE_NULL = 'null';




public const TYPE_NUMERIC = 'numeric';




public const TYPE_OBJECT = 'object';




public const TYPE_RESOURCE = 'resource';




public const TYPE_CLOSED_RESOURCE = 'resource (closed)';




public const TYPE_STRING = 'string';




public const TYPE_SCALAR = 'scalar';




public const TYPE_CALLABLE = 'callable';




public const TYPE_ITERABLE = 'iterable';




private const KNOWN_TYPES = [
'array' => true,
'boolean' => true,
'bool' => true,
'double' => true,
'float' => true,
'integer' => true,
'int' => true,
'null' => true,
'numeric' => true,
'object' => true,
'real' => true,
'resource' => true,
'resource (closed)' => true,
'string' => true,
'scalar' => true,
'callable' => true,
'iterable' => true,
];




private $type;




public function __construct(string $type)
{
if (!isset(self::KNOWN_TYPES[$type])) {
throw new \PHPUnit\Framework\Exception(
sprintf(
'Type specified for PHPUnit\Framework\Constraint\IsType <%s> ' .
'is not a valid type.',
$type,
),
);
}

$this->type = $type;
}




public function toString(): string
{
return sprintf(
'is of type "%s"',
$this->type,
);
}







protected function matches($other): bool
{
switch ($this->type) {
case 'numeric':
return is_numeric($other);

case 'integer':
case 'int':
return is_int($other);

case 'double':
case 'float':
case 'real':
return is_float($other);

case 'string':
return is_string($other);

case 'boolean':
case 'bool':
return is_bool($other);

case 'null':
return null === $other;

case 'array':
return is_array($other);

case 'object':
return is_object($other);

case 'resource':
$type = gettype($other);

return $type === 'resource' || $type === 'resource (closed)';

case 'resource (closed)':
return gettype($other) === 'resource (closed)';

case 'scalar':
return is_scalar($other);

case 'callable':
return is_callable($other);

case 'iterable':
return is_iterable($other);

default:
return false;
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class IsNull extends Constraint
{



public function toString(): string
{
return 'is null';
}







protected function matches($other): bool
{
return $other === null;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use ReflectionClass;
use ReflectionException;




final class IsInstanceOf extends Constraint
{



private $className;

public function __construct(string $className)
{
$this->className = $className;
}




public function toString(): string
{
return sprintf(
'is instance of %s "%s"',
$this->getType(),
$this->className,
);
}







protected function matches($other): bool
{
return $other instanceof $this->className;
}











protected function failureDescription($other): string
{
return sprintf(
'%s is an instance of %s "%s"',
$this->exporter()->shortenedExport($other),
$this->getType(),
$this->className,
);
}

private function getType(): string
{
try {
$reflection = new ReflectionClass($this->className);

if ($reflection->isInterface()) {
return 'interface';
}
} catch (ReflectionException $e) {
}

return 'class';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use Exception;
use PHPUnit\Util\RegularExpression as RegularExpressionUtil;




final class ExceptionMessageRegularExpression extends Constraint
{



private $expectedMessageRegExp;

public function __construct(string $expected)
{
$this->expectedMessageRegExp = $expected;
}

public function toString(): string
{
return 'exception message matches ';
}










protected function matches($other): bool
{
$match = RegularExpressionUtil::safeMatch($this->expectedMessageRegExp, $other->getMessage());

if ($match === false) {
throw new \PHPUnit\Framework\Exception(
"Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'",
);
}

return $match === 1;
}









protected function failureDescription($other): string
{
return sprintf(
"exception message '%s' matches '%s'",
$other->getMessage(),
$this->expectedMessageRegExp,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function get_class;
use function sprintf;
use PHPUnit\Util\Filter;
use Throwable;




final class Exception extends Constraint
{



private $className;

public function __construct(string $className)
{
$this->className = $className;
}




public function toString(): string
{
return sprintf(
'exception of type "%s"',
$this->className,
);
}







protected function matches($other): bool
{
return $other instanceof $this->className;
}









protected function failureDescription($other): string
{
if ($other !== null) {
$message = '';

if ($other instanceof Throwable) {
$message = '. Message was: "' . $other->getMessage() . '" at'
. "\n" . Filter::getFilteredStacktrace($other);
}

return sprintf(
'exception of type "%s" matches expected exception "%s"%s',
get_class($other),
$this->className,
$message,
);
}

return sprintf(
'exception of type "%s" is thrown',
$this->className,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use Throwable;




final class ExceptionCode extends Constraint
{



private $expectedCode;




public function __construct($expected)
{
$this->expectedCode = $expected;
}

public function toString(): string
{
return 'exception code is ';
}







protected function matches($other): bool
{
return (string) $other->getCode() === (string) $this->expectedCode;
}











protected function failureDescription($other): string
{
return sprintf(
'%s is equal to expected exception code %s',
$this->exporter()->export($other->getCode()),
$this->exporter()->export($this->expectedCode),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function sprintf;
use function strpos;
use Throwable;




final class ExceptionMessage extends Constraint
{



private $expectedMessage;

public function __construct(string $expected)
{
$this->expectedMessage = $expected;
}

public function toString(): string
{
if ($this->expectedMessage === '') {
return 'exception message is empty';
}

return 'exception message contains ';
}







protected function matches($other): bool
{
if ($this->expectedMessage === '') {
return $other->getMessage() === '';
}

return strpos((string) $other->getMessage(), $this->expectedMessage) !== false;
}









protected function failureDescription($other): string
{
if ($this->expectedMessage === '') {
return sprintf(
"exception message is empty but is '%s'",
$other->getMessage(),
);
}

return sprintf(
"exception message '%s' contains '%s'",
$other->getMessage(),
$this->expectedMessage,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_nan;




final class IsNan extends Constraint
{



public function toString(): string
{
return 'is nan';
}







protected function matches($other): bool
{
return is_nan($other);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_finite;




final class IsFinite extends Constraint
{



public function toString(): string
{
return 'is finite';
}







protected function matches($other): bool
{
return is_finite($other);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function is_infinite;




final class IsInfinite extends Constraint
{



public function toString(): string
{
return 'is infinite';
}







protected function matches($other): bool
{
return is_infinite($other);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class LogicalAnd extends BinaryOperator
{



public function operator(): string
{
return 'and';
}






public function precedence(): int
{
return 22;
}







protected function matches($other): bool
{
foreach ($this->constraints() as $constraint) {
if (!$constraint->evaluate($other, '', true)) {
return false;
}
}

return [] !== $this->constraints();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




abstract class Operator extends Constraint
{



abstract public function operator(): string;






abstract public function precedence(): int;




abstract public function arity(): int;




protected function checkConstraint($constraint): Constraint
{
if (!$constraint instanceof Constraint) {
return new IsEqual($constraint);
}

return $constraint;
}




protected function constraintNeedsParentheses(Constraint $constraint): bool
{
return $constraint instanceof self &&
$constraint->arity() > 1 &&
$this->precedence() <= $constraint->precedence();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function array_map;
use function count;
use function preg_match;
use function preg_quote;
use function preg_replace;




final class LogicalNot extends UnaryOperator
{
public static function negate(string $string): string
{
$positives = [
'contains ',
'exists',
'has ',
'is ',
'are ',
'matches ',
'starts with ',
'ends with ',
'reference ',
'not not ',
];

$negatives = [
'does not contain ',
'does not exist',
'does not have ',
'is not ',
'are not ',
'does not match ',
'starts not with ',
'ends not with ',
'don\'t reference ',
'not ',
];

preg_match('/(\'[\w\W]*\')([\w\W]*)("[\w\W]*")/i', $string, $matches);

if (count($matches) === 0) {
preg_match('/(\'[\w\W]*\')([\w\W]*)(\'[\w\W]*\')/i', $string, $matches);
}

$positives = array_map(
static function (string $s)
{
return '/\\b' . preg_quote($s, '/') . '/';
},
$positives,
);

if (count($matches) > 0) {
$nonInput = $matches[2];

$negatedString = preg_replace(
'/' . preg_quote($nonInput, '/') . '/',
preg_replace(
$positives,
$negatives,
$nonInput,
),
$string,
);
} else {
$negatedString = preg_replace(
$positives,
$negatives,
$string,
);
}

return $negatedString;
}




public function operator(): string
{
return 'not';
}






public function precedence(): int
{
return 5;
}







protected function matches($other): bool
{
return !$this->constraint()->evaluate($other, '', true);
}





protected function transformString(string $string): string
{
return self::negate($string);
}








protected function reduce(): Constraint
{
$constraint = $this->constraint();

if ($constraint instanceof self) {
return $constraint->constraint()->reduce();
}

return parent::reduce();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function array_map;
use function array_values;
use function count;




abstract class BinaryOperator extends Operator
{



private $constraints = [];

public static function fromConstraints(Constraint ...$constraints): self
{
$constraint = new static;

$constraint->constraints = $constraints;

return $constraint;
}




public function setConstraints(array $constraints): void
{
$this->constraints = array_map(function ($constraint): Constraint
{
return $this->checkConstraint($constraint);
}, array_values($constraints));
}




final public function arity(): int
{
return count($this->constraints);
}




public function toString(): string
{
$reduced = $this->reduce();

if ($reduced !== $this) {
return $reduced->toString();
}

$text = '';

foreach ($this->constraints as $key => $constraint) {
$constraint = $constraint->reduce();

$text .= $this->constraintToString($constraint, $key);
}

return $text;
}




public function count(): int
{
$count = 0;

foreach ($this->constraints as $constraint) {
$count += count($constraint);
}

return $count;
}




final protected function constraints(): array
{
return $this->constraints;
}




final protected function constraintNeedsParentheses(Constraint $constraint): bool
{
return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint);
}








protected function reduce(): Constraint
{
if ($this->arity() === 1 && $this->constraints[0] instanceof Operator) {
return $this->constraints[0]->reduce();
}

return parent::reduce();
}







private function constraintToString(Constraint $constraint, int $position): string
{
$prefix = '';

if ($position > 0) {
$prefix = (' ' . $this->operator() . ' ');
}

if ($this->constraintNeedsParentheses($constraint)) {
return $prefix . '( ' . $constraint->toString() . ' )';
}

$string = $constraint->toStringInContext($this, $position);

if ($string === '') {
$string = $constraint->toString();
}

return $prefix . $string;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function count;




abstract class UnaryOperator extends Operator
{



private $constraint;




public function __construct($constraint)
{
$this->constraint = $this->checkConstraint($constraint);
}




public function arity(): int
{
return 1;
}




public function toString(): string
{
$reduced = $this->reduce();

if ($reduced !== $this) {
return $reduced->toString();
}

$constraint = $this->constraint->reduce();

if ($this->constraintNeedsParentheses($constraint)) {
return $this->operator() . '( ' . $constraint->toString() . ' )';
}

$string = $constraint->toStringInContext($this, 0);

if ($string === '') {
return $this->transformString($constraint->toString());
}

return $string;
}




public function count(): int
{
return count($this->constraint);
}











protected function failureDescription($other): string
{
$reduced = $this->reduce();

if ($reduced !== $this) {
return $reduced->failureDescription($other);
}

$constraint = $this->constraint->reduce();

if ($this->constraintNeedsParentheses($constraint)) {
return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )';
}

$string = $constraint->failureDescriptionInContext($this, 0, $other);

if ($string === '') {
return $this->transformString($constraint->failureDescription($other));
}

return $string;
}












protected function transformString(string $string): string
{
return $string;
}




final protected function constraint(): Constraint
{
return $this->constraint;
}




protected function constraintNeedsParentheses(Constraint $constraint): bool
{
$constraint = $constraint->reduce();

return $constraint instanceof self || parent::constraintNeedsParentheses($constraint);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;




final class LogicalOr extends BinaryOperator
{



public function operator(): string
{
return 'or';
}






public function precedence(): int
{
return 24;
}







public function matches($other): bool
{
foreach ($this->constraints() as $constraint) {
if ($constraint->evaluate($other, '', true)) {
return true;
}
}

return false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Constraint;

use function array_reduce;
use function array_shift;




final class LogicalXor extends BinaryOperator
{



public function operator(): string
{
return 'xor';
}






public function precedence(): int
{
return 23;
}







public function matches($other): bool
{
$constraints = $this->constraints();

$initial = array_shift($constraints);

if ($initial === null) {
return false;
}

return array_reduce(
$constraints,
static function (bool $matches, Constraint $constraint) use ($other): bool
{
return $matches xor $constraint->evaluate($other, '', true);
},
$initial->evaluate($other, '', true),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




interface SelfDescribing
{



public function toString(): string;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const DEBUG_BACKTRACE_IGNORE_ARGS;
use const PHP_EOL;
use function array_shift;
use function array_unshift;
use function assert;
use function class_exists;
use function count;
use function debug_backtrace;
use function explode;
use function file_get_contents;
use function func_get_args;
use function implode;
use function interface_exists;
use function is_array;
use function is_bool;
use function is_int;
use function is_iterable;
use function is_object;
use function is_string;
use function preg_match;
use function preg_split;
use function sprintf;
use function strpos;
use ArrayAccess;
use Countable;
use DOMAttr;
use DOMDocument;
use DOMElement;
use Generator;
use PHPUnit\Framework\Constraint\ArrayHasKey;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\ClassHasAttribute;
use PHPUnit\Framework\Constraint\ClassHasStaticAttribute;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\Count;
use PHPUnit\Framework\Constraint\DirectoryExists;
use PHPUnit\Framework\Constraint\FileExists;
use PHPUnit\Framework\Constraint\GreaterThan;
use PHPUnit\Framework\Constraint\IsAnything;
use PHPUnit\Framework\Constraint\IsEmpty;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\Constraint\IsEqualCanonicalizing;
use PHPUnit\Framework\Constraint\IsEqualIgnoringCase;
use PHPUnit\Framework\Constraint\IsEqualWithDelta;
use PHPUnit\Framework\Constraint\IsFalse;
use PHPUnit\Framework\Constraint\IsFinite;
use PHPUnit\Framework\Constraint\IsIdentical;
use PHPUnit\Framework\Constraint\IsInfinite;
use PHPUnit\Framework\Constraint\IsInstanceOf;
use PHPUnit\Framework\Constraint\IsJson;
use PHPUnit\Framework\Constraint\IsNan;
use PHPUnit\Framework\Constraint\IsNull;
use PHPUnit\Framework\Constraint\IsReadable;
use PHPUnit\Framework\Constraint\IsTrue;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\Constraint\IsWritable;
use PHPUnit\Framework\Constraint\JsonMatches;
use PHPUnit\Framework\Constraint\LessThan;
use PHPUnit\Framework\Constraint\LogicalAnd;
use PHPUnit\Framework\Constraint\LogicalNot;
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\Constraint\LogicalXor;
use PHPUnit\Framework\Constraint\ObjectEquals;
use PHPUnit\Framework\Constraint\ObjectHasAttribute;
use PHPUnit\Framework\Constraint\ObjectHasProperty;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\SameSize;
use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit\Framework\Constraint\StringEndsWith;
use PHPUnit\Framework\Constraint\StringMatchesFormatDescription;
use PHPUnit\Framework\Constraint\StringStartsWith;
use PHPUnit\Framework\Constraint\TraversableContainsEqual;
use PHPUnit\Framework\Constraint\TraversableContainsIdentical;
use PHPUnit\Framework\Constraint\TraversableContainsOnly;
use PHPUnit\Util\Type;
use PHPUnit\Util\Xml;
use PHPUnit\Util\Xml\Loader as XmlLoader;




abstract class Assert
{



private static $count = 0;











public static function assertArrayHasKey($key, $array, string $message = ''): void
{
if (!(is_int($key) || is_string($key))) {
throw InvalidArgumentException::create(
1,
'integer or string',
);
}

if (!(is_array($array) || $array instanceof ArrayAccess)) {
throw InvalidArgumentException::create(
2,
'array or ArrayAccess',
);
}

$constraint = new ArrayHasKey($key);

static::assertThat($array, $constraint, $message);
}











public static function assertArrayNotHasKey($key, $array, string $message = ''): void
{
if (!(is_int($key) || is_string($key))) {
throw InvalidArgumentException::create(
1,
'integer or string',
);
}

if (!(is_array($array) || $array instanceof ArrayAccess)) {
throw InvalidArgumentException::create(
2,
'array or ArrayAccess',
);
}

$constraint = new LogicalNot(
new ArrayHasKey($key),
);

static::assertThat($array, $constraint, $message);
}








public static function assertContains($needle, iterable $haystack, string $message = ''): void
{
$constraint = new TraversableContainsIdentical($needle);

static::assertThat($haystack, $constraint, $message);
}

public static function assertContainsEquals($needle, iterable $haystack, string $message = ''): void
{
$constraint = new TraversableContainsEqual($needle);

static::assertThat($haystack, $constraint, $message);
}








public static function assertNotContains($needle, iterable $haystack, string $message = ''): void
{
$constraint = new LogicalNot(
new TraversableContainsIdentical($needle),
);

static::assertThat($haystack, $constraint, $message);
}

public static function assertNotContainsEquals($needle, iterable $haystack, string $message = ''): void
{
$constraint = new LogicalNot(new TraversableContainsEqual($needle));

static::assertThat($haystack, $constraint, $message);
}







public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
if ($isNativeType === null) {
$isNativeType = Type::isType($type);
}

static::assertThat(
$haystack,
new TraversableContainsOnly(
$type,
$isNativeType,
),
$message,
);
}







public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void
{
static::assertThat(
$haystack,
new TraversableContainsOnly(
$className,
false,
),
$message,
);
}







public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
if ($isNativeType === null) {
$isNativeType = Type::isType($type);
}

static::assertThat(
$haystack,
new LogicalNot(
new TraversableContainsOnly(
$type,
$isNativeType,
),
),
$message,
);
}










public static function assertCount(int $expectedCount, $haystack, string $message = ''): void
{
if ($haystack instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $haystack parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

if (!$haystack instanceof Countable && !is_iterable($haystack)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}

static::assertThat(
$haystack,
new Count($expectedCount),
$message,
);
}










public static function assertNotCount(int $expectedCount, $haystack, string $message = ''): void
{
if ($haystack instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $haystack parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

if (!$haystack instanceof Countable && !is_iterable($haystack)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}

$constraint = new LogicalNot(
new Count($expectedCount),
);

static::assertThat($haystack, $constraint, $message);
}







public static function assertEquals($expected, $actual, string $message = ''): void
{
$constraint = new IsEqual($expected);

static::assertThat($actual, $constraint, $message);
}







public static function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
$constraint = new IsEqualCanonicalizing($expected);

static::assertThat($actual, $constraint, $message);
}







public static function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
$constraint = new IsEqualIgnoringCase($expected);

static::assertThat($actual, $constraint, $message);
}







public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
$constraint = new IsEqualWithDelta(
$expected,
$delta,
);

static::assertThat($actual, $constraint, $message);
}







public static function assertNotEquals($expected, $actual, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqual($expected),
);

static::assertThat($actual, $constraint, $message);
}







public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqualCanonicalizing($expected),
);

static::assertThat($actual, $constraint, $message);
}







public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqualIgnoringCase($expected),
);

static::assertThat($actual, $constraint, $message);
}







public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqualWithDelta(
$expected,
$delta,
),
);

static::assertThat($actual, $constraint, $message);
}




public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void
{
static::assertThat(
$actual,
static::objectEquals($expected, $method),
$message,
);
}









public static function assertEmpty($actual, string $message = ''): void
{
if ($actual instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

static::assertThat($actual, static::isEmpty(), $message);
}









public static function assertNotEmpty($actual, string $message = ''): void
{
if ($actual instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

static::assertThat($actual, static::logicalNot(static::isEmpty()), $message);
}







public static function assertGreaterThan($expected, $actual, string $message = ''): void
{
static::assertThat($actual, static::greaterThan($expected), $message);
}







public static function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void
{
static::assertThat(
$actual,
static::greaterThanOrEqual($expected),
$message,
);
}







public static function assertLessThan($expected, $actual, string $message = ''): void
{
static::assertThat($actual, static::lessThan($expected), $message);
}







public static function assertLessThanOrEqual($expected, $actual, string $message = ''): void
{
static::assertThat($actual, static::lessThanOrEqual($expected), $message);
}








public static function assertFileEquals(string $expected, string $actual, string $message = ''): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);

$constraint = new IsEqual(file_get_contents($expected));

static::assertThat(file_get_contents($actual), $constraint, $message);
}








public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);

$constraint = new IsEqualCanonicalizing(
file_get_contents($expected),
);

static::assertThat(file_get_contents($actual), $constraint, $message);
}








public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);

$constraint = new IsEqualIgnoringCase(file_get_contents($expected));

static::assertThat(file_get_contents($actual), $constraint, $message);
}








public static function assertFileNotEquals(string $expected, string $actual, string $message = ''): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);

$constraint = new LogicalNot(
new IsEqual(file_get_contents($expected)),
);

static::assertThat(file_get_contents($actual), $constraint, $message);
}








public static function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);

$constraint = new LogicalNot(
new IsEqualCanonicalizing(file_get_contents($expected)),
);

static::assertThat(file_get_contents($actual), $constraint, $message);
}








public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);

$constraint = new LogicalNot(
new IsEqualIgnoringCase(file_get_contents($expected)),
);

static::assertThat(file_get_contents($actual), $constraint, $message);
}








public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);

$constraint = new IsEqual(file_get_contents($expectedFile));

static::assertThat($actualString, $constraint, $message);
}








public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);

$constraint = new IsEqualCanonicalizing(file_get_contents($expectedFile));

static::assertThat($actualString, $constraint, $message);
}








public static function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);

$constraint = new IsEqualIgnoringCase(file_get_contents($expectedFile));

static::assertThat($actualString, $constraint, $message);
}








public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);

$constraint = new LogicalNot(
new IsEqual(file_get_contents($expectedFile)),
);

static::assertThat($actualString, $constraint, $message);
}








public static function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);

$constraint = new LogicalNot(
new IsEqualCanonicalizing(file_get_contents($expectedFile)),
);

static::assertThat($actualString, $constraint, $message);
}








public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);

$constraint = new LogicalNot(
new IsEqualIgnoringCase(file_get_contents($expectedFile)),
);

static::assertThat($actualString, $constraint, $message);
}







public static function assertIsReadable(string $filename, string $message = ''): void
{
static::assertThat($filename, new IsReadable, $message);
}







public static function assertIsNotReadable(string $filename, string $message = ''): void
{
static::assertThat($filename, new LogicalNot(new IsReadable), $message);
}











public static function assertNotIsReadable(string $filename, string $message = ''): void
{
self::createWarning('assertNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotReadable() instead.');

static::assertThat($filename, new LogicalNot(new IsReadable), $message);
}







public static function assertIsWritable(string $filename, string $message = ''): void
{
static::assertThat($filename, new IsWritable, $message);
}







public static function assertIsNotWritable(string $filename, string $message = ''): void
{
static::assertThat($filename, new LogicalNot(new IsWritable), $message);
}











public static function assertNotIsWritable(string $filename, string $message = ''): void
{
self::createWarning('assertNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotWritable() instead.');

static::assertThat($filename, new LogicalNot(new IsWritable), $message);
}







public static function assertDirectoryExists(string $directory, string $message = ''): void
{
static::assertThat($directory, new DirectoryExists, $message);
}







public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void
{
static::assertThat($directory, new LogicalNot(new DirectoryExists), $message);
}











public static function assertDirectoryNotExists(string $directory, string $message = ''): void
{
self::createWarning('assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.');

static::assertThat($directory, new LogicalNot(new DirectoryExists), $message);
}







public static function assertDirectoryIsReadable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertIsReadable($directory, $message);
}







public static function assertDirectoryIsNotReadable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertIsNotReadable($directory, $message);
}











public static function assertDirectoryNotIsReadable(string $directory, string $message = ''): void
{
self::createWarning('assertDirectoryNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotReadable() instead.');

self::assertDirectoryExists($directory, $message);
self::assertIsNotReadable($directory, $message);
}







public static function assertDirectoryIsWritable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertIsWritable($directory, $message);
}







public static function assertDirectoryIsNotWritable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertIsNotWritable($directory, $message);
}











public static function assertDirectoryNotIsWritable(string $directory, string $message = ''): void
{
self::createWarning('assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.');

self::assertDirectoryExists($directory, $message);
self::assertIsNotWritable($directory, $message);
}







public static function assertFileExists(string $filename, string $message = ''): void
{
static::assertThat($filename, new FileExists, $message);
}







public static function assertFileDoesNotExist(string $filename, string $message = ''): void
{
static::assertThat($filename, new LogicalNot(new FileExists), $message);
}











public static function assertFileNotExists(string $filename, string $message = ''): void
{
self::createWarning('assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.');

static::assertThat($filename, new LogicalNot(new FileExists), $message);
}







public static function assertFileIsReadable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertIsReadable($file, $message);
}







public static function assertFileIsNotReadable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertIsNotReadable($file, $message);
}











public static function assertFileNotIsReadable(string $file, string $message = ''): void
{
self::createWarning('assertFileNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotReadable() instead.');

self::assertFileExists($file, $message);
self::assertIsNotReadable($file, $message);
}







public static function assertFileIsWritable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertIsWritable($file, $message);
}







public static function assertFileIsNotWritable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertIsNotWritable($file, $message);
}











public static function assertFileNotIsWritable(string $file, string $message = ''): void
{
self::createWarning('assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.');

self::assertFileExists($file, $message);
self::assertIsNotWritable($file, $message);
}









public static function assertTrue($condition, string $message = ''): void
{
static::assertThat($condition, static::isTrue(), $message);
}









public static function assertNotTrue($condition, string $message = ''): void
{
static::assertThat($condition, static::logicalNot(static::isTrue()), $message);
}









public static function assertFalse($condition, string $message = ''): void
{
static::assertThat($condition, static::isFalse(), $message);
}









public static function assertNotFalse($condition, string $message = ''): void
{
static::assertThat($condition, static::logicalNot(static::isFalse()), $message);
}









public static function assertNull($actual, string $message = ''): void
{
static::assertThat($actual, static::isNull(), $message);
}









public static function assertNotNull($actual, string $message = ''): void
{
static::assertThat($actual, static::logicalNot(static::isNull()), $message);
}







public static function assertFinite($actual, string $message = ''): void
{
static::assertThat($actual, static::isFinite(), $message);
}







public static function assertInfinite($actual, string $message = ''): void
{
static::assertThat($actual, static::isInfinite(), $message);
}







public static function assertNan($actual, string $message = ''): void
{
static::assertThat($actual, static::isNan(), $message);
}










public static function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void
{
self::createWarning('assertClassHasAttribute() is deprecated and will be removed in PHPUnit 10.');

if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}

if (!class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}

static::assertThat($className, new ClassHasAttribute($attributeName), $message);
}










public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void
{
self::createWarning('assertClassNotHasAttribute() is deprecated and will be removed in PHPUnit 10.');

if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}

if (!class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}

static::assertThat(
$className,
new LogicalNot(
new ClassHasAttribute($attributeName),
),
$message,
);
}










public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
self::createWarning('assertClassHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.');

if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}

if (!class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}

static::assertThat(
$className,
new ClassHasStaticAttribute($attributeName),
$message,
);
}










public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
self::createWarning('assertClassNotHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.');

if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}

if (!class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}

static::assertThat(
$className,
new LogicalNot(
new ClassHasStaticAttribute($attributeName),
),
$message,
);
}












public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void
{
self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() instead.');

if (!self::isValidObjectAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}

if (!is_object($object)) {
throw InvalidArgumentException::create(2, 'object');
}

static::assertThat(
$object,
new ObjectHasAttribute($attributeName),
$message,
);
}












public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void
{
self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() instead.');

if (!self::isValidObjectAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}

if (!is_object($object)) {
throw InvalidArgumentException::create(2, 'object');
}

static::assertThat(
$object,
new LogicalNot(
new ObjectHasAttribute($attributeName),
),
$message,
);
}






final public static function assertObjectHasProperty(string $propertyName, object $object, string $message = ''): void
{
static::assertThat(
$object,
new ObjectHasProperty($propertyName),
$message,
);
}






final public static function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void
{
static::assertThat(
$object,
new LogicalNot(
new ObjectHasProperty($propertyName),
),
$message,
);
}















public static function assertSame($expected, $actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsIdentical($expected),
$message,
);
}









public static function assertNotSame($expected, $actual, string $message = ''): void
{
if (is_bool($expected) && is_bool($actual)) {
static::assertNotEquals($expected, $actual, $message);
}

static::assertThat(
$actual,
new LogicalNot(
new IsIdentical($expected),
),
$message,
);
}














public static function assertInstanceOf(string $expected, $actual, string $message = ''): void
{
if (!class_exists($expected) && !interface_exists($expected)) {
throw InvalidArgumentException::create(1, 'class or interface name');
}

static::assertThat(
$actual,
new IsInstanceOf($expected),
$message,
);
}














public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void
{
if (!class_exists($expected) && !interface_exists($expected)) {
throw InvalidArgumentException::create(1, 'class or interface name');
}

static::assertThat(
$actual,
new LogicalNot(
new IsInstanceOf($expected),
),
$message,
);
}









public static function assertIsArray($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_ARRAY),
$message,
);
}









public static function assertIsBool($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_BOOL),
$message,
);
}









public static function assertIsFloat($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_FLOAT),
$message,
);
}









public static function assertIsInt($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_INT),
$message,
);
}









public static function assertIsNumeric($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_NUMERIC),
$message,
);
}









public static function assertIsObject($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_OBJECT),
$message,
);
}









public static function assertIsResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_RESOURCE),
$message,
);
}









public static function assertIsClosedResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_CLOSED_RESOURCE),
$message,
);
}









public static function assertIsString($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_STRING),
$message,
);
}









public static function assertIsScalar($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_SCALAR),
$message,
);
}









public static function assertIsCallable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_CALLABLE),
$message,
);
}









public static function assertIsIterable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_ITERABLE),
$message,
);
}









public static function assertIsNotArray($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_ARRAY)),
$message,
);
}









public static function assertIsNotBool($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_BOOL)),
$message,
);
}









public static function assertIsNotFloat($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_FLOAT)),
$message,
);
}









public static function assertIsNotInt($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_INT)),
$message,
);
}









public static function assertIsNotNumeric($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_NUMERIC)),
$message,
);
}









public static function assertIsNotObject($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_OBJECT)),
$message,
);
}









public static function assertIsNotResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_RESOURCE)),
$message,
);
}









public static function assertIsNotClosedResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)),
$message,
);
}









public static function assertIsNotString($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_STRING)),
$message,
);
}









public static function assertIsNotScalar($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_SCALAR)),
$message,
);
}









public static function assertIsNotCallable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_CALLABLE)),
$message,
);
}









public static function assertIsNotIterable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_ITERABLE)),
$message,
);
}







public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void
{
static::assertThat($string, new RegularExpression($pattern), $message);
}











public static function assertRegExp(string $pattern, string $string, string $message = ''): void
{
self::createWarning('assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.');

static::assertThat($string, new RegularExpression($pattern), $message);
}







public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new RegularExpression($pattern),
),
$message,
);
}











public static function assertNotRegExp(string $pattern, string $string, string $message = ''): void
{
self::createWarning('assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.');

static::assertThat(
$string,
new LogicalNot(
new RegularExpression($pattern),
),
$message,
);
}












public static function assertSameSize($expected, $actual, string $message = ''): void
{
if ($expected instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

if ($actual instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

if (!$expected instanceof Countable && !is_iterable($expected)) {
throw InvalidArgumentException::create(1, 'countable or iterable');
}

if (!$actual instanceof Countable && !is_iterable($actual)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}

static::assertThat(
$actual,
new SameSize($expected),
$message,
);
}












public static function assertNotSameSize($expected, $actual, string $message = ''): void
{
if ($expected instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $expected parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

if ($actual instanceof Generator) {
self::createWarning('Passing an argument of type Generator for the $actual parameter is deprecated. Support for this will be removed in PHPUnit 10.');
}

if (!$expected instanceof Countable && !is_iterable($expected)) {
throw InvalidArgumentException::create(1, 'countable or iterable');
}

if (!$actual instanceof Countable && !is_iterable($actual)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}

static::assertThat(
$actual,
new LogicalNot(
new SameSize($expected),
),
$message,
);
}







public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void
{
static::assertThat($string, new StringMatchesFormatDescription($format), $message);
}







public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new StringMatchesFormatDescription($format),
),
$message,
);
}







public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
static::assertFileExists($formatFile, $message);

static::assertThat(
$string,
new StringMatchesFormatDescription(
file_get_contents($formatFile),
),
$message,
);
}







public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
static::assertFileExists($formatFile, $message);

static::assertThat(
$string,
new LogicalNot(
new StringMatchesFormatDescription(
file_get_contents($formatFile),
),
),
$message,
);
}







public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void
{
static::assertThat($string, new StringStartsWith($prefix), $message);
}










public static function assertStringStartsNotWith($prefix, $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new StringStartsWith($prefix),
),
$message,
);
}





public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void
{
$constraint = new StringContains($needle, false);

static::assertThat($haystack, $constraint, $message);
}





public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
$constraint = new StringContains($needle, true);

static::assertThat($haystack, $constraint, $message);
}





public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void
{
$constraint = new LogicalNot(new StringContains($needle));

static::assertThat($haystack, $constraint, $message);
}





public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
$constraint = new LogicalNot(new StringContains($needle, true));

static::assertThat($haystack, $constraint, $message);
}







public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void
{
static::assertThat($string, new StringEndsWith($suffix), $message);
}







public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new StringEndsWith($suffix),
),
$message,
);
}








public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
$expected = (new XmlLoader)->loadFile($expectedFile);
$actual = (new XmlLoader)->loadFile($actualFile);

static::assertEquals($expected, $actual, $message);
}








public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
$expected = (new XmlLoader)->loadFile($expectedFile);
$actual = (new XmlLoader)->loadFile($actualFile);

static::assertNotEquals($expected, $actual, $message);
}










public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
if (!is_string($actualXml)) {
self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.');

$actual = $actualXml;
} else {
$actual = (new XmlLoader)->load($actualXml);
}

$expected = (new XmlLoader)->loadFile($expectedFile);

static::assertEquals($expected, $actual, $message);
}










public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
if (!is_string($actualXml)) {
self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.');

$actual = $actualXml;
} else {
$actual = (new XmlLoader)->load($actualXml);
}

$expected = (new XmlLoader)->loadFile($expectedFile);

static::assertNotEquals($expected, $actual, $message);
}











public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
if (!is_string($expectedXml)) {
self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.');

$expected = $expectedXml;
} else {
$expected = (new XmlLoader)->load($expectedXml);
}

if (!is_string($actualXml)) {
self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.');

$actual = $actualXml;
} else {
$actual = (new XmlLoader)->load($actualXml);
}

static::assertEquals($expected, $actual, $message);
}











public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
if (!is_string($expectedXml)) {
self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.');

$expected = $expectedXml;
} else {
$expected = (new XmlLoader)->load($expectedXml);
}

if (!is_string($actualXml)) {
self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.');

$actual = $actualXml;
} else {
$actual = (new XmlLoader)->load($actualXml);
}

static::assertNotEquals($expected, $actual, $message);
}












public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void
{
self::createWarning('assertEqualXMLStructure() is deprecated and will be removed in PHPUnit 10.');

$expectedElement = Xml::import($expectedElement);
$actualElement = Xml::import($actualElement);

static::assertSame(
$expectedElement->tagName,
$actualElement->tagName,
$message,
);

if ($checkAttributes) {
static::assertSame(
$expectedElement->attributes->length,
$actualElement->attributes->length,
sprintf(
'%s%sNumber of attributes on node "%s" does not match',
$message,
!empty($message) ? "\n" : '',
$expectedElement->tagName,
),
);

for ($i = 0; $i < $expectedElement->attributes->length; $i++) {
$expectedAttribute = $expectedElement->attributes->item($i);
$actualAttribute = $actualElement->attributes->getNamedItem($expectedAttribute->name);

assert($expectedAttribute instanceof DOMAttr);

if (!$actualAttribute) {
static::fail(
sprintf(
'%s%sCould not find attribute "%s" on node "%s"',
$message,
!empty($message) ? "\n" : '',
$expectedAttribute->name,
$expectedElement->tagName,
),
);
}
}
}

Xml::removeCharacterDataNodes($expectedElement);
Xml::removeCharacterDataNodes($actualElement);

static::assertSame(
$expectedElement->childNodes->length,
$actualElement->childNodes->length,
sprintf(
'%s%sNumber of child nodes of "%s" differs',
$message,
!empty($message) ? "\n" : '',
$expectedElement->tagName,
),
);

for ($i = 0; $i < $expectedElement->childNodes->length; $i++) {
static::assertEqualXMLStructure(
$expectedElement->childNodes->item($i),
$actualElement->childNodes->item($i),
$checkAttributes,
$message,
);
}
}







public static function assertThat($value, Constraint $constraint, string $message = ''): void
{
self::$count += count($constraint);

$constraint->evaluate($value, $message);
}







public static function assertJson(string $actualJson, string $message = ''): void
{
static::assertThat($actualJson, static::isJson(), $message);
}







public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void
{
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);

static::assertThat($actualJson, new JsonMatches($expectedJson), $message);
}










public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void
{
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);

static::assertThat(
$actualJson,
new LogicalNot(
new JsonMatches($expectedJson),
),
$message,
);
}







public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
$expectedJson = file_get_contents($expectedFile);

static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);

static::assertThat($actualJson, new JsonMatches($expectedJson), $message);
}







public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
$expectedJson = file_get_contents($expectedFile);

static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);

static::assertThat(
$actualJson,
new LogicalNot(
new JsonMatches($expectedJson),
),
$message,
);
}







public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
static::assertFileExists($actualFile, $message);

$actualJson = file_get_contents($actualFile);
$expectedJson = file_get_contents($expectedFile);

static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);

$constraintExpected = new JsonMatches(
$expectedJson,
);

$constraintActual = new JsonMatches($actualJson);

static::assertThat($expectedJson, $constraintActual, $message);
static::assertThat($actualJson, $constraintExpected, $message);
}







public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
static::assertFileExists($actualFile, $message);

$actualJson = file_get_contents($actualFile);
$expectedJson = file_get_contents($expectedFile);

static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);

$constraintExpected = new JsonMatches(
$expectedJson,
);

$constraintActual = new JsonMatches($actualJson);

static::assertThat($expectedJson, new LogicalNot($constraintActual), $message);
static::assertThat($actualJson, new LogicalNot($constraintExpected), $message);
}




public static function logicalAnd(): LogicalAnd
{
$constraints = func_get_args();

$constraint = new LogicalAnd;
$constraint->setConstraints($constraints);

return $constraint;
}

public static function logicalOr(): LogicalOr
{
$constraints = func_get_args();

$constraint = new LogicalOr;
$constraint->setConstraints($constraints);

return $constraint;
}

public static function logicalNot(Constraint $constraint): LogicalNot
{
return new LogicalNot($constraint);
}

public static function logicalXor(): LogicalXor
{
$constraints = func_get_args();

$constraint = new LogicalXor;
$constraint->setConstraints($constraints);

return $constraint;
}

public static function anything(): IsAnything
{
return new IsAnything;
}

public static function isTrue(): IsTrue
{
return new IsTrue;
}








public static function callback(callable $callback): Callback
{
return new Callback($callback);
}

public static function isFalse(): IsFalse
{
return new IsFalse;
}

public static function isJson(): IsJson
{
return new IsJson;
}

public static function isNull(): IsNull
{
return new IsNull;
}

public static function isFinite(): IsFinite
{
return new IsFinite;
}

public static function isInfinite(): IsInfinite
{
return new IsInfinite;
}

public static function isNan(): IsNan
{
return new IsNan;
}

public static function containsEqual($value): TraversableContainsEqual
{
return new TraversableContainsEqual($value);
}

public static function containsIdentical($value): TraversableContainsIdentical
{
return new TraversableContainsIdentical($value);
}

public static function containsOnly(string $type): TraversableContainsOnly
{
return new TraversableContainsOnly($type);
}

public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly
{
return new TraversableContainsOnly($className, false);
}




public static function arrayHasKey($key): ArrayHasKey
{
return new ArrayHasKey($key);
}

public static function equalTo($value): IsEqual
{
return new IsEqual($value, 0.0, false, false);
}

public static function equalToCanonicalizing($value): IsEqualCanonicalizing
{
return new IsEqualCanonicalizing($value);
}

public static function equalToIgnoringCase($value): IsEqualIgnoringCase
{
return new IsEqualIgnoringCase($value);
}

public static function equalToWithDelta($value, float $delta): IsEqualWithDelta
{
return new IsEqualWithDelta($value, $delta);
}

public static function isEmpty(): IsEmpty
{
return new IsEmpty;
}

public static function isWritable(): IsWritable
{
return new IsWritable;
}

public static function isReadable(): IsReadable
{
return new IsReadable;
}

public static function directoryExists(): DirectoryExists
{
return new DirectoryExists;
}

public static function fileExists(): FileExists
{
return new FileExists;
}

public static function greaterThan($value): GreaterThan
{
return new GreaterThan($value);
}

public static function greaterThanOrEqual($value): LogicalOr
{
return static::logicalOr(
new IsEqual($value),
new GreaterThan($value),
);
}




public static function classHasAttribute(string $attributeName): ClassHasAttribute
{
self::createWarning('classHasAttribute() is deprecated and will be removed in PHPUnit 10.');

return new ClassHasAttribute($attributeName);
}




public static function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute
{
self::createWarning('classHasStaticAttribute() is deprecated and will be removed in PHPUnit 10.');

return new ClassHasStaticAttribute($attributeName);
}




public static function objectHasAttribute($attributeName): ObjectHasAttribute
{
self::createWarning('objectHasAttribute() is deprecated and will be removed in PHPUnit 10.');

return new ObjectHasAttribute($attributeName);
}

public static function identicalTo($value): IsIdentical
{
return new IsIdentical($value);
}

public static function isInstanceOf(string $className): IsInstanceOf
{
return new IsInstanceOf($className);
}

public static function isType(string $type): IsType
{
return new IsType($type);
}

public static function lessThan($value): LessThan
{
return new LessThan($value);
}

public static function lessThanOrEqual($value): LogicalOr
{
return static::logicalOr(
new IsEqual($value),
new LessThan($value),
);
}

public static function matchesRegularExpression(string $pattern): RegularExpression
{
return new RegularExpression($pattern);
}

public static function matches(string $string): StringMatchesFormatDescription
{
return new StringMatchesFormatDescription($string);
}

public static function stringStartsWith($prefix): StringStartsWith
{
return new StringStartsWith($prefix);
}

public static function stringContains(string $string, bool $case = true): StringContains
{
return new StringContains($string, $case);
}

public static function stringEndsWith(string $suffix): StringEndsWith
{
return new StringEndsWith($suffix);
}

public static function countOf(int $count): Count
{
return new Count($count);
}

public static function objectEquals(object $object, string $method = 'equals'): ObjectEquals
{
return new ObjectEquals($object, $method);
}








public static function fail(string $message = ''): void
{
self::$count++;

throw new AssertionFailedError($message);
}








public static function markTestIncomplete(string $message = ''): void
{
throw new IncompleteTestError($message);
}









public static function markTestSkipped(string $message = ''): void
{
if ($hint = self::detectLocationHint($message)) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
array_unshift($trace, $hint);

throw new SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace);
}

throw new SkippedTestError($message);
}




public static function getCount(): int
{
return self::$count;
}




public static function resetCount(): void
{
self::$count = 0;
}

private static function detectLocationHint(string $message): ?array
{
$hint = null;
$lines = preg_split('/\r\n|\r|\n/', $message);

while (strpos($lines[0], '__OFFSET') !== false) {
$offset = explode('=', array_shift($lines));

if ($offset[0] === '__OFFSET_FILE') {
$hint['file'] = $offset[1];
}

if ($offset[0] === '__OFFSET_LINE') {
$hint['line'] = $offset[1];
}
}

if ($hint) {
$hint['message'] = implode(PHP_EOL, $lines);
}

return $hint;
}

private static function isValidObjectAttributeName(string $attributeName): bool
{
return (bool) preg_match('/[^\x00-\x1f\x7f-\x9f]+/', $attributeName);
}

private static function isValidClassAttributeName(string $attributeName): bool
{
return (bool) preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName);
}




private static function createWarning(string $warning): void
{
foreach (debug_backtrace() as $step) {
if (isset($step['object']) && $step['object'] instanceof TestCase) {
assert($step['object'] instanceof TestCase);

$step['object']->addWarning($warning);

break;
}
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_VERSION_ID;
use function array_keys;
use function get_class;
use function spl_object_hash;
use PHPUnit\Util\Filter;
use Throwable;
use WeakReference;












final class ExceptionWrapper extends Exception
{



protected $className;




protected $previous;




private $originalException;

public function __construct(Throwable $t)
{


parent::__construct($t->getMessage(), (int) $t->getCode());

$this->setOriginalException($t);
}

public function __toString(): string
{
$string = TestFailure::exceptionToString($this);

if ($trace = Filter::getFilteredStacktrace($this)) {
$string .= "\n" . $trace;
}

if ($this->previous) {
$string .= "\nCaused by\n" . $this->previous;
}

return $string;
}

public function getClassName(): string
{
return $this->className;
}

public function getPreviousWrapped(): ?self
{
return $this->previous;
}

public function setClassName(string $className): void
{
$this->className = $className;
}

public function setOriginalException(Throwable $t): void
{
$this->originalException($t);

$this->className = get_class($t);
$this->file = $t->getFile();
$this->line = $t->getLine();

$this->serializableTrace = $t->getTrace();

foreach (array_keys($this->serializableTrace) as $key) {
unset($this->serializableTrace[$key]['args']);
}

if ($t->getPrevious()) {
$this->previous = new self($t->getPrevious());
}
}

public function getOriginalException(): ?Throwable
{
return $this->originalException();
}







private function originalException(Throwable $exceptionToStore = null): ?Throwable
{

if (PHP_VERSION_ID < 70400) {
static $originalExceptions;

$instanceId = spl_object_hash($this);

if ($exceptionToStore) {
$originalExceptions[$instanceId] = $exceptionToStore;
}

return $originalExceptions[$instanceId] ?? null;
}

if ($exceptionToStore) {
$this->originalException = WeakReference::create($exceptionToStore);
}

return $this->originalException !== null ? $this->originalException->get() : null;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use Throwable;











interface TestListener
{
public function addError(Test $test, Throwable $t, float $time): void;

public function addWarning(Test $test, Warning $e, float $time): void;

public function addFailure(Test $test, AssertionFailedError $e, float $time): void;

public function addIncompleteTest(Test $test, Throwable $t, float $time): void;

public function addRiskyTest(Test $test, Throwable $t, float $time): void;

public function addSkippedTest(Test $test, Throwable $t, float $time): void;

public function startTestSuite(TestSuite $suite): void;

public function endTestSuite(TestSuite $suite): void;

public function startTest(Test $test): void;

public function endTest(Test $test, float $time): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function array_filter;
use function array_map;
use function array_values;
use function count;
use function explode;
use function in_array;
use function strpos;
use function trim;




final class ExecutionOrderDependency
{



private $className = '';




private $methodName = '';




private $useShallowClone = false;




private $useDeepClone = false;

public static function createFromDependsAnnotation(string $className, string $annotation): self
{

$parts = explode(' ', trim($annotation), 2);

if (count($parts) === 1) {
$cloneOption = '';
$target = $parts[0];
} else {
$cloneOption = $parts[0];
$target = $parts[1];
}


if ($target !== '' && strpos($target, '::') === false) {
$target = $className . '::' . $target;
}

return new self($target, null, $cloneOption);
}






public static function filterInvalid(array $dependencies): array
{
return array_values(
array_filter(
$dependencies,
static function (self $d)
{
return $d->isValid();
},
),
);
}







public static function mergeUnique(array $existing, array $additional): array
{
$existingTargets = array_map(
static function ($dependency)
{
return $dependency->getTarget();
},
$existing,
);

foreach ($additional as $dependency) {
if (in_array($dependency->getTarget(), $existingTargets, true)) {
continue;
}

$existingTargets[] = $dependency->getTarget();
$existing[] = $dependency;
}

return $existing;
}







public static function diff(array $left, array $right): array
{
if ($right === []) {
return $left;
}

if ($left === []) {
return [];
}

$diff = [];
$rightTargets = array_map(
static function ($dependency)
{
return $dependency->getTarget();
},
$right,
);

foreach ($left as $dependency) {
if (in_array($dependency->getTarget(), $rightTargets, true)) {
continue;
}

$diff[] = $dependency;
}

return $diff;
}

public function __construct(string $classOrCallableName, ?string $methodName = null, ?string $option = null)
{
if ($classOrCallableName === '') {
return;
}

if (strpos($classOrCallableName, '::') !== false) {
[$this->className, $this->methodName] = explode('::', $classOrCallableName);
} else {
$this->className = $classOrCallableName;
$this->methodName = !empty($methodName) ? $methodName : 'class';
}

if ($option === 'clone') {
$this->useDeepClone = true;
} elseif ($option === 'shallowClone') {
$this->useShallowClone = true;
}
}

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

public function isValid(): bool
{

return $this->className !== '' && $this->methodName !== '';
}

public function useShallowClone(): bool
{
return $this->useShallowClone;
}

public function useDeepClone(): bool
{
return $this->useDeepClone;
}

public function targetIsClass(): bool
{
return $this->methodName === 'class';
}

public function getTarget(): string
{
return $this->isValid()
? $this->className . '::' . $this->methodName
: '';
}

public function getTargetClassName(): string
{
return $this->className;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use Throwable;






trait TestListenerDefaultImplementation
{
public function addError(Test $test, Throwable $t, float $time): void
{
}

public function addWarning(Test $test, Warning $e, float $time): void
{
}

public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
}

public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
}

public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
}

public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
}

public function startTestSuite(TestSuite $suite): void
{
}

public function endTestSuite(TestSuite $suite): void
{
}

public function startTest(Test $test): void
{
}

public function endTest(Test $test, float $time): void
{
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function assert;
use function count;
use RecursiveIterator;






final class TestSuiteIterator implements RecursiveIterator
{



private $position = 0;




private $tests;

public function __construct(TestSuite $testSuite)
{
$this->tests = $testSuite->tests();
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->tests);
}

public function key(): int
{
return $this->position;
}

public function current(): Test
{
return $this->tests[$this->position];
}

public function next(): void
{
$this->position++;
}




public function getChildren(): self
{
if (!$this->hasChildren()) {
throw new NoChildTestSuiteException(
'The current item is not a TestSuite instance and therefore does not have any children.',
);
}

$current = $this->current();

assert($current instanceof TestSuite);

return new self($current);
}

public function hasChildren(): bool
{
return $this->valid() && $this->current() instanceof TestSuite;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class IncompleteTestCase extends TestCase
{



protected $backupGlobals = false;




protected $backupStaticAttributes = false;




protected $runTestInSeparateProcess = false;




private $message;

public function __construct(string $className, string $methodName, string $message = '')
{
parent::__construct($className . '::' . $methodName);

$this->message = $message;
}

public function getMessage(): string
{
return $this->message;
}






public function toString(): string
{
return $this->getName();
}




protected function runTest(): void
{
$this->markTestIncomplete($this->message);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function get_class;
use function sprintf;
use function trim;
use PHPUnit\Framework\Error\Error;
use Throwable;




final class TestFailure
{



private $failedTest;




private $thrownException;




private $testName;




public static function exceptionToString(Throwable $e): string
{
if ($e instanceof SelfDescribing) {
$buffer = $e->toString();

if ($e instanceof ExpectationFailedException && $e->getComparisonFailure()) {
$buffer .= $e->getComparisonFailure()->getDiff();
}

if ($e instanceof PHPTAssertionFailedError) {
$buffer .= $e->getDiff();
}

if (!empty($buffer)) {
$buffer = trim($buffer) . "\n";
}

return $buffer;
}

if ($e instanceof Error) {
return $e->getMessage() . "\n";
}

if ($e instanceof ExceptionWrapper) {
return $e->getClassName() . ': ' . $e->getMessage() . "\n";
}

return get_class($e) . ': ' . $e->getMessage() . "\n";
}




public function __construct(Test $failedTest, Throwable $t)
{
if ($failedTest instanceof SelfDescribing) {
$this->testName = $failedTest->toString();
} else {
$this->testName = get_class($failedTest);
}

if (!$failedTest instanceof TestCase || !$failedTest->isInIsolation()) {
$this->failedTest = $failedTest;
}

$this->thrownException = $t;
}




public function toString(): string
{
return sprintf(
'%s: %s',
$this->testName,
$this->thrownException->getMessage(),
);
}




public function getExceptionAsString(): string
{
return self::exceptionToString($this->thrownException);
}




public function getTestName(): string
{
return $this->testName;
}









public function failedTest(): ?Test
{
return $this->failedTest;
}




public function thrownException(): Throwable
{
return $this->thrownException;
}




public function exceptionMessage(): string
{
return $this->thrownException()->getMessage();
}





public function isFailure(): bool
{
return $this->thrownException() instanceof AssertionFailedError;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use Throwable;




interface SkippedTest extends Throwable
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function func_get_args;
use function function_exists;
use ArrayAccess;
use Countable;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\Constraint\ArrayHasKey;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\ClassHasAttribute;
use PHPUnit\Framework\Constraint\ClassHasStaticAttribute;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\Count;
use PHPUnit\Framework\Constraint\DirectoryExists;
use PHPUnit\Framework\Constraint\FileExists;
use PHPUnit\Framework\Constraint\GreaterThan;
use PHPUnit\Framework\Constraint\IsAnything;
use PHPUnit\Framework\Constraint\IsEmpty;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\Constraint\IsEqualCanonicalizing;
use PHPUnit\Framework\Constraint\IsEqualIgnoringCase;
use PHPUnit\Framework\Constraint\IsEqualWithDelta;
use PHPUnit\Framework\Constraint\IsFalse;
use PHPUnit\Framework\Constraint\IsFinite;
use PHPUnit\Framework\Constraint\IsIdentical;
use PHPUnit\Framework\Constraint\IsInfinite;
use PHPUnit\Framework\Constraint\IsInstanceOf;
use PHPUnit\Framework\Constraint\IsJson;
use PHPUnit\Framework\Constraint\IsNan;
use PHPUnit\Framework\Constraint\IsNull;
use PHPUnit\Framework\Constraint\IsReadable;
use PHPUnit\Framework\Constraint\IsTrue;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\Constraint\IsWritable;
use PHPUnit\Framework\Constraint\LessThan;
use PHPUnit\Framework\Constraint\LogicalAnd;
use PHPUnit\Framework\Constraint\LogicalNot;
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\Constraint\LogicalXor;
use PHPUnit\Framework\Constraint\ObjectEquals;
use PHPUnit\Framework\Constraint\ObjectHasAttribute;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit\Framework\Constraint\StringEndsWith;
use PHPUnit\Framework\Constraint\StringMatchesFormatDescription;
use PHPUnit\Framework\Constraint\StringStartsWith;
use PHPUnit\Framework\Constraint\TraversableContainsEqual;
use PHPUnit\Framework\Constraint\TraversableContainsIdentical;
use PHPUnit\Framework\Constraint\TraversableContainsOnly;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub;
use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub;
use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub;
use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub;
use Throwable;

if (!function_exists('PHPUnit\Framework\assertArrayHasKey')) {














function assertArrayHasKey($key, $array, string $message = ''): void
{
Assert::assertArrayHasKey(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertArrayNotHasKey')) {














function assertArrayNotHasKey($key, $array, string $message = ''): void
{
Assert::assertArrayNotHasKey(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertContains')) {











function assertContains($needle, iterable $haystack, string $message = ''): void
{
Assert::assertContains(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertContainsEquals')) {
function assertContainsEquals($needle, iterable $haystack, string $message = ''): void
{
Assert::assertContainsEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotContains')) {











function assertNotContains($needle, iterable $haystack, string $message = ''): void
{
Assert::assertNotContains(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotContainsEquals')) {
function assertNotContainsEquals($needle, iterable $haystack, string $message = ''): void
{
Assert::assertNotContainsEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertContainsOnly')) {










function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
Assert::assertContainsOnly(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertContainsOnlyInstancesOf')) {










function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void
{
Assert::assertContainsOnlyInstancesOf(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotContainsOnly')) {










function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
Assert::assertNotContainsOnly(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertCount')) {













function assertCount(int $expectedCount, $haystack, string $message = ''): void
{
Assert::assertCount(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotCount')) {













function assertNotCount(int $expectedCount, $haystack, string $message = ''): void
{
Assert::assertNotCount(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertEquals')) {










function assertEquals($expected, $actual, string $message = ''): void
{
Assert::assertEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertEqualsCanonicalizing')) {










function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
Assert::assertEqualsCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertEqualsIgnoringCase')) {










function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
Assert::assertEqualsIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertEqualsWithDelta')) {










function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
Assert::assertEqualsWithDelta(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotEquals')) {










function assertNotEquals($expected, $actual, string $message = ''): void
{
Assert::assertNotEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotEqualsCanonicalizing')) {










function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
Assert::assertNotEqualsCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotEqualsIgnoringCase')) {










function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
Assert::assertNotEqualsIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotEqualsWithDelta')) {










function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
Assert::assertNotEqualsWithDelta(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertObjectEquals')) {







function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void
{
Assert::assertObjectEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertEmpty')) {












function assertEmpty($actual, string $message = ''): void
{
Assert::assertEmpty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotEmpty')) {












function assertNotEmpty($actual, string $message = ''): void
{
Assert::assertNotEmpty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertGreaterThan')) {










function assertGreaterThan($expected, $actual, string $message = ''): void
{
Assert::assertGreaterThan(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertGreaterThanOrEqual')) {










function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void
{
Assert::assertGreaterThanOrEqual(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertLessThan')) {










function assertLessThan($expected, $actual, string $message = ''): void
{
Assert::assertLessThan(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertLessThanOrEqual')) {










function assertLessThanOrEqual($expected, $actual, string $message = ''): void
{
Assert::assertLessThanOrEqual(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileEquals')) {











function assertFileEquals(string $expected, string $actual, string $message = ''): void
{
Assert::assertFileEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileEqualsCanonicalizing')) {











function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void
{
Assert::assertFileEqualsCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileEqualsIgnoringCase')) {











function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void
{
Assert::assertFileEqualsIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileNotEquals')) {











function assertFileNotEquals(string $expected, string $actual, string $message = ''): void
{
Assert::assertFileNotEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileNotEqualsCanonicalizing')) {











function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void
{
Assert::assertFileNotEqualsCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileNotEqualsIgnoringCase')) {











function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void
{
Assert::assertFileNotEqualsIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringEqualsFile')) {











function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = ''): void
{
Assert::assertStringEqualsFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringEqualsFileCanonicalizing')) {











function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void
{
Assert::assertStringEqualsFileCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringEqualsFileIgnoringCase')) {











function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void
{
Assert::assertStringEqualsFileIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotEqualsFile')) {











function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = ''): void
{
Assert::assertStringNotEqualsFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotEqualsFileCanonicalizing')) {











function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void
{
Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotEqualsFileIgnoringCase')) {











function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void
{
Assert::assertStringNotEqualsFileIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsReadable')) {










function assertIsReadable(string $filename, string $message = ''): void
{
Assert::assertIsReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotReadable')) {










function assertIsNotReadable(string $filename, string $message = ''): void
{
Assert::assertIsNotReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotIsReadable')) {














function assertNotIsReadable(string $filename, string $message = ''): void
{
Assert::assertNotIsReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsWritable')) {










function assertIsWritable(string $filename, string $message = ''): void
{
Assert::assertIsWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotWritable')) {










function assertIsNotWritable(string $filename, string $message = ''): void
{
Assert::assertIsNotWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotIsWritable')) {














function assertNotIsWritable(string $filename, string $message = ''): void
{
Assert::assertNotIsWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryExists')) {










function assertDirectoryExists(string $directory, string $message = ''): void
{
Assert::assertDirectoryExists(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryDoesNotExist')) {










function assertDirectoryDoesNotExist(string $directory, string $message = ''): void
{
Assert::assertDirectoryDoesNotExist(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryNotExists')) {














function assertDirectoryNotExists(string $directory, string $message = ''): void
{
Assert::assertDirectoryNotExists(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryIsReadable')) {










function assertDirectoryIsReadable(string $directory, string $message = ''): void
{
Assert::assertDirectoryIsReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryIsNotReadable')) {










function assertDirectoryIsNotReadable(string $directory, string $message = ''): void
{
Assert::assertDirectoryIsNotReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryNotIsReadable')) {














function assertDirectoryNotIsReadable(string $directory, string $message = ''): void
{
Assert::assertDirectoryNotIsReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryIsWritable')) {










function assertDirectoryIsWritable(string $directory, string $message = ''): void
{
Assert::assertDirectoryIsWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryIsNotWritable')) {










function assertDirectoryIsNotWritable(string $directory, string $message = ''): void
{
Assert::assertDirectoryIsNotWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDirectoryNotIsWritable')) {














function assertDirectoryNotIsWritable(string $directory, string $message = ''): void
{
Assert::assertDirectoryNotIsWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileExists')) {










function assertFileExists(string $filename, string $message = ''): void
{
Assert::assertFileExists(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileDoesNotExist')) {










function assertFileDoesNotExist(string $filename, string $message = ''): void
{
Assert::assertFileDoesNotExist(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileNotExists')) {














function assertFileNotExists(string $filename, string $message = ''): void
{
Assert::assertFileNotExists(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileIsReadable')) {










function assertFileIsReadable(string $file, string $message = ''): void
{
Assert::assertFileIsReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileIsNotReadable')) {










function assertFileIsNotReadable(string $file, string $message = ''): void
{
Assert::assertFileIsNotReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileNotIsReadable')) {














function assertFileNotIsReadable(string $file, string $message = ''): void
{
Assert::assertFileNotIsReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileIsWritable')) {










function assertFileIsWritable(string $file, string $message = ''): void
{
Assert::assertFileIsWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileIsNotWritable')) {










function assertFileIsNotWritable(string $file, string $message = ''): void
{
Assert::assertFileIsNotWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFileNotIsWritable')) {














function assertFileNotIsWritable(string $file, string $message = ''): void
{
Assert::assertFileNotIsWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertTrue')) {












function assertTrue($condition, string $message = ''): void
{
Assert::assertTrue(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotTrue')) {












function assertNotTrue($condition, string $message = ''): void
{
Assert::assertNotTrue(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFalse')) {












function assertFalse($condition, string $message = ''): void
{
Assert::assertFalse(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotFalse')) {












function assertNotFalse($condition, string $message = ''): void
{
Assert::assertNotFalse(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNull')) {












function assertNull($actual, string $message = ''): void
{
Assert::assertNull(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotNull')) {












function assertNotNull($actual, string $message = ''): void
{
Assert::assertNotNull(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertFinite')) {










function assertFinite($actual, string $message = ''): void
{
Assert::assertFinite(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertInfinite')) {










function assertInfinite($actual, string $message = ''): void
{
Assert::assertInfinite(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNan')) {










function assertNan($actual, string $message = ''): void
{
Assert::assertNan(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertClassHasAttribute')) {











function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassHasAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertClassNotHasAttribute')) {











function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassNotHasAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertClassHasStaticAttribute')) {











function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassHasStaticAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertClassNotHasStaticAttribute')) {











function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassNotHasStaticAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertObjectHasAttribute')) {













function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void
{
Assert::assertObjectHasAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertObjectNotHasAttribute')) {













function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void
{
Assert::assertObjectNotHasAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertObjectHasProperty')) {











function assertObjectHasProperty(string $attributeName, object $object, string $message = ''): void
{
Assert::assertObjectHasProperty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertObjectNotHasProperty')) {











function assertObjectNotHasProperty(string $attributeName, object $object, string $message = ''): void
{
Assert::assertObjectNotHasProperty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertSame')) {


















function assertSame($expected, $actual, string $message = ''): void
{
Assert::assertSame(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotSame')) {












function assertNotSame($expected, $actual, string $message = ''): void
{
Assert::assertNotSame(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertInstanceOf')) {

















function assertInstanceOf(string $expected, $actual, string $message = ''): void
{
Assert::assertInstanceOf(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotInstanceOf')) {

















function assertNotInstanceOf(string $expected, $actual, string $message = ''): void
{
Assert::assertNotInstanceOf(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsArray')) {












function assertIsArray($actual, string $message = ''): void
{
Assert::assertIsArray(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsBool')) {












function assertIsBool($actual, string $message = ''): void
{
Assert::assertIsBool(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsFloat')) {












function assertIsFloat($actual, string $message = ''): void
{
Assert::assertIsFloat(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsInt')) {












function assertIsInt($actual, string $message = ''): void
{
Assert::assertIsInt(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNumeric')) {












function assertIsNumeric($actual, string $message = ''): void
{
Assert::assertIsNumeric(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsObject')) {












function assertIsObject($actual, string $message = ''): void
{
Assert::assertIsObject(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsResource')) {












function assertIsResource($actual, string $message = ''): void
{
Assert::assertIsResource(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsClosedResource')) {












function assertIsClosedResource($actual, string $message = ''): void
{
Assert::assertIsClosedResource(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsString')) {












function assertIsString($actual, string $message = ''): void
{
Assert::assertIsString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsScalar')) {












function assertIsScalar($actual, string $message = ''): void
{
Assert::assertIsScalar(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsCallable')) {












function assertIsCallable($actual, string $message = ''): void
{
Assert::assertIsCallable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsIterable')) {












function assertIsIterable($actual, string $message = ''): void
{
Assert::assertIsIterable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotArray')) {












function assertIsNotArray($actual, string $message = ''): void
{
Assert::assertIsNotArray(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotBool')) {












function assertIsNotBool($actual, string $message = ''): void
{
Assert::assertIsNotBool(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotFloat')) {












function assertIsNotFloat($actual, string $message = ''): void
{
Assert::assertIsNotFloat(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotInt')) {












function assertIsNotInt($actual, string $message = ''): void
{
Assert::assertIsNotInt(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotNumeric')) {












function assertIsNotNumeric($actual, string $message = ''): void
{
Assert::assertIsNotNumeric(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotObject')) {












function assertIsNotObject($actual, string $message = ''): void
{
Assert::assertIsNotObject(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotResource')) {












function assertIsNotResource($actual, string $message = ''): void
{
Assert::assertIsNotResource(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotClosedResource')) {












function assertIsNotClosedResource($actual, string $message = ''): void
{
Assert::assertIsNotClosedResource(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotString')) {












function assertIsNotString($actual, string $message = ''): void
{
Assert::assertIsNotString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotScalar')) {












function assertIsNotScalar($actual, string $message = ''): void
{
Assert::assertIsNotScalar(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotCallable')) {












function assertIsNotCallable($actual, string $message = ''): void
{
Assert::assertIsNotCallable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertIsNotIterable')) {












function assertIsNotIterable($actual, string $message = ''): void
{
Assert::assertIsNotIterable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertMatchesRegularExpression')) {










function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void
{
Assert::assertMatchesRegularExpression(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertRegExp')) {














function assertRegExp(string $pattern, string $string, string $message = ''): void
{
Assert::assertRegExp(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertDoesNotMatchRegularExpression')) {










function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void
{
Assert::assertDoesNotMatchRegularExpression(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotRegExp')) {














function assertNotRegExp(string $pattern, string $string, string $message = ''): void
{
Assert::assertNotRegExp(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertSameSize')) {















function assertSameSize($expected, $actual, string $message = ''): void
{
Assert::assertSameSize(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertNotSameSize')) {















function assertNotSameSize($expected, $actual, string $message = ''): void
{
Assert::assertNotSameSize(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringMatchesFormat')) {










function assertStringMatchesFormat(string $format, string $string, string $message = ''): void
{
Assert::assertStringMatchesFormat(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotMatchesFormat')) {










function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void
{
Assert::assertStringNotMatchesFormat(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringMatchesFormatFile')) {










function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
Assert::assertStringMatchesFormatFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotMatchesFormatFile')) {










function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
Assert::assertStringNotMatchesFormatFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringStartsWith')) {










function assertStringStartsWith(string $prefix, string $string, string $message = ''): void
{
Assert::assertStringStartsWith(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringStartsNotWith')) {













function assertStringStartsNotWith($prefix, $string, string $message = ''): void
{
Assert::assertStringStartsNotWith(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringContainsString')) {








function assertStringContainsString(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringContainsString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringContainsStringIgnoringCase')) {








function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringContainsStringIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotContainsString')) {








function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringNotContainsString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringNotContainsStringIgnoringCase')) {








function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringNotContainsStringIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringEndsWith')) {










function assertStringEndsWith(string $suffix, string $string, string $message = ''): void
{
Assert::assertStringEndsWith(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertStringEndsNotWith')) {










function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void
{
Assert::assertStringEndsNotWith(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertXmlFileEqualsXmlFile')) {











function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertXmlFileEqualsXmlFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertXmlFileNotEqualsXmlFile')) {











function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertXmlFileNotEqualsXmlFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertXmlStringEqualsXmlFile')) {













function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
Assert::assertXmlStringEqualsXmlFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertXmlStringNotEqualsXmlFile')) {













function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
Assert::assertXmlStringNotEqualsXmlFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertXmlStringEqualsXmlString')) {














function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
Assert::assertXmlStringEqualsXmlString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertXmlStringNotEqualsXmlString')) {














function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
Assert::assertXmlStringNotEqualsXmlString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertEqualXMLStructure')) {















function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void
{
Assert::assertEqualXMLStructure(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertThat')) {










function assertThat($value, Constraint $constraint, string $message = ''): void
{
Assert::assertThat(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJson')) {










function assertJson(string $actualJson, string $message = ''): void
{
Assert::assertJson(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJsonStringEqualsJsonString')) {










function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void
{
Assert::assertJsonStringEqualsJsonString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJsonStringNotEqualsJsonString')) {













function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void
{
Assert::assertJsonStringNotEqualsJsonString(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJsonStringEqualsJsonFile')) {










function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
Assert::assertJsonStringEqualsJsonFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJsonStringNotEqualsJsonFile')) {










function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
Assert::assertJsonStringNotEqualsJsonFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJsonFileEqualsJsonFile')) {










function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertJsonFileEqualsJsonFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\assertJsonFileNotEqualsJsonFile')) {










function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertJsonFileNotEqualsJsonFile(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\logicalAnd')) {
function logicalAnd(): LogicalAnd
{
return Assert::logicalAnd(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\logicalOr')) {
function logicalOr(): LogicalOr
{
return Assert::logicalOr(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\logicalNot')) {
function logicalNot(Constraint $constraint): LogicalNot
{
return Assert::logicalNot(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\logicalXor')) {
function logicalXor(): LogicalXor
{
return Assert::logicalXor(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\anything')) {
function anything(): IsAnything
{
return Assert::anything(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isTrue')) {
function isTrue(): IsTrue
{
return Assert::isTrue(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\callback')) {
function callback(callable $callback): Callback
{
return Assert::callback(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isFalse')) {
function isFalse(): IsFalse
{
return Assert::isFalse(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isJson')) {
function isJson(): IsJson
{
return Assert::isJson(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isNull')) {
function isNull(): IsNull
{
return Assert::isNull(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isFinite')) {
function isFinite(): IsFinite
{
return Assert::isFinite(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isInfinite')) {
function isInfinite(): IsInfinite
{
return Assert::isInfinite(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isNan')) {
function isNan(): IsNan
{
return Assert::isNan(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\containsEqual')) {
function containsEqual($value): TraversableContainsEqual
{
return Assert::containsEqual(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\containsIdentical')) {
function containsIdentical($value): TraversableContainsIdentical
{
return Assert::containsIdentical(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\containsOnly')) {
function containsOnly(string $type): TraversableContainsOnly
{
return Assert::containsOnly(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\containsOnlyInstancesOf')) {
function containsOnlyInstancesOf(string $className): TraversableContainsOnly
{
return Assert::containsOnlyInstancesOf(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\arrayHasKey')) {
function arrayHasKey($key): ArrayHasKey
{
return Assert::arrayHasKey(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\equalTo')) {
function equalTo($value): IsEqual
{
return Assert::equalTo(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\equalToCanonicalizing')) {
function equalToCanonicalizing($value): IsEqualCanonicalizing
{
return Assert::equalToCanonicalizing(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\equalToIgnoringCase')) {
function equalToIgnoringCase($value): IsEqualIgnoringCase
{
return Assert::equalToIgnoringCase(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\equalToWithDelta')) {
function equalToWithDelta($value, float $delta): IsEqualWithDelta
{
return Assert::equalToWithDelta(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isEmpty')) {
function isEmpty(): IsEmpty
{
return Assert::isEmpty(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isWritable')) {
function isWritable(): IsWritable
{
return Assert::isWritable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isReadable')) {
function isReadable(): IsReadable
{
return Assert::isReadable(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\directoryExists')) {
function directoryExists(): DirectoryExists
{
return Assert::directoryExists(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\fileExists')) {
function fileExists(): FileExists
{
return Assert::fileExists(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\greaterThan')) {
function greaterThan($value): GreaterThan
{
return Assert::greaterThan(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\greaterThanOrEqual')) {
function greaterThanOrEqual($value): LogicalOr
{
return Assert::greaterThanOrEqual(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\classHasAttribute')) {
function classHasAttribute(string $attributeName): ClassHasAttribute
{
return Assert::classHasAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\classHasStaticAttribute')) {
function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute
{
return Assert::classHasStaticAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\objectHasAttribute')) {
function objectHasAttribute($attributeName): ObjectHasAttribute
{
return Assert::objectHasAttribute(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\identicalTo')) {
function identicalTo($value): IsIdentical
{
return Assert::identicalTo(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isInstanceOf')) {
function isInstanceOf(string $className): IsInstanceOf
{
return Assert::isInstanceOf(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\isType')) {
function isType(string $type): IsType
{
return Assert::isType(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\lessThan')) {
function lessThan($value): LessThan
{
return Assert::lessThan(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\lessThanOrEqual')) {
function lessThanOrEqual($value): LogicalOr
{
return Assert::lessThanOrEqual(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\matchesRegularExpression')) {
function matchesRegularExpression(string $pattern): RegularExpression
{
return Assert::matchesRegularExpression(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\matches')) {
function matches(string $string): StringMatchesFormatDescription
{
return Assert::matches(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\stringStartsWith')) {
function stringStartsWith($prefix): StringStartsWith
{
return Assert::stringStartsWith(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\stringContains')) {
function stringContains(string $string, bool $case = true): StringContains
{
return Assert::stringContains(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\stringEndsWith')) {
function stringEndsWith(string $suffix): StringEndsWith
{
return Assert::stringEndsWith(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\countOf')) {
function countOf(int $count): Count
{
return Assert::countOf(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\objectEquals')) {
function objectEquals(object $object, string $method = 'equals'): ObjectEquals
{
return Assert::objectEquals(...func_get_args());
}
}

if (!function_exists('PHPUnit\Framework\any')) {




function any(): AnyInvokedCountMatcher
{
return new AnyInvokedCountMatcher;
}
}

if (!function_exists('PHPUnit\Framework\never')) {



function never(): InvokedCountMatcher
{
return new InvokedCountMatcher(0);
}
}

if (!function_exists('PHPUnit\Framework\atLeast')) {




function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher
{
return new InvokedAtLeastCountMatcher(
$requiredInvocations,
);
}
}

if (!function_exists('PHPUnit\Framework\atLeastOnce')) {



function atLeastOnce(): InvokedAtLeastOnceMatcher
{
return new InvokedAtLeastOnceMatcher;
}
}

if (!function_exists('PHPUnit\Framework\once')) {



function once(): InvokedCountMatcher
{
return new InvokedCountMatcher(1);
}
}

if (!function_exists('PHPUnit\Framework\exactly')) {




function exactly(int $count): InvokedCountMatcher
{
return new InvokedCountMatcher($count);
}
}

if (!function_exists('PHPUnit\Framework\atMost')) {




function atMost(int $allowedInvocations): InvokedAtMostCountMatcher
{
return new InvokedAtMostCountMatcher($allowedInvocations);
}
}

if (!function_exists('PHPUnit\Framework\at')) {




function at(int $index): InvokedAtIndexMatcher
{
return new InvokedAtIndexMatcher($index);
}
}

if (!function_exists('PHPUnit\Framework\returnValue')) {
function returnValue($value): ReturnStub
{
return new ReturnStub($value);
}
}

if (!function_exists('PHPUnit\Framework\returnValueMap')) {
function returnValueMap(array $valueMap): ReturnValueMapStub
{
return new ReturnValueMapStub($valueMap);
}
}

if (!function_exists('PHPUnit\Framework\returnArgument')) {
function returnArgument(int $argumentIndex): ReturnArgumentStub
{
return new ReturnArgumentStub($argumentIndex);
}
}

if (!function_exists('PHPUnit\Framework\returnCallback')) {
function returnCallback($callback): ReturnCallbackStub
{
return new ReturnCallbackStub($callback);
}
}

if (!function_exists('PHPUnit\Framework\returnSelf')) {





function returnSelf(): ReturnSelfStub
{
return new ReturnSelfStub;
}
}

if (!function_exists('PHPUnit\Framework\throwException')) {
function throwException(Throwable $exception): ExceptionStub
{
return new ExceptionStub($exception);
}
}

if (!function_exists('PHPUnit\Framework\onConsecutiveCalls')) {
function onConsecutiveCalls(): ConsecutiveCallsStub
{
$args = func_get_args();

return new ConsecutiveCallsStub($args);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function assert;
use function count;
use function get_class;
use function sprintf;
use function trim;
use PHPUnit\Util\Filter;
use PHPUnit\Util\InvalidDataSetException;
use PHPUnit\Util\Test as TestUtil;
use ReflectionClass;
use Throwable;




final class TestBuilder
{
public function build(ReflectionClass $theClass, string $methodName): Test
{
$className = $theClass->getName();

if (!$theClass->isInstantiable()) {
return new ErrorTestCase(
sprintf('Cannot instantiate class "%s".', $className),
);
}

$backupSettings = TestUtil::getBackupSettings(
$className,
$methodName,
);

$preserveGlobalState = TestUtil::getPreserveGlobalStateSettings(
$className,
$methodName,
);

$runTestInSeparateProcess = TestUtil::getProcessIsolationSettings(
$className,
$methodName,
);

$runClassInSeparateProcess = TestUtil::getClassProcessIsolationSettings(
$className,
$methodName,
);

$constructor = $theClass->getConstructor();

if ($constructor === null) {
throw new Exception('No valid test provided.');
}

$parameters = $constructor->getParameters();


if (count($parameters) < 2) {
$test = $this->buildTestWithoutData($className);
} 
else {
try {
$data = TestUtil::getProvidedData(
$className,
$methodName,
);
} catch (IncompleteTestError $e) {
$message = sprintf(
"Test for %s::%s marked incomplete by data provider\n%s",
$className,
$methodName,
$this->throwableToString($e),
);

$data = new IncompleteTestCase($className, $methodName, $message);
} catch (SkippedTestError $e) {
$message = sprintf(
"Test for %s::%s skipped by data provider\n%s",
$className,
$methodName,
$this->throwableToString($e),
);

$data = new SkippedTestCase($className, $methodName, $message);
} catch (Throwable $t) {
$message = sprintf(
"The data provider specified for %s::%s is invalid.\n%s",
$className,
$methodName,
$this->throwableToString($t),
);

$data = new ErrorTestCase($message);
}


if (isset($data)) {
$test = $this->buildDataProviderTestSuite(
$methodName,
$className,
$data,
$runTestInSeparateProcess,
$preserveGlobalState,
$runClassInSeparateProcess,
$backupSettings,
);
} else {
$test = $this->buildTestWithoutData($className);
}
}

if ($test instanceof TestCase) {
$test->setName($methodName);
$this->configureTestCase(
$test,
$runTestInSeparateProcess,
$preserveGlobalState,
$runClassInSeparateProcess,
$backupSettings,
);
}

return $test;
}


private function buildTestWithoutData(string $className)
{
return new $className;
}


private function buildDataProviderTestSuite(
string $methodName,
string $className,
$data,
bool $runTestInSeparateProcess,
?bool $preserveGlobalState,
bool $runClassInSeparateProcess,
array $backupSettings
): DataProviderTestSuite {
$dataProviderTestSuite = new DataProviderTestSuite(
$className . '::' . $methodName,
);

$groups = TestUtil::getGroups($className, $methodName);

if ($data instanceof ErrorTestCase ||
$data instanceof SkippedTestCase ||
$data instanceof IncompleteTestCase) {
$dataProviderTestSuite->addTest($data, $groups);
} else {
foreach ($data as $_dataName => $_data) {
$_test = new $className($methodName, $_data, $_dataName);

assert($_test instanceof TestCase);

$this->configureTestCase(
$_test,
$runTestInSeparateProcess,
$preserveGlobalState,
$runClassInSeparateProcess,
$backupSettings,
);

$dataProviderTestSuite->addTest($_test, $groups);
}
}

return $dataProviderTestSuite;
}

private function configureTestCase(
TestCase $test,
bool $runTestInSeparateProcess,
?bool $preserveGlobalState,
bool $runClassInSeparateProcess,
array $backupSettings
): void {
if ($runTestInSeparateProcess) {
$test->setRunTestInSeparateProcess(true);

if ($preserveGlobalState !== null) {
$test->setPreserveGlobalState($preserveGlobalState);
}
}

if ($runClassInSeparateProcess) {
$test->setRunClassInSeparateProcess(true);

if ($preserveGlobalState !== null) {
$test->setPreserveGlobalState($preserveGlobalState);
}
}

if ($backupSettings['backupGlobals'] !== null) {
$test->setBackupGlobals($backupSettings['backupGlobals']);
}

if ($backupSettings['backupStaticAttributes'] !== null) {
$test->setBackupStaticAttributes(
$backupSettings['backupStaticAttributes'],
);
}
}

private function throwableToString(Throwable $t): string
{
$message = $t->getMessage();

if (empty(trim($message))) {
$message = '<no message>';
}

if ($t instanceof InvalidDataSetException) {
return sprintf(
"%s\n%s",
$message,
Filter::getFilteredStacktrace($t),
);
}

return sprintf(
"%s: %s\n%s",
get_class($t),
$message,
Filter::getFilteredStacktrace($t),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Error;

use PHPUnit\Framework\Exception;




class Error extends Exception
{
public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);

$this->file = $file;
$this->line = $line;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Error;




final class Deprecated extends Error
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Error;




final class Notice extends Error
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework\Error;




final class Warning extends Error
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class Error extends Exception implements SelfDescribing
{



public function toString(): string
{
return $this->getMessage();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class InvalidDataProviderException extends Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




class SyntheticError extends AssertionFailedError
{





protected $syntheticFile = '';






protected $syntheticLine = 0;






protected $syntheticTrace = [];

public function __construct(string $message, int $code, string $file, int $line, array $trace)
{
parent::__construct($message, $code);

$this->syntheticFile = $file;
$this->syntheticLine = $line;
$this->syntheticTrace = $trace;
}

public function getSyntheticFile(): string
{
return $this->syntheticFile;
}

public function getSyntheticLine(): int
{
return $this->syntheticLine;
}

public function getSyntheticTrace(): array
{
return $this->syntheticTrace;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function sprintf;




final class ComparisonMethodDoesNotDeclareExactlyOneParameterException extends Exception
{
public function __construct(string $className, string $methodName)
{
parent::__construct(
sprintf(
'Comparison method %s::%s() does not declare exactly one parameter.',
$className,
$methodName,
),
0,
null,
);
}

public function __toString(): string
{
return $this->getMessage() . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class CoveredCodeNotExecutedException extends RiskyTestError
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class MissingCoversAnnotationException extends RiskyTestError
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use Exception;
use SebastianBergmann\Comparator\ComparisonFailure;










final class ExpectationFailedException extends AssertionFailedError
{



protected $comparisonFailure;

public function __construct(string $message, ComparisonFailure $comparisonFailure = null, Exception $previous = null)
{
$this->comparisonFailure = $comparisonFailure;

parent::__construct($message, 0, $previous);
}

public function getComparisonFailure(): ?ComparisonFailure
{
return $this->comparisonFailure;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function sprintf;




final class ComparisonMethodDoesNotDeclareBoolReturnTypeException extends Exception
{
public function __construct(string $className, string $methodName)
{
parent::__construct(
sprintf(
'Comparison method %s::%s() does not declare bool return type.',
$className,
$methodName,
),
0,
null,
);
}

public function __toString(): string
{
return $this->getMessage() . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function array_keys;
use function get_object_vars;
use PHPUnit\Util\Filter;
use RuntimeException;
use Throwable;























class Exception extends RuntimeException implements \PHPUnit\Exception
{



protected $serializableTrace;

public function __construct($message = '', $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);

$this->serializableTrace = $this->getTrace();

foreach (array_keys($this->serializableTrace) as $key) {
unset($this->serializableTrace[$key]['args']);
}
}

public function __toString(): string
{
$string = TestFailure::exceptionToString($this);

if ($trace = Filter::getFilteredStacktrace($this)) {
$string .= "\n" . $trace;
}

return $string;
}

public function __sleep(): array
{
return array_keys(get_object_vars($this));
}




public function getSerializableTrace(): array
{
return $this->serializableTrace;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




class RiskyTestError extends AssertionFailedError
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




class AssertionFailedError extends Exception implements SelfDescribing
{



public function toString(): string
{
return $this->getMessage();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function sprintf;




final class ComparisonMethodDoesNotAcceptParameterTypeException extends Exception
{
public function __construct(string $className, string $methodName, string $type)
{
parent::__construct(
sprintf(
'%s is not an accepted argument type for comparison method %s::%s().',
$type,
$className,
$methodName,
),
0,
null,
);
}

public function __toString(): string
{
return $this->getMessage() . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class OutputError extends AssertionFailedError
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class SkippedTestSuiteError extends AssertionFailedError implements SkippedTest
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class PHPTAssertionFailedError extends SyntheticError
{



private $diff;

public function __construct(string $message, int $code, string $file, int $line, array $trace, string $diff)
{
parent::__construct($message, $code, $file, $line, $trace);
$this->diff = $diff;
}

public function getDiff(): string
{
return $this->diff;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class InvalidCoversTargetException extends CodeCoverageException
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use function debug_backtrace;
use function in_array;
use function lcfirst;
use function sprintf;




final class InvalidArgumentException extends Exception
{
public static function create(int $argument, string $type): self
{
$stack = debug_backtrace();
$function = $stack[1]['function'];

if (isset($stack[1]['class'])) {
$function = sprintf('%s::%s', $stack[1]['class'], $stack[1]['function']);
}

return new self(
sprintf(
'Argument #%d of %s() must be %s %s',
$argument,
$function,
in_array(lcfirst($type)[0], ['a', 'e', 'i', 'o', 'u'], true) ? 'an' : 'a',
$type,
),
);
}

private function __construct(string $message = '', int $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class NoChildTestSuiteException extends Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class Warning extends Exception implements SelfDescribing
{



public function toString(): string
{
return $this->getMessage();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class UnintentionallyCoveredCodeError extends RiskyTestError
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;




final class ActualValueIsNotAnObjectException extends Exception
{
public function __construct()
{
parent::__construct(
'Actual value is not an object',
0,
null,
);
}

public function __toString(): string
{
return $this->getMessage() . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




class CodeCoverageException extends Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function sprintf;




final class ComparisonMethodDoesNotDeclareParameterTypeException extends Exception
{
public function __construct(string $className, string $methodName)
{
parent::__construct(
sprintf(
'Parameter of comparison method %s::%s() does not have a declared type.',
$className,
$methodName,
),
0,
null,
);
}

public function __toString(): string
{
return $this->getMessage() . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class IncompleteTestError extends AssertionFailedError implements IncompleteTest
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class SkippedTestError extends AssertionFailedError implements SkippedTest
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;




final class SyntheticSkippedError extends SyntheticError implements SkippedTest
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function sprintf;




final class ComparisonMethodDoesNotExistException extends Exception
{
public function __construct(string $className, string $methodName)
{
parent::__construct(
sprintf(
'Comparison method %s::%s() does not exist.',
$className,
$methodName,
),
0,
null,
);
}

public function __toString(): string
{
return $this->getMessage() . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use const PHP_EOL;
use function array_keys;
use function array_map;
use function array_merge;
use function array_slice;
use function array_unique;
use function basename;
use function call_user_func;
use function class_exists;
use function count;
use function dirname;
use function get_declared_classes;
use function implode;
use function is_bool;
use function is_callable;
use function is_file;
use function is_object;
use function is_string;
use function method_exists;
use function preg_match;
use function preg_quote;
use function sprintf;
use function strpos;
use function substr;
use Iterator;
use IteratorAggregate;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\Filter\Factory;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\FileLoader;
use PHPUnit\Util\Reflection;
use PHPUnit\Util\Test as TestUtil;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use Throwable;






class TestSuite implements IteratorAggregate, Reorderable, SelfDescribing, Test
{





protected $backupGlobals;






protected $backupStaticAttributes;




protected $runTestInSeparateProcess = false;






protected $name = '';






protected $groups = [];






protected $tests = [];






protected $numTests = -1;




protected $testCase = false;




protected $foundClasses = [];




protected $providedTests;




protected $requiredTests;




private $beStrictAboutChangesToGlobalState;




private $iteratorFilter;




private $declaredClassesPointer;




private $warnings = [];






















public function __construct($theClass = '', string $name = '')
{
if (!is_string($theClass) && !$theClass instanceof ReflectionClass) {
throw InvalidArgumentException::create(
1,
'ReflectionClass object or string',
);
}

$this->declaredClassesPointer = count(get_declared_classes());

if (!$theClass instanceof ReflectionClass) {
if (class_exists($theClass, true)) {
if ($name === '') {
$name = $theClass;
}

try {
$theClass = new ReflectionClass($theClass);
} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

} else {
$this->setName($theClass);

return;
}
}

if (!$theClass->isSubclassOf(TestCase::class)) {
$this->setName((string) $theClass);

return;
}

if ($name !== '') {
$this->setName($name);
} else {
$this->setName($theClass->getName());
}

$constructor = $theClass->getConstructor();

if ($constructor !== null &&
!$constructor->isPublic()) {
$this->addTest(
new WarningTestCase(
sprintf(
'Class "%s" has no public constructor.',
$theClass->getName(),
),
),
);

return;
}

foreach ((new Reflection)->publicMethodsInTestClass($theClass) as $method) {
if (!TestUtil::isTestMethod($method)) {
continue;
}

$this->addTestMethod($theClass, $method);
}

if (empty($this->tests)) {
$this->addTest(
new WarningTestCase(
sprintf(
'No tests found in class "%s".',
$theClass->getName(),
),
),
);
}

$this->testCase = true;
}




public function toString(): string
{
return $this->getName();
}






public function addTest(Test $test, $groups = []): void
{
try {
$class = new ReflectionClass($test);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if (!$class->isAbstract()) {
$this->tests[] = $test;
$this->clearCaches();

if ($test instanceof self && empty($groups)) {
$groups = $test->getGroups();
}

if ($this->containsOnlyVirtualGroups($groups)) {
$groups[] = 'default';
}

foreach ($groups as $group) {
if (!isset($this->groups[$group])) {
$this->groups[$group] = [$test];
} else {
$this->groups[$group][] = $test;
}
}

if ($test instanceof TestCase) {
$test->setGroups($groups);
}
}
}








public function addTestSuite($testClass): void
{
if (!(is_object($testClass) || (is_string($testClass) && class_exists($testClass)))) {
throw InvalidArgumentException::create(
1,
'class name or object',
);
}

if (!is_object($testClass)) {
try {
$testClass = new ReflectionClass($testClass);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}

if ($testClass instanceof self) {
$this->addTest($testClass);
} elseif ($testClass instanceof ReflectionClass) {
$suiteMethod = false;

if (!$testClass->isAbstract() && $testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) {
try {
$method = $testClass->getMethod(
BaseTestRunner::SUITE_METHODNAME,
);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($method->isStatic()) {
$this->addTest(
$method->invoke(null, $testClass->getName()),
);

$suiteMethod = true;
}
}

if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(TestCase::class)) {
$this->addTest(new self($testClass));
}
} else {
throw new Exception;
}
}

public function addWarning(string $warning): void
{
$this->warnings[] = $warning;
}











public function addTestFile(string $filename): void
{
if (is_file($filename) && substr($filename, -5) === '.phpt') {
$this->addTest(new PhptTestCase($filename));

$this->declaredClassesPointer = count(get_declared_classes());

return;
}

$numTests = count($this->tests);



$filename = FileLoader::checkAndLoad($filename);
$newClasses = array_slice(get_declared_classes(), $this->declaredClassesPointer);





if (!empty($newClasses)) {



$this->foundClasses = array_merge($newClasses, $this->foundClasses);
$this->declaredClassesPointer = count(get_declared_classes());
}





$shortName = basename($filename, '.php');
$shortNameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortName, '/') . '$/';

foreach ($this->foundClasses as $i => $className) {
if (preg_match($shortNameRegEx, $className)) {
try {
$class = new ReflectionClass($className);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($class->getFileName() == $filename) {
$newClasses = [$className];
unset($this->foundClasses[$i]);

break;
}
}
}

foreach ($newClasses as $className) {
try {
$class = new ReflectionClass($className);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if (dirname($class->getFileName()) === __DIR__) {
continue;
}

if ($class->isAbstract() && $class->isSubclassOf(TestCase::class)) {
$this->addWarning(
sprintf(
'Abstract test case classes with "Test" suffix are deprecated (%s)',
$class->getName(),
),
);
}

if (!$class->isAbstract()) {
if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) {
try {
$method = $class->getMethod(
BaseTestRunner::SUITE_METHODNAME,
);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($method->isStatic()) {
$this->addTest($method->invoke(null, $className));
}
} elseif ($class->implementsInterface(Test::class)) {

$isPsr0 = (!$class->inNamespace()) && (strpos($class->getName(), '_') !== false);
$expectedClassName = $isPsr0 ? $className : $shortName;

if (($pos = strpos($expectedClassName, '.')) !== false) {
$expectedClassName = substr(
$expectedClassName,
0,
$pos,
);
}

if ($class->getShortName() !== $expectedClassName) {
$this->addWarning(
sprintf(
"Test case class not matching filename is deprecated\n               in %s\n               Class name was '%s', expected '%s'",
$filename,
$class->getShortName(),
$expectedClassName,
),
);
}

$this->addTestSuite($class);
}
}
}

if (count($this->tests) > ++$numTests) {
$this->addWarning(
sprintf(
"Multiple test case classes per file is deprecated\n               in %s",
$filename,
),
);
}

$this->numTests = -1;
}






public function addTestFiles(iterable $fileNames): void
{
foreach ($fileNames as $filename) {
$this->addTestFile((string) $filename);
}
}






public function count(): int
{
$this->numTests = 0;

foreach ($this as $test) {
$this->numTests += count($test);
}

return $this->numTests;
}




public function getName(): string
{
return $this->name;
}






public function getGroups(): array
{
return array_map(
static function ($key): string
{
return (string) $key;
},
array_keys($this->groups),
);
}

public function getGroupDetails(): array
{
return $this->groups;
}




public function setGroupDetails(array $groups): void
{
$this->groups = $groups;
}










public function run(TestResult $result = null): TestResult
{
if ($result === null) {
$result = $this->createResult();
}

if (count($this) === 0) {
return $result;
}


$className = $this->name;
$hookMethods = TestUtil::getHookMethods($className);

$result->startTestSuite($this);

$test = null;

if ($this->testCase && class_exists($this->name, false)) {
try {
foreach ($hookMethods['beforeClass'] as $beforeClassMethod) {
if (method_exists($this->name, $beforeClassMethod)) {
if ($missingRequirements = TestUtil::getMissingRequirements($this->name, $beforeClassMethod)) {
$this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements));
}

call_user_func([$this->name, $beforeClassMethod]);
}
}
} catch (SkippedTestError|SkippedTestSuiteError $error) {
foreach ($this->tests() as $test) {
$result->startTest($test);
$result->addFailure($test, $error, 0);
$result->endTest($test, 0);
}

$result->endTestSuite($this);

return $result;
} catch (Throwable $t) {
$errorAdded = false;

foreach ($this->tests() as $test) {
if ($result->shouldStop()) {
break;
}

$result->startTest($test);

if (!$errorAdded) {
$result->addError($test, $t, 0);

$errorAdded = true;
} else {
$result->addFailure(
$test,
new SkippedTestError('Test skipped because of an error in hook method'),
0,
);
}

$result->endTest($test, 0);
}

$result->endTestSuite($this);

return $result;
}
}

foreach ($this as $test) {
if ($result->shouldStop()) {
break;
}

if ($test instanceof TestCase || $test instanceof self) {
$test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState);
$test->setBackupGlobals($this->backupGlobals);
$test->setBackupStaticAttributes($this->backupStaticAttributes);
$test->setRunTestInSeparateProcess($this->runTestInSeparateProcess);
}

$test->run($result);
}

if ($this->testCase && class_exists($this->name, false)) {
foreach ($hookMethods['afterClass'] as $afterClassMethod) {
if (method_exists($this->name, $afterClassMethod)) {
try {
call_user_func([$this->name, $afterClassMethod]);
} catch (Throwable $t) {
$message = "Exception in {$this->name}::{$afterClassMethod}" . PHP_EOL . $t->getMessage();
$error = new SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace());

$placeholderTest = clone $test;
$placeholderTest->setName($afterClassMethod);

$result->startTest($placeholderTest);
$result->addFailure($placeholderTest, $error, 0);
$result->endTest($placeholderTest, 0);
}
}
}
}

$result->endTestSuite($this);

return $result;
}

public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void
{
$this->runTestInSeparateProcess = $runTestInSeparateProcess;
}

public function setName(string $name): void
{
$this->name = $name;
}






public function tests(): array
{
return $this->tests;
}






public function setTests(array $tests): void
{
$this->tests = $tests;
}










public function markTestSuiteSkipped($message = ''): void
{
throw new SkippedTestSuiteError($message);
}




public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState): void
{
if (null === $this->beStrictAboutChangesToGlobalState && is_bool($beStrictAboutChangesToGlobalState)) {
$this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
}
}




public function setBackupGlobals($backupGlobals): void
{
if (null === $this->backupGlobals && is_bool($backupGlobals)) {
$this->backupGlobals = $backupGlobals;
}
}




public function setBackupStaticAttributes($backupStaticAttributes): void
{
if (null === $this->backupStaticAttributes && is_bool($backupStaticAttributes)) {
$this->backupStaticAttributes = $backupStaticAttributes;
}
}




public function getIterator(): Iterator
{
$iterator = new TestSuiteIterator($this);

if ($this->iteratorFilter !== null) {
$iterator = $this->iteratorFilter->factory($iterator, $this);
}

return $iterator;
}

public function injectFilter(Factory $filter): void
{
$this->iteratorFilter = $filter;

foreach ($this as $test) {
if ($test instanceof self) {
$test->injectFilter($filter);
}
}
}




public function warnings(): array
{
return array_unique($this->warnings);
}




public function provides(): array
{
if ($this->providedTests === null) {
$this->providedTests = [];

if (is_callable($this->sortId(), true)) {
$this->providedTests[] = new ExecutionOrderDependency($this->sortId());
}

foreach ($this->tests as $test) {
if (!($test instanceof Reorderable)) {

continue;

}
$this->providedTests = ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides());
}
}

return $this->providedTests;
}




public function requires(): array
{
if ($this->requiredTests === null) {
$this->requiredTests = [];

foreach ($this->tests as $test) {
if (!($test instanceof Reorderable)) {

continue;

}
$this->requiredTests = ExecutionOrderDependency::mergeUnique(
ExecutionOrderDependency::filterInvalid($this->requiredTests),
$test->requires(),
);
}

$this->requiredTests = ExecutionOrderDependency::diff($this->requiredTests, $this->provides());
}

return $this->requiredTests;
}

public function sortId(): string
{
return $this->getName() . '::class';
}




protected function createResult(): TestResult
{
return new TestResult;
}




protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method): void
{
$methodName = $method->getName();

$test = (new TestBuilder)->build($class, $methodName);

if ($test instanceof TestCase || $test instanceof DataProviderTestSuite) {
$test->setDependencies(
TestUtil::getDependencies($class->getName(), $methodName),
);
}

$this->addTest(
$test,
TestUtil::getGroups($class->getName(), $methodName),
);
}

private function clearCaches(): void
{
$this->numTests = -1;
$this->providedTests = null;
$this->requiredTests = null;
}

private function containsOnlyVirtualGroups(array $groups): bool
{
foreach ($groups as $group) {
if (strpos($group, '__phpunit_') !== 0) {
return false;
}
}

return true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Framework;

use Countable;




interface Test extends Countable
{



public function run(TestResult $result = null): TestResult;
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const DIRECTORY_SEPARATOR;
use function array_keys;
use function array_map;
use function array_values;
use function count;
use function explode;
use function implode;
use function min;
use function preg_replace;
use function preg_replace_callback;
use function sprintf;
use function strtr;
use function trim;




final class Color
{



private const WHITESPACE_MAP = [
' ' => '·',
"\t" => '⇥',
];




private const WHITESPACE_EOL_MAP = [
' ' => '·',
"\t" => '⇥',
"\n" => '↵',
"\r" => '⟵',
];




private static $ansiCodes = [
'reset' => '0',
'bold' => '1',
'dim' => '2',
'dim-reset' => '22',
'underlined' => '4',
'fg-default' => '39',
'fg-black' => '30',
'fg-red' => '31',
'fg-green' => '32',
'fg-yellow' => '33',
'fg-blue' => '34',
'fg-magenta' => '35',
'fg-cyan' => '36',
'fg-white' => '37',
'bg-default' => '49',
'bg-black' => '40',
'bg-red' => '41',
'bg-green' => '42',
'bg-yellow' => '43',
'bg-blue' => '44',
'bg-magenta' => '45',
'bg-cyan' => '46',
'bg-white' => '47',
];

public static function colorize(string $color, string $buffer): string
{
if (trim($buffer) === '') {
return $buffer;
}

$codes = array_map('\trim', explode(',', $color));
$styles = [];

foreach ($codes as $code) {
if (isset(self::$ansiCodes[$code])) {
$styles[] = self::$ansiCodes[$code] ?? '';
}
}

if (empty($styles)) {
return $buffer;
}

return self::optimizeColor(sprintf("\x1b[%sm", implode(';', $styles)) . $buffer . "\x1b[0m");
}

public static function colorizePath(string $path, ?string $prevPath = null, bool $colorizeFilename = false): string
{
if ($prevPath === null) {
$prevPath = '';
}

$path = explode(DIRECTORY_SEPARATOR, $path);
$prevPath = explode(DIRECTORY_SEPARATOR, $prevPath);

for ($i = 0; $i < min(count($path), count($prevPath)); $i++) {
if ($path[$i] == $prevPath[$i]) {
$path[$i] = self::dim($path[$i]);
}
}

if ($colorizeFilename) {
$last = count($path) - 1;
$path[$last] = preg_replace_callback(
'/([\-_\.]+|phpt$)/',
static function ($matches)
{
return self::dim($matches[0]);
},
$path[$last],
);
}

return self::optimizeColor(implode(self::dim(DIRECTORY_SEPARATOR), $path));
}

public static function dim(string $buffer): string
{
if (trim($buffer) === '') {
return $buffer;
}

return "\e[2m{$buffer}\e[22m";
}

public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = false): string
{
$replaceMap = $visualizeEOL ? self::WHITESPACE_EOL_MAP : self::WHITESPACE_MAP;

return preg_replace_callback('/\s+/', static function ($matches) use ($replaceMap)
{
return self::dim(strtr($matches[0], $replaceMap));
}, $buffer);
}

private static function optimizeColor(string $buffer): string
{
$patterns = [
"/\e\\[22m\e\\[2m/" => '',
"/\e\\[([^m]*)m\e\\[([1-9][0-9;]*)m/" => "\e[$1;$2m",
"/(\e\\[[^m]*m)+(\e\\[0m)/" => '$2',
];

return preg_replace(array_keys($patterns), array_values($patterns), $buffer);
}
}
<?php
use PHPUnit\Framework\TestCase;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use PHPUnit\TextUI\XmlConfiguration\Loader;
use PHPUnit\TextUI\XmlConfiguration\PhpHandler;

if (!defined('STDOUT')) {


define('STDOUT', fopen('php://temp', 'w+b'));
define('STDERR', fopen('php://stderr', 'wb'));
}

{iniSettings}
ini_set('display_errors', 'stderr');
set_include_path('{include_path}');

$composerAutoload = {composerAutoload};
$phar = {phar};

ob_start();

if ($composerAutoload) {
require_once $composerAutoload;
define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload);
} else if ($phar) {
require $phar;
}

function __phpunit_run_isolated_test()
{
if (!class_exists('{className}')) {
require_once '{filename}';
}

$result = new PHPUnit\Framework\TestResult;

if ({collectCodeCoverageInformation}) {
$filter = unserialize('{codeCoverageFilter}');

$codeCoverage = new CodeCoverage(
(new Selector)->{driverMethod}($filter),
$filter
);

if ({cachesStaticAnalysis}) {
$codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}'));
}

$result->setCodeCoverage($codeCoverage);
}

$result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything});
$result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests});
$result->enforceTimeLimit({enforcesTimeLimit});
$result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests});
$result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests});

$test = new {className}('{methodName}', unserialize('{data}'), '{dataName}');
\assert($test instanceof TestCase);

$test->setDependencyInput(unserialize('{dependencyInput}'));
$test->setInIsolation(true);

ob_end_clean();
$test->run($result);
$output = '';
if (!$test->hasExpectationOnOutput()) {
$output = $test->getActualOutput();
}

ini_set('xdebug.scream', '0');

@rewind(STDOUT); 
if ($stdout = @stream_get_contents(STDOUT)) {
$output = $stdout . $output;
$streamMetaData = stream_get_meta_data(STDOUT);
if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) {
@ftruncate(STDOUT, 0);
@rewind(STDOUT);
}
}

file_put_contents(
'{processResultFile}',
serialize(
[
'testResult' => $test->getResult(),
'numAssertions' => $test->getNumAssertions(),
'result' => $result,
'output' => $output
]
)
);
}

$configurationFilePath = '{configurationFilePath}';

if ('' !== $configurationFilePath) {
$configuration = (new Loader)->load($configurationFilePath);

(new PhpHandler)->handle($configuration->php());

unset($configuration);
}

function __phpunit_error_handler($errno, $errstr, $errfile, $errline)
{
return true;
}

set_error_handler('__phpunit_error_handler');

{constants}
{included_files}
{globals}

restore_error_handler();

if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
require_once $GLOBALS['__PHPUNIT_BOOTSTRAP'];
unset($GLOBALS['__PHPUNIT_BOOTSTRAP']);
}

__phpunit_run_isolated_test();
<?php
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use SebastianBergmann\CodeCoverage\Filter;

$composerAutoload = {composerAutoload};
$phar = {phar};

ob_start();

$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'][] = '{job}';

if ($composerAutoload) {
require_once $composerAutoload;

define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload);
} else if ($phar) {
require $phar;
}

{globals}
$coverage = null;

if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
require_once $GLOBALS['__PHPUNIT_BOOTSTRAP'];
}

if (class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) {
$filter = new Filter;

$coverage = new CodeCoverage(
(new Selector)->{driverMethod}($filter),
$filter
);

if ({codeCoverageCacheDirectory}) {
$coverage->cacheStaticAnalysis({codeCoverageCacheDirectory});
}

$coverage->start(__FILE__);
}

register_shutdown_function(
function() use ($coverage) {
$output = null;

if ($coverage) {
$output = $coverage->stop();
}

file_put_contents('{coverageFile}', serialize($output));
}
);

ob_end_clean();

require '{job}';
<?php
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use PHPUnit\TextUI\XmlConfiguration\Loader;
use PHPUnit\TextUI\XmlConfiguration\PhpHandler;

if (!defined('STDOUT')) {


define('STDOUT', fopen('php://temp', 'w+b'));
define('STDERR', fopen('php://stderr', 'wb'));
}

{iniSettings}
ini_set('display_errors', 'stderr');
set_include_path('{include_path}');

$composerAutoload = {composerAutoload};
$phar = {phar};

ob_start();

if ($composerAutoload) {
require_once $composerAutoload;
define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload);
} else if ($phar) {
require $phar;
}

function __phpunit_run_isolated_test()
{
if (!class_exists('{className}')) {
require_once '{filename}';
}

$result = new PHPUnit\Framework\TestResult;

if ({collectCodeCoverageInformation}) {
$filter = unserialize('{codeCoverageFilter}');

$codeCoverage = new CodeCoverage(
(new Selector)->{driverMethod}($filter),
$filter
);

if ({cachesStaticAnalysis}) {
$codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}'));
}

$result->setCodeCoverage($codeCoverage);
}

$result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything});
$result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests});
$result->enforceTimeLimit({enforcesTimeLimit});
$result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests});
$result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests});

$test = new {className}('{name}', unserialize('{data}'), '{dataName}');
$test->setDependencyInput(unserialize('{dependencyInput}'));
$test->setInIsolation(true);

ob_end_clean();
$test->run($result);
$output = '';
if (!$test->hasExpectationOnOutput()) {
$output = $test->getActualOutput();
}

ini_set('xdebug.scream', '0');

@rewind(STDOUT); 
if ($stdout = @stream_get_contents(STDOUT)) {
$output = $stdout . $output;
$streamMetaData = stream_get_meta_data(STDOUT);
if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) {
@ftruncate(STDOUT, 0);
@rewind(STDOUT);
}
}

file_put_contents(
'{processResultFile}',
serialize(
[
'testResult' => $test->getResult(),
'numAssertions' => $test->getNumAssertions(),
'result' => $result,
'output' => $output
]
)
);
}

$configurationFilePath = '{configurationFilePath}';

if ('' !== $configurationFilePath) {
$configuration = (new Loader)->load($configurationFilePath);

(new PhpHandler)->handle($configuration->php());

unset($configuration);
}

function __phpunit_error_handler($errno, $errstr, $errfile, $errline)
{
return true;
}

set_error_handler('__phpunit_error_handler');

{constants}
{included_files}
{globals}

restore_error_handler();

if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
require_once $GLOBALS['__PHPUNIT_BOOTSTRAP'];
unset($GLOBALS['__PHPUNIT_BOOTSTRAP']);
}

__phpunit_run_isolated_test();
<?php declare(strict_types=1);








namespace PHPUnit\Util\PHP;

use function array_merge;
use function fclose;
use function file_put_contents;
use function fread;
use function fwrite;
use function is_array;
use function is_resource;
use function proc_close;
use function proc_open;
use function proc_terminate;
use function rewind;
use function sprintf;
use function stream_get_contents;
use function stream_select;
use function sys_get_temp_dir;
use function tempnam;
use function unlink;
use PHPUnit\Framework\Exception;




class DefaultPhpProcess extends AbstractPhpProcess
{



protected $tempFile;






public function runJob(string $job, array $settings = []): array
{
if ($this->stdin || $this->useTemporaryFile()) {
if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) ||
file_put_contents($this->tempFile, $job) === false) {
throw new Exception(
'Unable to write temporary file',
);
}

$job = $this->stdin;
}

return $this->runProcess($job, $settings);
}




protected function getHandles(): array
{
return [];
}






protected function runProcess(string $job, array $settings): array
{
$handles = $this->getHandles();

$env = null;

if ($this->env) {
$env = $_SERVER ?? [];
unset($env['argv'], $env['argc']);
$env = array_merge($env, $this->env);

foreach ($env as $envKey => $envVar) {
if (is_array($envVar)) {
unset($env[$envKey]);
}
}
}

$pipeSpec = [
0 => $handles[0] ?? ['pipe', 'r'],
1 => $handles[1] ?? ['pipe', 'w'],
2 => $handles[2] ?? ['pipe', 'w'],
];

$process = proc_open(
$this->getCommand($settings, $this->tempFile),
$pipeSpec,
$pipes,
null,
$env,
);

if (!is_resource($process)) {
throw new Exception(
'Unable to spawn worker process',
);
}

if ($job) {
$this->process($pipes[0], $job);
}

fclose($pipes[0]);

$stderr = $stdout = '';

if ($this->timeout) {
unset($pipes[0]);

while (true) {
$r = $pipes;
$w = null;
$e = null;

$n = @stream_select($r, $w, $e, $this->timeout);

if ($n === false) {
break;
}

if ($n === 0) {
proc_terminate($process, 9);

throw new Exception(
sprintf(
'Job execution aborted after %d seconds',
$this->timeout,
),
);
}

if ($n > 0) {
foreach ($r as $pipe) {
$pipeOffset = 0;

foreach ($pipes as $i => $origPipe) {
if ($pipe === $origPipe) {
$pipeOffset = $i;

break;
}
}

if (!$pipeOffset) {
break;
}

$line = fread($pipe, 8192);

if ($line === '' || $line === false) {
fclose($pipes[$pipeOffset]);

unset($pipes[$pipeOffset]);
} elseif ($pipeOffset === 1) {
$stdout .= $line;
} else {
$stderr .= $line;
}
}

if (empty($pipes)) {
break;
}
}
}
} else {
if (isset($pipes[1])) {
$stdout = stream_get_contents($pipes[1]);

fclose($pipes[1]);
}

if (isset($pipes[2])) {
$stderr = stream_get_contents($pipes[2]);

fclose($pipes[2]);
}
}

if (isset($handles[1])) {
rewind($handles[1]);

$stdout = stream_get_contents($handles[1]);

fclose($handles[1]);
}

if (isset($handles[2])) {
rewind($handles[2]);

$stderr = stream_get_contents($handles[2]);

fclose($handles[2]);
}

proc_close($process);

$this->cleanup();

return ['stdout' => $stdout, 'stderr' => $stderr];
}




protected function process($pipe, string $job): void
{
fwrite($pipe, $job);
}

protected function cleanup(): void
{
if ($this->tempFile) {
unlink($this->tempFile);
}
}

protected function useTemporaryFile(): bool
{
return false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\PHP;

use const DIRECTORY_SEPARATOR;
use const PHP_SAPI;
use function array_keys;
use function array_merge;
use function assert;
use function escapeshellarg;
use function file_exists;
use function file_get_contents;
use function ini_get_all;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function str_replace;
use function strpos;
use function strrpos;
use function substr;
use function trim;
use function unlink;
use function unserialize;
use __PHP_Incomplete_Class;
use ErrorException;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use SebastianBergmann\Environment\Runtime;




abstract class AbstractPhpProcess
{



protected $runtime;




protected $stderrRedirection = false;




protected $stdin = '';




protected $args = '';




protected $env = [];




protected $timeout = 0;

public static function factory(): self
{
if (DIRECTORY_SEPARATOR === '\\') {
return new WindowsPhpProcess;
}

return new DefaultPhpProcess;
}

public function __construct()
{
$this->runtime = new Runtime;
}






public function setUseStderrRedirection(bool $stderrRedirection): void
{
$this->stderrRedirection = $stderrRedirection;
}




public function useStderrRedirection(): bool
{
return $this->stderrRedirection;
}




public function setStdin(string $stdin): void
{
$this->stdin = $stdin;
}




public function getStdin(): string
{
return $this->stdin;
}




public function setArgs(string $args): void
{
$this->args = $args;
}




public function getArgs(): string
{
return $this->args;
}






public function setEnv(array $env): void
{
$this->env = $env;
}




public function getEnv(): array
{
return $this->env;
}




public function setTimeout(int $timeout): void
{
$this->timeout = $timeout;
}




public function getTimeout(): int
{
return $this->timeout;
}






public function runTestJob(string $job, Test $test, TestResult $result, string $processResultFile): void
{
$result->startTest($test);

$processResult = '';
$_result = $this->runJob($job);

if (file_exists($processResultFile)) {
$processResult = file_get_contents($processResultFile);

@unlink($processResultFile);
}

$this->processChildResult(
$test,
$result,
$processResult,
$_result['stderr'],
);
}




public function getCommand(array $settings, string $file = null): string
{
$command = $this->runtime->getBinary();

if ($this->runtime->hasPCOV()) {
$settings = array_merge(
$settings,
$this->runtime->getCurrentSettings(
array_keys(ini_get_all('pcov')),
),
);
} elseif ($this->runtime->hasXdebug()) {
$settings = array_merge(
$settings,
$this->runtime->getCurrentSettings(
array_keys(ini_get_all('xdebug')),
),
);
}

$command .= $this->settingsToParameters($settings);

if (PHP_SAPI === 'phpdbg') {
$command .= ' -qrr';

if (!$file) {
$command .= 's=';
}
}

if ($file) {
$command .= ' ' . escapeshellarg($file);
}

if ($this->args) {
if (!$file) {
$command .= ' --';
}
$command .= ' ' . $this->args;
}

if ($this->stderrRedirection) {
$command .= ' 2>&1';
}

return $command;
}




abstract public function runJob(string $job, array $settings = []): array;

protected function settingsToParameters(array $settings): string
{
$buffer = '';

foreach ($settings as $setting) {
$buffer .= ' -d ' . escapeshellarg($setting);
}

return $buffer;
}






private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr): void
{
$time = 0;

if (!empty($stderr)) {
$result->addError(
$test,
new Exception(trim($stderr)),
$time,
);
} else {
set_error_handler(



static function ($errno, $errstr, $errfile, $errline): void
{
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
},
);

try {
if (strpos($stdout, "#!/usr/bin/env php\n") === 0) {
$stdout = substr($stdout, 19);
}

$childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout));
restore_error_handler();

if ($childResult === false) {
$result->addFailure(
$test,
new AssertionFailedError('Test was run in child process and ended unexpectedly'),
$time,
);
}
} catch (ErrorException $e) {
restore_error_handler();
$childResult = false;

$result->addError(
$test,
new Exception(trim($stdout), 0, $e),
$time,
);
}

if ($childResult !== false) {
if (!empty($childResult['output'])) {
$output = $childResult['output'];
}



$test->setResult($childResult['testResult']);
$test->addToAssertionCount($childResult['numAssertions']);

$childResult = $childResult['result'];
assert($childResult instanceof TestResult);

if ($result->getCollectCodeCoverageInformation()) {
$result->getCodeCoverage()->merge(
$childResult->getCodeCoverage(),
);
}

$time = $childResult->time();
$notImplemented = $childResult->notImplemented();
$risky = $childResult->risky();
$skipped = $childResult->skipped();
$errors = $childResult->errors();
$warnings = $childResult->warnings();
$failures = $childResult->failures();

if (!empty($notImplemented)) {
$result->addError(
$test,
$this->getException($notImplemented[0]),
$time,
);
} elseif (!empty($risky)) {
$result->addError(
$test,
$this->getException($risky[0]),
$time,
);
} elseif (!empty($skipped)) {
$result->addError(
$test,
$this->getException($skipped[0]),
$time,
);
} elseif (!empty($errors)) {
$result->addError(
$test,
$this->getException($errors[0]),
$time,
);
} elseif (!empty($warnings)) {
$result->addWarning(
$test,
$this->getException($warnings[0]),
$time,
);
} elseif (!empty($failures)) {
$result->addFailure(
$test,
$this->getException($failures[0]),
$time,
);
}
}
}

$result->endTest($test, $time);

if (!empty($output)) {
print $output;
}
}






private function getException(TestFailure $error): Exception
{
$exception = $error->thrownException();

if ($exception instanceof __PHP_Incomplete_Class) {
$exceptionArray = [];

foreach ((array) $exception as $key => $value) {
$key = substr($key, strrpos($key, "\0") + 1);
$exceptionArray[$key] = $value;
}

$exception = new SyntheticError(
sprintf(
'%s: %s',
$exceptionArray['_PHP_Incomplete_Class_Name'],
$exceptionArray['message'],
),
$exceptionArray['code'],
$exceptionArray['file'],
$exceptionArray['line'],
$exceptionArray['trace'],
);
}

return $exception;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\PHP;

use const PHP_MAJOR_VERSION;
use function tmpfile;
use PHPUnit\Framework\Exception;






final class WindowsPhpProcess extends DefaultPhpProcess
{
public function getCommand(array $settings, string $file = null): string
{
if (PHP_MAJOR_VERSION < 8) {
return '"' . parent::getCommand($settings, $file) . '"';
}

return parent::getCommand($settings, $file);
}




protected function getHandles(): array
{
if (false === $stdout_handle = tmpfile()) {
throw new Exception(
'A temporary file could not be created; verify that your TEMP environment variable is writable',
);
}

return [
1 => $stdout_handle,
];
}

protected function useTemporaryFile(): bool
{
return true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const PHP_MAJOR_VERSION;
use const PHP_MINOR_VERSION;
use function array_keys;
use function array_reverse;
use function array_shift;
use function defined;
use function get_defined_constants;
use function get_included_files;
use function in_array;
use function ini_get_all;
use function is_array;
use function is_file;
use function is_scalar;
use function preg_match;
use function serialize;
use function sprintf;
use function strpos;
use function strtr;
use function substr;
use function var_export;
use Closure;




final class GlobalState
{



private const SUPER_GLOBAL_ARRAYS = [
'_ENV',
'_POST',
'_GET',
'_COOKIE',
'_SERVER',
'_FILES',
'_REQUEST',
];




private const DEPRECATED_INI_SETTINGS = [
'7.3' => [
'iconv.input_encoding' => true,
'iconv.output_encoding' => true,
'iconv.internal_encoding' => true,
'mbstring.func_overload' => true,
'mbstring.http_input' => true,
'mbstring.http_output' => true,
'mbstring.internal_encoding' => true,
'string.strip_tags' => true,
],

'7.4' => [
'iconv.input_encoding' => true,
'iconv.output_encoding' => true,
'iconv.internal_encoding' => true,
'mbstring.func_overload' => true,
'mbstring.http_input' => true,
'mbstring.http_output' => true,
'mbstring.internal_encoding' => true,
'pdo_odbc.db2_instance_name' => true,
'string.strip_tags' => true,
],

'8.0' => [
'iconv.input_encoding' => true,
'iconv.output_encoding' => true,
'iconv.internal_encoding' => true,
'mbstring.http_input' => true,
'mbstring.http_output' => true,
'mbstring.internal_encoding' => true,
],

'8.1' => [
'auto_detect_line_endings' => true,
'filter.default' => true,
'iconv.input_encoding' => true,
'iconv.output_encoding' => true,
'iconv.internal_encoding' => true,
'mbstring.http_input' => true,
'mbstring.http_output' => true,
'mbstring.internal_encoding' => true,
'oci8.old_oci_close_semantics' => true,
],

'8.2' => [
'auto_detect_line_endings' => true,
'filter.default' => true,
'iconv.input_encoding' => true,
'iconv.output_encoding' => true,
'iconv.internal_encoding' => true,
'mbstring.http_input' => true,
'mbstring.http_output' => true,
'mbstring.internal_encoding' => true,
'oci8.old_oci_close_semantics' => true,
],

'8.3' => [
'auto_detect_line_endings' => true,
'filter.default' => true,
'iconv.input_encoding' => true,
'iconv.output_encoding' => true,
'iconv.internal_encoding' => true,
'mbstring.http_input' => true,
'mbstring.http_output' => true,
'mbstring.internal_encoding' => true,
'oci8.old_oci_close_semantics' => true,
],
];




public static function getIncludedFilesAsString(): string
{
return self::processIncludedFilesAsString(get_included_files());
}






public static function processIncludedFilesAsString(array $files): string
{
$excludeList = new ExcludeList;
$prefix = false;
$result = '';

if (defined('__PHPUNIT_PHAR__')) {
$prefix = 'phar://' . __PHPUNIT_PHAR__ . '/';
}


array_shift($files);


if (substr(strtr($files[0], '\\', '/'), -24) === '/phpunit/phpunit/phpunit') {
array_shift($files);
}

foreach (array_reverse($files) as $file) {
if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) &&
in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) {
continue;
}

if ($prefix !== false && strpos($file, $prefix) === 0) {
continue;
}


if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) {
continue;
}

if (!$excludeList->isExcluded($file) && is_file($file)) {
$result = 'require_once \'' . $file . "';\n" . $result;
}
}

return $result;
}

public static function getIniSettingsAsString(): string
{
$result = '';

foreach (ini_get_all(null, false) as $key => $value) {
if (self::isIniSettingDeprecated($key)) {
continue;
}

$result .= sprintf(
'@ini_set(%s, %s);' . "\n",
self::exportVariable($key),
self::exportVariable((string) $value),
);
}

return $result;
}

public static function getConstantsAsString(): string
{
$constants = get_defined_constants(true);
$result = '';

if (isset($constants['user'])) {
foreach ($constants['user'] as $name => $value) {
$result .= sprintf(
'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
$name,
$name,
self::exportVariable($value),
);
}
}

return $result;
}

public static function getGlobalsAsString(): string
{
$result = '';

foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) {
if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) {
foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) {
if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) {
continue;
}

$result .= sprintf(
'$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n",
$superGlobalArray,
$key,
self::exportVariable($GLOBALS[$superGlobalArray][$key]),
);
}
}
}

$excludeList = self::SUPER_GLOBAL_ARRAYS;
$excludeList[] = 'GLOBALS';

foreach (array_keys($GLOBALS) as $key) {
if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, true)) {
$result .= sprintf(
'$GLOBALS[\'%s\'] = %s;' . "\n",
$key,
self::exportVariable($GLOBALS[$key]),
);
}
}

return $result;
}

private static function exportVariable($variable): string
{
if (is_scalar($variable) || $variable === null ||
(is_array($variable) && self::arrayOnlyContainsScalars($variable))) {
return var_export($variable, true);
}

return 'unserialize(' . var_export(serialize($variable), true) . ')';
}

private static function arrayOnlyContainsScalars(array $array): bool
{
$result = true;

foreach ($array as $element) {
if (is_array($element)) {
$result = self::arrayOnlyContainsScalars($element);
} elseif (!is_scalar($element) && $element !== null) {
$result = false;
}

if (!$result) {
break;
}
}

return $result;
}

private static function isIniSettingDeprecated(string $iniSetting): bool
{
return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use function preg_match;




final class RegularExpression
{



public static function safeMatch(string $pattern, string $subject)
{
return ErrorHandler::invokeIgnoringWarnings(
static function () use ($pattern, $subject)
{
return preg_match($pattern, $subject);
},
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use function get_class;
use function implode;
use function str_replace;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
use RecursiveIteratorIterator;
use XMLWriter;




final class XmlTestListRenderer
{



public function render(TestSuite $suite): string
{
$writer = new XMLWriter;

$writer->openMemory();
$writer->setIndent(true);
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement('tests');

$currentTestCase = null;

foreach (new RecursiveIteratorIterator($suite->getIterator()) as $test) {
if ($test instanceof TestCase) {
if (get_class($test) !== $currentTestCase) {
if ($currentTestCase !== null) {
$writer->endElement();
}

$writer->startElement('testCaseClass');
$writer->writeAttribute('name', get_class($test));

$currentTestCase = get_class($test);
}

$writer->startElement('testCaseMethod');
$writer->writeAttribute('name', $test->getName(false));
$writer->writeAttribute('groups', implode(',', $test->getGroups()));

if (!empty($test->getDataSetAsString(false))) {
$writer->writeAttribute(
'dataSet',
str_replace(
' with data set ',
'',
$test->getDataSetAsString(false),
),
);
}

$writer->endElement();
} elseif ($test instanceof PhptTestCase) {
if ($currentTestCase !== null) {
$writer->endElement();

$currentTestCase = null;
}

$writer->startElement('phptFile');
$writer->writeAttribute('path', $test->getName());
$writer->endElement();
}
}

if ($currentTestCase !== null) {
$writer->endElement();
}

$writer->endElement();
$writer->endDocument();

return $writer->outputMemory();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Annotation;

use const JSON_ERROR_NONE;
use const PREG_OFFSET_CAPTURE;
use function array_filter;
use function array_key_exists;
use function array_map;
use function array_merge;
use function array_pop;
use function array_slice;
use function array_values;
use function count;
use function explode;
use function file;
use function implode;
use function is_array;
use function is_int;
use function json_decode;
use function json_last_error;
use function json_last_error_msg;
use function preg_match;
use function preg_match_all;
use function preg_replace;
use function preg_split;
use function realpath;
use function rtrim;
use function sprintf;
use function str_replace;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
use function trim;
use PharIo\Version\VersionConstraintParser;
use PHPUnit\Framework\InvalidDataProviderException;
use PHPUnit\Framework\SkippedTestError;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Exception;
use PHPUnit\Util\InvalidDataSetException;
use ReflectionClass;
use ReflectionException;
use ReflectionFunctionAbstract;
use ReflectionMethod;
use Reflector;
use Traversable;








final class DocBlock
{



public const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';

private const REGEX_REQUIRES_VERSION = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<constraint>[\d\t \-.|~^]+)[ \t]*\r?$/m';
private const REGEX_REQUIRES_OS = '/@requires\s+(?P<name>OS(?:FAMILY)?)\s+(?P<value>.+?)[ \t]*\r?$/m';
private const REGEX_REQUIRES_SETTING = '/@requires\s+(?P<name>setting)\s+(?P<setting>([^ ]+?))\s*(?P<value>[\w\.-]+[\w\.]?)?[ \t]*\r?$/m';
private const REGEX_REQUIRES = '/@requires\s+(?P<name>function|extension)\s+(?P<value>([^\s<>=!]+))\s*(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+[\d\.]?)?[ \t]*\r?$/m';
private const REGEX_TEST_WITH = '/@testWith\s+/';


private $docComment;


private $isMethod;


private $symbolAnnotations;













private $parsedRequirements;


private $startLine;


private $endLine;


private $fileName;


private $name;






private $className;

public static function ofClass(ReflectionClass $class): self
{
$className = $class->getName();

return new self(
(string) $class->getDocComment(),
false,
self::extractAnnotationsFromReflector($class),
$class->getStartLine(),
$class->getEndLine(),
$class->getFileName(),
$className,
$className,
);
}




public static function ofMethod(ReflectionMethod $method, string $classNameInHierarchy): self
{
return new self(
(string) $method->getDocComment(),
true,
self::extractAnnotationsFromReflector($method),
$method->getStartLine(),
$method->getEndLine(),
$method->getFileName(),
$method->getName(),
$classNameInHierarchy,
);
}








private function __construct(string $docComment, bool $isMethod, array $symbolAnnotations, int $startLine, int $endLine, string $fileName, string $name, string $className)
{
$this->docComment = $docComment;
$this->isMethod = $isMethod;
$this->symbolAnnotations = $symbolAnnotations;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->fileName = $fileName;
$this->name = $name;
$this->className = $className;
}













public function requirements(): array
{
if ($this->parsedRequirements !== null) {
return $this->parsedRequirements;
}

$offset = $this->startLine;
$requires = [];
$recordedSettings = [];
$extensionVersions = [];
$recordedOffsets = [
'__FILE' => realpath($this->fileName),
];


$lines = preg_replace(['#^/\*{2}#', '#\*/$#'], '', preg_split('/\r\n|\r|\n/', $this->docComment));
$offset -= count($lines);

foreach ($lines as $line) {
if (preg_match(self::REGEX_REQUIRES_OS, $line, $matches)) {
$requires[$matches['name']] = $matches['value'];
$recordedOffsets[$matches['name']] = $offset;
}

if (preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) {
$requires[$matches['name']] = [
'version' => $matches['version'],
'operator' => $matches['operator'],
];
$recordedOffsets[$matches['name']] = $offset;
}

if (preg_match(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $line, $matches)) {
if (!empty($requires[$matches['name']])) {
$offset++;

continue;
}

try {
$versionConstraintParser = new VersionConstraintParser;

$requires[$matches['name'] . '_constraint'] = [
'constraint' => $versionConstraintParser->parse(trim($matches['constraint'])),
];
$recordedOffsets[$matches['name'] . '_constraint'] = $offset;
} catch (\PharIo\Version\Exception $e) {
throw new Warning($e->getMessage(), $e->getCode(), $e);
}
}

if (preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) {
$recordedSettings[$matches['setting']] = $matches['value'];
$recordedOffsets['__SETTING_' . $matches['setting']] = $offset;
}

if (preg_match(self::REGEX_REQUIRES, $line, $matches)) {
$name = $matches['name'] . 's';

if (!isset($requires[$name])) {
$requires[$name] = [];
}

$requires[$name][] = $matches['value'];
$recordedOffsets[$matches['name'] . '_' . $matches['value']] = $offset;

if ($name === 'extensions' && !empty($matches['version'])) {
$extensionVersions[$matches['value']] = [
'version' => $matches['version'],
'operator' => $matches['operator'],
];
}
}

$offset++;
}

return $this->parsedRequirements = array_merge(
$requires,
['__OFFSET' => $recordedOffsets],
array_filter([
'setting' => $recordedSettings,
'extension_versions' => $extensionVersions,
]),
);
}






public function getProvidedData(): ?array
{

$data = $this->getDataFromDataProviderAnnotation($this->docComment) ?? $this->getDataFromTestWithAnnotation($this->docComment);

if ($data === null) {
return null;
}

if ($data === []) {
throw new SkippedTestError;
}

foreach ($data as $key => $value) {
if (!is_array($value)) {
throw new InvalidDataSetException(
sprintf(
'Data set %s is invalid.',
is_int($key) ? '#' . $key : '"' . $key . '"',
),
);
}
}

return $data;
}




public function getInlineAnnotations(): array
{
$code = file($this->fileName);
$lineNumber = $this->startLine;
$startLine = $this->startLine - 1;
$endLine = $this->endLine - 1;
$codeLines = array_slice($code, $startLine, $endLine - $startLine + 1);
$annotations = [];

foreach ($codeLines as $line) {
if (preg_match('#/\*\*?\s*@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?\*/$#m', $line, $matches)) {
$annotations[strtolower($matches['name'])] = [
'line' => $lineNumber,
'value' => $matches['value'],
];
}

$lineNumber++;
}

return $annotations;
}

public function symbolAnnotations(): array
{
return $this->symbolAnnotations;
}

public function isHookToBeExecutedBeforeClass(): bool
{
return $this->isMethod &&
false !== strpos($this->docComment, '@beforeClass');
}

public function isHookToBeExecutedAfterClass(): bool
{
return $this->isMethod &&
false !== strpos($this->docComment, '@afterClass');
}

public function isToBeExecutedBeforeTest(): bool
{
return 1 === preg_match('/@before\b/', $this->docComment);
}

public function isToBeExecutedAfterTest(): bool
{
return 1 === preg_match('/@after\b/', $this->docComment);
}

public function isToBeExecutedAsPreCondition(): bool
{
return 1 === preg_match('/@preCondition\b/', $this->docComment);
}

public function isToBeExecutedAsPostCondition(): bool
{
return 1 === preg_match('/@postCondition\b/', $this->docComment);
}

private function getDataFromDataProviderAnnotation(string $docComment): ?array
{
$methodName = null;
$className = $this->className;

if ($this->isMethod) {
$methodName = $this->name;
}

if (!preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
return null;
}

$result = [];

foreach ($matches[1] as $match) {
$dataProviderMethodNameNamespace = explode('\\', $match);
$leaf = explode('::', array_pop($dataProviderMethodNameNamespace));
$dataProviderMethodName = array_pop($leaf);

if (empty($dataProviderMethodNameNamespace)) {
$dataProviderMethodNameNamespace = '';
} else {
$dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\';
}

if (empty($leaf)) {
$dataProviderClassName = $className;
} else {

$dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
}

try {
$dataProviderClass = new ReflectionClass($dataProviderClassName);

$dataProviderMethod = $dataProviderClass->getMethod(
$dataProviderMethodName,
);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);

}

if ($dataProviderMethod->isStatic()) {
$object = null;
} else {
$object = $dataProviderClass->newInstance();
}

if ($dataProviderMethod->getNumberOfParameters() === 0) {
$data = $dataProviderMethod->invoke($object);
} else {
$data = $dataProviderMethod->invoke($object, $methodName);
}

if ($data instanceof Traversable) {
$origData = $data;
$data = [];

foreach ($origData as $key => $value) {
if (is_int($key)) {
$data[] = $value;
} elseif (array_key_exists($key, $data)) {
throw new InvalidDataProviderException(
sprintf(
'The key "%s" has already been defined in the data provider "%s".',
$key,
$match,
),
);
} else {
$data[$key] = $value;
}
}
}

if (is_array($data)) {
$result = array_merge($result, $data);
}
}

return $result;
}




private function getDataFromTestWithAnnotation(string $docComment): ?array
{
$docComment = $this->cleanUpMultiLineAnnotation($docComment);

if (!preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
return null;
}

$offset = strlen($matches[0][0]) + $matches[0][1];
$annotationContent = substr($docComment, $offset);
$data = [];

foreach (explode("\n", $annotationContent) as $candidateRow) {
$candidateRow = trim($candidateRow);

if ($candidateRow[0] !== '[') {
break;
}

$dataSet = json_decode($candidateRow, true);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception(
'The data set for the @testWith annotation cannot be parsed: ' . json_last_error_msg(),
);
}

$data[] = $dataSet;
}

if (!$data) {
throw new Exception('The data set for the @testWith annotation cannot be parsed.');
}

return $data;
}

private function cleanUpMultiLineAnnotation(string $docComment): string
{

$docComment = str_replace("\r\n", "\n", $docComment);
$docComment = preg_replace('/\n\s*\*\s?/', "\n", $docComment);
$docComment = (string) substr($docComment, 0, -1);

return rtrim($docComment, "\n");
}


private static function parseDocBlock(string $docBlock): array
{

$docBlock = (string) substr($docBlock, 3, -2);
$annotations = [];

if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docBlock, $matches)) {
$numMatches = count($matches[0]);

for ($i = 0; $i < $numMatches; $i++) {
$annotations[$matches['name'][$i]][] = (string) $matches['value'][$i];
}
}

return $annotations;
}


private static function extractAnnotationsFromReflector(Reflector $reflector): array
{
$annotations = [];

if ($reflector instanceof ReflectionClass) {
$annotations = array_merge(
$annotations,
...array_map(
static function (ReflectionClass $trait): array
{
return self::parseDocBlock((string) $trait->getDocComment());
},
array_values($reflector->getTraits()),
),
);
}

return array_merge(
$annotations,
self::parseDocBlock((string) $reflector->getDocComment()),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Annotation;

use function array_key_exists;
use PHPUnit\Util\Exception;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;







final class Registry
{

private static $instance;


private $classDocBlocks = [];


private $methodDocBlocks = [];

public static function getInstance(): self
{
return self::$instance ?? self::$instance = new self;
}

private function __construct()
{
}






public function forClassName(string $class): DocBlock
{
if (array_key_exists($class, $this->classDocBlocks)) {
return $this->classDocBlocks[$class];
}

try {
$reflection = new ReflectionClass($class);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


return $this->classDocBlocks[$class] = DocBlock::ofClass($reflection);
}






public function forMethod(string $classInHierarchy, string $method): DocBlock
{
if (isset($this->methodDocBlocks[$classInHierarchy][$method])) {
return $this->methodDocBlocks[$classInHierarchy][$method];
}

try {
$reflection = new ReflectionMethod($classInHierarchy, $method);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


return $this->methodDocBlocks[$classInHierarchy][$method] = DocBlock::ofMethod($reflection, $classInHierarchy);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const ENT_COMPAT;
use const ENT_SUBSTITUTE;
use const PHP_SAPI;
use function assert;
use function count;
use function dirname;
use function explode;
use function fclose;
use function fopen;
use function fsockopen;
use function fwrite;
use function htmlspecialchars;
use function is_resource;
use function is_string;
use function sprintf;
use function str_replace;
use function strncmp;
use function strpos;




class Printer
{



private $stream;




private $isPhpStream;






public function __construct($out = null)
{
if (is_resource($out)) {
$this->stream = $out;

return;
}

if (!is_string($out)) {
return;
}

if (strpos($out, 'socket://') === 0) {
$tmp = explode(':', str_replace('socket://', '', $out));

if (count($tmp) !== 2) {
throw new Exception(
sprintf(
'"%s" does not match "socket://hostname:port" format',
$out,
),
);
}

$this->stream = fsockopen($tmp[0], (int) $tmp[1]);

return;
}

if (strpos($out, 'php://') === false && !Filesystem::createDirectory(dirname($out))) {
throw new Exception(
sprintf(
'Directory "%s" was not created',
dirname($out),
),
);
}

$this->stream = fopen($out, 'wb');
$this->isPhpStream = strncmp($out, 'php://', 6) !== 0;
}

public function write(string $buffer): void
{
if ($this->stream) {
assert(is_resource($this->stream));

fwrite($this->stream, $buffer);
} else {
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
$buffer = htmlspecialchars($buffer, ENT_COMPAT | ENT_SUBSTITUTE);
}

print $buffer;
}
}

public function flush(): void
{
if ($this->stream && $this->isPhpStream) {
assert(is_resource($this->stream));

fclose($this->stream);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Log;

use function class_exists;
use function get_class;
use function method_exists;
use function sprintf;
use function str_replace;
use function trim;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Exception;
use PHPUnit\Util\Filter;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Xml;
use ReflectionClass;
use ReflectionException;
use Throwable;




final class JUnit extends Printer implements TestListener
{



private $document;




private $root;




private $reportRiskyTests = false;




private $testSuites = [];




private $testSuiteTests = [0];




private $testSuiteAssertions = [0];




private $testSuiteErrors = [0];




private $testSuiteWarnings = [0];




private $testSuiteFailures = [0];




private $testSuiteSkipped = [0];




private $testSuiteTimes = [0];




private $testSuiteLevel = 0;




private $currentTestCase;




public function __construct($out = null, bool $reportRiskyTests = false)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;

$this->root = $this->document->createElement('testsuites');
$this->document->appendChild($this->root);

parent::__construct($out);

$this->reportRiskyTests = $reportRiskyTests;
}




public function flush(): void
{
$this->write($this->getXML());

parent::flush();
}




public function addError(Test $test, Throwable $t, float $time): void
{
$this->doAddFault($test, $t, 'error');
$this->testSuiteErrors[$this->testSuiteLevel]++;
}




public function addWarning(Test $test, Warning $e, float $time): void
{
$this->doAddFault($test, $e, 'warning');
$this->testSuiteWarnings[$this->testSuiteLevel]++;
}




public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->doAddFault($test, $e, 'failure');
$this->testSuiteFailures[$this->testSuiteLevel]++;
}




public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
$this->doAddSkipped();
}




public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
if (!$this->reportRiskyTests) {
return;
}

$this->doAddFault($test, $t, 'error');
$this->testSuiteErrors[$this->testSuiteLevel]++;
}




public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
$this->doAddSkipped();
}




public function startTestSuite(TestSuite $suite): void
{
$testSuite = $this->document->createElement('testsuite');
$testSuite->setAttribute('name', $suite->getName());

if (class_exists($suite->getName(), false)) {
try {
$class = new ReflectionClass($suite->getName());

$testSuite->setAttribute('file', $class->getFileName());
} catch (ReflectionException $e) {
}
}

if ($this->testSuiteLevel > 0) {
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
} else {
$this->root->appendChild($testSuite);
}

$this->testSuiteLevel++;
$this->testSuites[$this->testSuiteLevel] = $testSuite;
$this->testSuiteTests[$this->testSuiteLevel] = 0;
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
$this->testSuiteWarnings[$this->testSuiteLevel] = 0;
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
}




public function endTestSuite(TestSuite $suite): 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(
'warnings',
(string) $this->testSuiteWarnings[$this->testSuiteLevel],
);

$this->testSuites[$this->testSuiteLevel]->setAttribute(
'failures',
(string) $this->testSuiteFailures[$this->testSuiteLevel],
);

$this->testSuites[$this->testSuiteLevel]->setAttribute(
'skipped',
(string) $this->testSuiteSkipped[$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->testSuiteWarnings[$this->testSuiteLevel - 1] += $this->testSuiteWarnings[$this->testSuiteLevel];
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
}

$this->testSuiteLevel--;
}




public function startTest(Test $test): void
{
$usesDataprovider = false;

if (method_exists($test, 'usesDataProvider')) {
$usesDataprovider = $test->usesDataProvider();
}

$testCase = $this->document->createElement('testcase');
$testCase->setAttribute('name', $test->getName());

try {
$class = new ReflectionClass($test);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$methodName = $test->getName(!$usesDataprovider);

if ($class->hasMethod($methodName)) {
try {
$method = $class->getMethod($methodName);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$testCase->setAttribute('class', $class->getName());
$testCase->setAttribute('classname', str_replace('\\', '.', $class->getName()));
$testCase->setAttribute('file', $class->getFileName());
$testCase->setAttribute('line', (string) $method->getStartLine());
}

$this->currentTestCase = $testCase;
}




public function endTest(Test $test, float $time): void
{
$numAssertions = 0;

if (method_exists($test, 'getNumAssertions')) {
$numAssertions = $test->getNumAssertions();
}

$this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;

$this->currentTestCase->setAttribute(
'assertions',
(string) $numAssertions,
);

$this->currentTestCase->setAttribute(
'time',
sprintf('%F', $time),
);

$this->testSuites[$this->testSuiteLevel]->appendChild(
$this->currentTestCase,
);

$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;

$testOutput = '';

if (method_exists($test, 'hasOutput') && method_exists($test, 'getActualOutput')) {
$testOutput = $test->hasOutput() ? $test->getActualOutput() : '';
}

if (!empty($testOutput)) {
$systemOut = $this->document->createElement(
'system-out',
Xml::prepareString($testOutput),
);

$this->currentTestCase->appendChild($systemOut);
}

$this->currentTestCase = null;
}




public function getXML(): string
{
return $this->document->saveXML();
}

private function doAddFault(Test $test, Throwable $t, string $type): void
{
if ($this->currentTestCase === null) {
return;
}

if ($test instanceof SelfDescribing) {
$buffer = $test->toString() . "\n";
} else {
$buffer = '';
}

$buffer .= trim(
TestFailure::exceptionToString($t) . "\n" .
Filter::getFilteredStacktrace($t),
);

$fault = $this->document->createElement(
$type,
Xml::prepareString($buffer),
);

if ($t instanceof ExceptionWrapper) {
$fault->setAttribute('type', $t->getClassName());
} else {
$fault->setAttribute('type', get_class($t));
}

$this->currentTestCase->appendChild($fault);
}

private function doAddSkipped(): void
{
if ($this->currentTestCase === null) {
return;
}

$skipped = $this->document->createElement('skipped');

$this->currentTestCase->appendChild($skipped);

$this->testSuiteSkipped[$this->testSuiteLevel]++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Log;

use function class_exists;
use function count;
use function explode;
use function get_class;
use function getmypid;
use function ini_get;
use function is_bool;
use function is_scalar;
use function method_exists;
use function print_r;
use function round;
use function str_replace;
use function stripos;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\TextUI\DefaultResultPrinter;
use PHPUnit\Util\Exception;
use PHPUnit\Util\Filter;
use ReflectionClass;
use ReflectionException;
use SebastianBergmann\Comparator\ComparisonFailure;
use Throwable;




final class TeamCity extends DefaultResultPrinter
{



private $isSummaryTestCountPrinted = false;




private $startedTestName;




private $flowId;

public function printResult(TestResult $result): void
{
$this->printHeader($result);
$this->printFooter($result);
}




public function addError(Test $test, Throwable $t, float $time): void
{
$this->printEvent(
'testFailed',
[
'name' => $test->getName(),
'message' => self::getMessage($t),
'details' => self::getDetails($t),
'duration' => self::toMilliseconds($time),
],
);
}




public function addWarning(Test $test, Warning $e, float $time): void
{
$this->write(self::getMessage($e) . PHP_EOL);
}




public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$parameters = [
'name' => $test->getName(),
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
];

if ($e instanceof ExpectationFailedException) {
$comparisonFailure = $e->getComparisonFailure();

if ($comparisonFailure instanceof ComparisonFailure) {
$expectedString = $comparisonFailure->getExpectedAsString();

if ($expectedString === null || empty($expectedString)) {
$expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
}

$actualString = $comparisonFailure->getActualAsString();

if ($actualString === null || empty($actualString)) {
$actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
}

if ($actualString !== null && $expectedString !== null) {
$parameters['type'] = 'comparisonFailure';
$parameters['actual'] = $actualString;
$parameters['expected'] = $expectedString;
}
}
}

$this->printEvent('testFailed', $parameters);
}




public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
$this->printIgnoredTest($test->getName(), $t, $time);
}




public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
$this->addError($test, $t, $time);
}




public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
$testName = $test->getName();

if ($this->startedTestName !== $testName) {
$this->startTest($test);
$this->printIgnoredTest($testName, $t, $time);
$this->endTest($test, $time);
} else {
$this->printIgnoredTest($testName, $t, $time);
}
}

public function printIgnoredTest(string $testName, Throwable $t, float $time): void
{
$this->printEvent(
'testIgnored',
[
'name' => $testName,
'message' => self::getMessage($t),
'details' => self::getDetails($t),
'duration' => self::toMilliseconds($time),
],
);
}




public function startTestSuite(TestSuite $suite): void
{
if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
$this->flowId = getmypid();
} else {
$this->flowId = false;
}

if (!$this->isSummaryTestCountPrinted) {
$this->isSummaryTestCountPrinted = true;

$this->printEvent(
'testCount',
['count' => count($suite)],
);
}

$suiteName = $suite->getName();

if (empty($suiteName)) {
return;
}

$parameters = ['name' => $suiteName];

if (class_exists($suiteName, false)) {
$fileName = self::getFileName($suiteName);
$parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}";
} else {
$split = explode('::', $suiteName);

if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) {
$fileName = self::getFileName($split[0]);
$parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}";
$parameters['name'] = $split[1];
}
}

$this->printEvent('testSuiteStarted', $parameters);
}




public function endTestSuite(TestSuite $suite): void
{
$suiteName = $suite->getName();

if (empty($suiteName)) {
return;
}

$parameters = ['name' => $suiteName];

if (!class_exists($suiteName, false)) {
$split = explode('::', $suiteName);

if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) {
$parameters['name'] = $split[1];
}
}

$this->printEvent('testSuiteFinished', $parameters);
}




public function startTest(Test $test): void
{
$testName = $test->getName();
$this->startedTestName = $testName;
$params = ['name' => $testName];

if ($test instanceof TestCase) {
$className = get_class($test);
$fileName = self::getFileName($className);
$params['locationHint'] = "php_qn://{$fileName}::\\{$className}::{$testName}";
}

$this->printEvent('testStarted', $params);
}




public function endTest(Test $test, float $time): void
{
parent::endTest($test, $time);

$this->printEvent(
'testFinished',
[
'name' => $test->getName(),
'duration' => self::toMilliseconds($time),
],
);
}

protected function writeProgress(string $progress): void
{
}

private function printEvent(string $eventName, array $params = []): void
{
$this->write("\n##teamcity[{$eventName}");

if ($this->flowId) {
$params['flowId'] = $this->flowId;
}

foreach ($params as $key => $value) {
$escapedValue = self::escapeValue((string) $value);
$this->write(" {$key}='{$escapedValue}'");
}

$this->write("]\n");
}

private static function getMessage(Throwable $t): string
{
$message = '';

if ($t instanceof ExceptionWrapper) {
if ($t->getClassName() !== '') {
$message .= $t->getClassName();
}

if ($message !== '' && $t->getMessage() !== '') {
$message .= ' : ';
}
}

return $message . $t->getMessage();
}

private static function getDetails(Throwable $t): string
{
$stackTrace = Filter::getFilteredStacktrace($t);
$previous = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious();

while ($previous) {
$stackTrace .= "\nCaused by\n" .
TestFailure::exceptionToString($previous) . "\n" .
Filter::getFilteredStacktrace($previous);

$previous = $previous instanceof ExceptionWrapper ?
$previous->getPreviousWrapped() : $previous->getPrevious();
}

return ' ' . str_replace("\n", "\n ", $stackTrace);
}

private static function getPrimitiveValueAsString($value): ?string
{
if ($value === null) {
return 'null';
}

if (is_bool($value)) {
return $value ? 'true' : 'false';
}

if (is_scalar($value)) {
return print_r($value, true);
}

return null;
}

private static function escapeValue(string $text): string
{
return str_replace(
['|', "'", "\n", "\r", ']', '['],
['||', "|'", '|n', '|r', '|]', '|['],
$text,
);
}




private static function getFileName($className): string
{
try {
return (new ReflectionClass($className))->getFileName();

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}




private static function toMilliseconds(float $time): int
{
return (int) round($time * 1000);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const PHP_EOL;
use function get_class;
use function sprintf;
use function str_replace;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
use RecursiveIteratorIterator;




final class TextTestListRenderer
{



public function render(TestSuite $suite): string
{
$buffer = 'Available test(s):' . PHP_EOL;

foreach (new RecursiveIteratorIterator($suite->getIterator()) as $test) {
if ($test instanceof TestCase) {
$name = sprintf(
'%s::%s',
get_class($test),
str_replace(' with data set ', '', $test->getName()),
);
} elseif ($test instanceof PhptTestCase) {
$name = $test->getName();
} else {
continue;
}

$buffer .= sprintf(
' - %s' . PHP_EOL,
$name,
);
}

return $buffer;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use PHPUnit\Framework\TestResult;




final class TextResultPrinter extends ResultPrinter
{
public function printResult(TestResult $result): void
{
}




protected function startClass(string $name): void
{
$this->write($this->currentTestClassPrettified . "\n");
}




protected function onTest(string $name, bool $success = true): void
{
if ($success) {
$this->write(' [x] ');
} else {
$this->write(' [ ] ');
}

$this->write($name . "\n");
}




protected function endClass(string $name): void
{
$this->write("\n");
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use function array_filter;
use function get_class;
use function implode;
use function strpos;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Test as TestUtil;
use ReflectionClass;
use ReflectionException;
use Throwable;




final class XmlResultPrinter extends Printer implements TestListener
{



private $document;




private $root;




private $prettifier;




private $exception;






public function __construct($out = null)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;

$this->root = $this->document->createElement('tests');
$this->document->appendChild($this->root);

$this->prettifier = new NamePrettifier;

parent::__construct($out);
}




public function flush(): void
{
$this->write($this->document->saveXML());

parent::flush();
}




public function addError(Test $test, Throwable $t, float $time): void
{
$this->exception = $t;
}




public function addWarning(Test $test, Warning $e, float $time): void
{
}




public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->exception = $e;
}




public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
}




public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
}




public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
}




public function startTestSuite(TestSuite $suite): void
{
}




public function endTestSuite(TestSuite $suite): void
{
}




public function startTest(Test $test): void
{
$this->exception = null;
}






public function endTest(Test $test, float $time): void
{
if (!$test instanceof TestCase || $test instanceof WarningTestCase) {
return;
}

$groups = array_filter(
$test->getGroups(),
static function ($group)
{
return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0);
},
);

$testNode = $this->document->createElement('test');

$testNode->setAttribute('className', get_class($test));
$testNode->setAttribute('methodName', $test->getName());
$testNode->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(get_class($test)));
$testNode->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test));
$testNode->setAttribute('status', (string) $test->getStatus());
$testNode->setAttribute('time', (string) $time);
$testNode->setAttribute('size', (string) $test->getSize());
$testNode->setAttribute('groups', implode(',', $groups));

foreach ($groups as $group) {
$groupNode = $this->document->createElement('group');

$groupNode->setAttribute('name', $group);

$testNode->appendChild($groupNode);
}

$annotations = TestUtil::parseTestMethodAnnotations(
get_class($test),
$test->getName(false),
);

foreach (['class', 'method'] as $type) {
foreach ($annotations[$type] as $annotation => $values) {
if ($annotation !== 'covers' && $annotation !== 'uses') {
continue;
}

foreach ($values as $value) {
$coversNode = $this->document->createElement($annotation);

$coversNode->setAttribute('target', $value);

$testNode->appendChild($coversNode);
}
}
}

foreach ($test->doubledTypes() as $doubledType) {
$testDoubleNode = $this->document->createElement('testDouble');

$testDoubleNode->setAttribute('type', $doubledType);

$testNode->appendChild($testDoubleNode);
}

$inlineAnnotations = TestUtil::getInlineAnnotations(get_class($test), $test->getName(false));

if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) {
$testNode->setAttribute('given', $inlineAnnotations['given']['value']);
$testNode->setAttribute('givenStartLine', (string) $inlineAnnotations['given']['line']);
$testNode->setAttribute('when', $inlineAnnotations['when']['value']);
$testNode->setAttribute('whenStartLine', (string) $inlineAnnotations['when']['line']);
$testNode->setAttribute('then', $inlineAnnotations['then']['value']);
$testNode->setAttribute('thenStartLine', (string) $inlineAnnotations['then']['line']);
}

if ($this->exception !== null) {
if ($this->exception instanceof Exception) {
$steps = $this->exception->getSerializableTrace();
} else {
$steps = $this->exception->getTrace();
}

try {
$file = (new ReflectionClass($test))->getFileName();

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}


foreach ($steps as $step) {
if (isset($step['file']) && $step['file'] === $file) {
$testNode->setAttribute('exceptionLine', (string) $step['line']);

break;
}
}

$testNode->setAttribute('exceptionMessage', $this->exception->getMessage());
}

$this->root->appendChild($testNode);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use const PHP_EOL;
use function array_map;
use function get_class;
use function implode;
use function method_exists;
use function preg_split;
use function trim;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Reorderable;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\TextUI\DefaultResultPrinter;
use Throwable;




class TestDoxPrinter extends DefaultResultPrinter
{



protected $prettifier;




protected $testIndex = 0;




protected $testFlushIndex = 0;




protected $testResults = [];




protected $testNameResultIndex = [];




protected $enableOutputBuffer = false;




protected $originalExecutionOrder = [];




protected $spinState = 0;




protected $showProgress = true;







public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
{
parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);

$this->prettifier = new NamePrettifier($this->colors);
}

public function setOriginalExecutionOrder(array $order): void
{
$this->originalExecutionOrder = $order;
$this->enableOutputBuffer = !empty($order);
}

public function setShowProgressAnimation(bool $showProgress): void
{
$this->showProgress = $showProgress;
}

public function printResult(TestResult $result): void
{
}




public function endTest(Test $test, float $time): void
{
if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) {
return;
}

if ($this->testHasPassed()) {
$this->registerTestResult($test, null, BaseTestRunner::STATUS_PASSED, $time, false);
}

if ($test instanceof TestCase || $test instanceof PhptTestCase) {
$this->testIndex++;
}

parent::endTest($test, $time);
}




public function addError(Test $test, Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, true);
}




public function addWarning(Test $test, Warning $e, float $time): void
{
$this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, true);
}




public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, true);
}




public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, false);
}




public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, false);
}




public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_SKIPPED, $time, false);
}

public function writeProgress(string $progress): void
{
$this->flushOutputBuffer();
}

public function flush(): void
{
$this->flushOutputBuffer(true);
}




protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose): void
{
$testName = $test instanceof Reorderable ? $test->sortId() : $test->getName();

$result = [
'className' => $this->formatClassName($test),
'testName' => $testName,
'testMethod' => $this->formatTestName($test),
'message' => '',
'status' => $status,
'time' => $time,
'verbose' => $verbose,
];

if ($t !== null) {
$result['message'] = $this->formatTestResultMessage($t, $result);
}

$this->testResults[$this->testIndex] = $result;
$this->testNameResultIndex[$testName] = $this->testIndex;
}

protected function formatTestName(Test $test): string
{
return method_exists($test, 'getName') ? $test->getName() : '';
}

protected function formatClassName(Test $test): string
{
return get_class($test);
}

protected function testHasPassed(): bool
{
if (!isset($this->testResults[$this->testIndex]['status'])) {
return true;
}

if ($this->testResults[$this->testIndex]['status'] === BaseTestRunner::STATUS_PASSED) {
return true;
}

return false;
}

protected function flushOutputBuffer(bool $forceFlush = false): void
{
if ($this->testFlushIndex === $this->testIndex) {
return;
}

if ($this->testFlushIndex > 0) {
if ($this->enableOutputBuffer &&
isset($this->originalExecutionOrder[$this->testFlushIndex - 1])) {
$prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]);
} else {
$prevResult = $this->testResults[$this->testFlushIndex - 1];
}
} else {
$prevResult = $this->getEmptyTestResult();
}

if (!$this->enableOutputBuffer) {
$this->writeTestResult($prevResult, $this->testResults[$this->testFlushIndex++]);
} else {
do {
$flushed = false;

if (!$forceFlush && isset($this->originalExecutionOrder[$this->testFlushIndex])) {
$result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]);
} else {


$result = $this->testResults[$this->testFlushIndex];
}

if (!empty($result)) {
$this->hideSpinner();
$this->writeTestResult($prevResult, $result);
$this->testFlushIndex++;
$prevResult = $result;
$flushed = true;
} else {
$this->showSpinner();
}
} while ($flushed && $this->testFlushIndex < $this->testIndex);
}
}

protected function showSpinner(): void
{
if (!$this->showProgress) {
return;
}

if ($this->spinState) {
$this->undrawSpinner();
}

$this->spinState++;
$this->drawSpinner();
}

protected function hideSpinner(): void
{
if (!$this->showProgress) {
return;
}

if ($this->spinState) {
$this->undrawSpinner();
}

$this->spinState = 0;
}

protected function drawSpinner(): void
{

}

protected function undrawSpinner(): void
{

}

protected function writeTestResult(array $prevResult, array $result): void
{
}

protected function getEmptyTestResult(): array
{
return [
'className' => '',
'testName' => '',
'message' => '',
'failed' => '',
'verbose' => '',
];
}

protected function getTestResultByName(?string $testName): array
{
if (isset($this->testNameResultIndex[$testName])) {
return $this->testResults[$this->testNameResultIndex[$testName]];
}

return [];
}

protected function formatThrowable(Throwable $t, ?int $status = null): string
{
$message = trim(\PHPUnit\Framework\TestFailure::exceptionToString($t));

if ($message) {
$message .= PHP_EOL . PHP_EOL . $this->formatStacktrace($t);
} else {
$message = $this->formatStacktrace($t);
}

return $message;
}

protected function formatStacktrace(Throwable $t): string
{
return \PHPUnit\Util\Filter::getFilteredStacktrace($t);
}

protected function formatTestResultMessage(Throwable $t, array $result, string $prefix = '│'): string
{
$message = $this->formatThrowable($t, $result['status']);

if ($message === '') {
return '';
}

if (!($this->verbose || $result['verbose'])) {
return '';
}

return $this->prefixLines($prefix, $message);
}

protected function prefixLines(string $prefix, string $message): string
{
$message = trim($message);

return implode(
PHP_EOL,
array_map(
static function (string $text) use ($prefix)
{
return '   ' . $prefix . ($text ? ' ' . $text : '');
},
preg_split('/\r\n|\r|\n/', $message),
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use const PHP_EOL;
use function array_map;
use function ceil;
use function count;
use function explode;
use function get_class;
use function implode;
use function preg_match;
use function sprintf;
use function strlen;
use function strpos;
use function trim;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestResult;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Color;
use SebastianBergmann\Timer\ResourceUsageFormatter;
use SebastianBergmann\Timer\Timer;
use Throwable;




class CliTestDoxPrinter extends TestDoxPrinter
{



private const PREFIX_SIMPLE = [
'default' => '│',
'start' => '│',
'message' => '│',
'diff' => '│',
'trace' => '│',
'last' => '│',
];




private const PREFIX_DECORATED = [
'default' => '│',
'start' => '┐',
'message' => '├',
'diff' => '┊',
'trace' => '╵',
'last' => '┴',
];

private const SPINNER_ICONS = [
" \e[36m◐\e[0m running tests",
" \e[36m◓\e[0m running tests",
" \e[36m◑\e[0m running tests",
" \e[36m◒\e[0m running tests",
];
private const STATUS_STYLES = [
BaseTestRunner::STATUS_PASSED => [
'symbol' => '✔',
'color' => 'fg-green',
],
BaseTestRunner::STATUS_ERROR => [
'symbol' => '✘',
'color' => 'fg-yellow',
'message' => 'bg-yellow,fg-black',
],
BaseTestRunner::STATUS_FAILURE => [
'symbol' => '✘',
'color' => 'fg-red',
'message' => 'bg-red,fg-white',
],
BaseTestRunner::STATUS_SKIPPED => [
'symbol' => '↩',
'color' => 'fg-cyan',
'message' => 'fg-cyan',
],
BaseTestRunner::STATUS_RISKY => [
'symbol' => '☢',
'color' => 'fg-yellow',
'message' => 'fg-yellow',
],
BaseTestRunner::STATUS_INCOMPLETE => [
'symbol' => '∅',
'color' => 'fg-yellow',
'message' => 'fg-yellow',
],
BaseTestRunner::STATUS_WARNING => [
'symbol' => '⚠',
'color' => 'fg-yellow',
'message' => 'fg-yellow',
],
BaseTestRunner::STATUS_UNKNOWN => [
'symbol' => '?',
'color' => 'fg-blue',
'message' => 'fg-white,bg-blue',
],
];




private $nonSuccessfulTestResults = [];




private $timer;







public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
{
parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);

$this->timer = new Timer;

$this->timer->start();
}

public function printResult(TestResult $result): void
{
$this->printHeader($result);

$this->printNonSuccessfulTestsSummary($result->count());

$this->printFooter($result);
}

protected function printHeader(TestResult $result): void
{
$this->write("\n" . (new ResourceUsageFormatter)->resourceUsage($this->timer->stop()) . "\n\n");
}

protected function formatClassName(Test $test): string
{
if ($test instanceof TestCase) {
return $this->prettifier->prettifyTestClass(get_class($test));
}

return get_class($test);
}




protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose): void
{
if ($status !== BaseTestRunner::STATUS_PASSED) {
$this->nonSuccessfulTestResults[] = $this->testIndex;
}

parent::registerTestResult($test, $t, $status, $time, $verbose);
}




protected function formatTestName(Test $test): string
{
if ($test instanceof TestCase) {
return $this->prettifier->prettifyTestCase($test);
}

return parent::formatTestName($test);
}

protected function writeTestResult(array $prevResult, array $result): void
{

if ($prevResult['testName'] !== '' &&
(!empty($prevResult['message']) || $prevResult['className'] !== $result['className'])) {
$this->write(PHP_EOL);
}


if ($prevResult['className'] !== $result['className']) {
$this->write($this->colorizeTextBox('underlined', $result['className']) . PHP_EOL);
}


if ($this->colors && $result['className'] === PhptTestCase::class) {
$testName = Color::colorizePath($result['testName'], $prevResult['testName'], true);
} else {
$testName = $result['testMethod'];
}

$style = self::STATUS_STYLES[$result['status']];
$line = sprintf(
' %s %s%s' . PHP_EOL,
$this->colorizeTextBox($style['color'], $style['symbol']),
$testName,
$this->verbose ? ' ' . $this->formatRuntime($result['time'], $style['color']) : '',
);

$this->write($line);


$this->write($result['message']);
}

protected function formatThrowable(Throwable $t, ?int $status = null): string
{
return trim(\PHPUnit\Framework\TestFailure::exceptionToString($t));
}

protected function colorizeMessageAndDiff(string $style, string $buffer): array
{
$lines = $buffer ? array_map('\rtrim', explode(PHP_EOL, $buffer)) : [];
$message = [];
$diff = [];
$insideDiff = false;

foreach ($lines as $line) {
if ($line === '--- Expected') {
$insideDiff = true;
}

if (!$insideDiff) {
$message[] = $line;
} else {
if (strpos($line, '-') === 0) {
$line = Color::colorize('fg-red', Color::visualizeWhitespace($line, true));
} elseif (strpos($line, '+') === 0) {
$line = Color::colorize('fg-green', Color::visualizeWhitespace($line, true));
} elseif ($line === '@@ @@') {
$line = Color::colorize('fg-cyan', $line);
}
$diff[] = $line;
}
}
$diff = implode(PHP_EOL, $diff);

if (!empty($message)) {
$message = $this->colorizeTextBox($style, implode(PHP_EOL, $message));
}

return [$message, $diff];
}

protected function formatStacktrace(Throwable $t): string
{
$trace = \PHPUnit\Util\Filter::getFilteredStacktrace($t);

if (!$this->colors) {
return $trace;
}

$lines = [];
$prevPath = '';

foreach (explode(PHP_EOL, $trace) as $line) {
if (preg_match('/^(.*):(\d+)$/', $line, $matches)) {
$lines[] = Color::colorizePath($matches[1], $prevPath) .
Color::dim(':') .
Color::colorize('fg-blue', $matches[2]) .
"\n";
$prevPath = $matches[1];
} else {
$lines[] = $line;
$prevPath = '';
}
}

return implode('', $lines);
}

protected function formatTestResultMessage(Throwable $t, array $result, ?string $prefix = null): string
{
$message = $this->formatThrowable($t, $result['status']);
$diff = '';

if (!($this->verbose || $result['verbose'])) {
return '';
}

if ($message && $this->colors) {
$style = self::STATUS_STYLES[$result['status']]['message'] ?? '';
[$message, $diff] = $this->colorizeMessageAndDiff($style, $message);
}

if ($prefix === null || !$this->colors) {
$prefix = self::PREFIX_SIMPLE;
}

if ($this->colors) {
$color = self::STATUS_STYLES[$result['status']]['color'] ?? '';
$prefix = array_map(static function ($p) use ($color)
{
return Color::colorize($color, $p);
}, self::PREFIX_DECORATED);
}

$trace = $this->formatStacktrace($t);
$out = $this->prefixLines($prefix['start'], PHP_EOL) . PHP_EOL;

if ($message) {
$out .= $this->prefixLines($prefix['message'], $message . PHP_EOL) . PHP_EOL;
}

if ($diff) {
$out .= $this->prefixLines($prefix['diff'], $diff . PHP_EOL) . PHP_EOL;
}

if ($trace) {
if ($message || $diff) {
$out .= $this->prefixLines($prefix['default'], PHP_EOL) . PHP_EOL;
}
$out .= $this->prefixLines($prefix['trace'], $trace . PHP_EOL) . PHP_EOL;
}
$out .= $this->prefixLines($prefix['last'], PHP_EOL) . PHP_EOL;

return $out;
}

protected function drawSpinner(): void
{
if ($this->colors) {
$id = $this->spinState % count(self::SPINNER_ICONS);
$this->write(self::SPINNER_ICONS[$id]);
}
}

protected function undrawSpinner(): void
{
if ($this->colors) {
$id = $this->spinState % count(self::SPINNER_ICONS);
$this->write("\e[1K\e[" . strlen(self::SPINNER_ICONS[$id]) . 'D');
}
}

private function formatRuntime(float $time, string $color = ''): string
{
if (!$this->colors) {
return sprintf('[%.2f ms]', $time * 1000);
}

if ($time > 1) {
$color = 'fg-magenta';
}

return Color::colorize($color, ' ' . (int) ceil($time * 1000) . ' ' . Color::dim('ms'));
}

private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests): void
{
if (empty($this->nonSuccessfulTestResults)) {
return;
}

if ((count($this->nonSuccessfulTestResults) / $numberOfExecutedTests) >= 0.7) {
return;
}

$this->write("Summary of non-successful tests:\n\n");

$prevResult = $this->getEmptyTestResult();

foreach ($this->nonSuccessfulTestResults as $testIndex) {
$result = $this->testResults[$testIndex];
$this->writeTestResult($prevResult, $result);
$prevResult = $result;
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use function array_key_exists;
use function array_keys;
use function array_map;
use function array_pop;
use function array_values;
use function explode;
use function get_class;
use function gettype;
use function implode;
use function in_array;
use function is_bool;
use function is_float;
use function is_int;
use function is_numeric;
use function is_object;
use function is_scalar;
use function is_string;
use function ord;
use function preg_quote;
use function preg_replace;
use function range;
use function sprintf;
use function str_replace;
use function strlen;
use function strpos;
use function strtolower;
use function strtoupper;
use function substr;
use function trim;
use PHPUnit\Framework\TestCase;
use PHPUnit\Util\Color;
use PHPUnit\Util\Exception as UtilException;
use PHPUnit\Util\Test;
use ReflectionException;
use ReflectionMethod;
use ReflectionObject;
use SebastianBergmann\Exporter\Exporter;




final class NamePrettifier
{



private $strings = [];




private $useColor;

public function __construct(bool $useColor = false)
{
$this->useColor = $useColor;
}






public function prettifyTestClass(string $className): string
{
try {
$annotations = Test::parseTestMethodAnnotations($className);

if (isset($annotations['class']['testdox'][0])) {
return $annotations['class']['testdox'][0];
}
} catch (UtilException $e) {

}

$parts = explode('\\', $className);
$className = array_pop($parts);

if (substr($className, -1 * strlen('Test')) === 'Test') {
$className = substr($className, 0, strlen($className) - strlen('Test'));
}

if (strpos($className, 'Tests') === 0) {
$className = substr($className, strlen('Tests'));
} elseif (strpos($className, 'Test') === 0) {
$className = substr($className, strlen('Test'));
}

if (empty($className)) {
$className = 'UnnamedTests';
}

if (!empty($parts)) {
$parts[] = $className;
$fullyQualifiedName = implode('\\', $parts);
} else {
$fullyQualifiedName = $className;
}

$result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className);

if ($fullyQualifiedName !== $className) {
return $result . ' (' . $fullyQualifiedName . ')';
}

return $result;
}




public function prettifyTestCase(TestCase $test): string
{
$annotations = Test::parseTestMethodAnnotations(
get_class($test),
$test->getName(false),
);

$annotationWithPlaceholders = false;

$callback = static function (string $variable): string
{
return sprintf('/%s(?=\b)/', preg_quote($variable, '/'));
};

if (isset($annotations['method']['testdox'][0])) {
$result = $annotations['method']['testdox'][0];

if (strpos($result, '$') !== false) {
$annotation = $annotations['method']['testdox'][0];
$providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test);
$variables = array_map($callback, array_keys($providedData));

$result = trim(preg_replace($variables, $providedData, $annotation));

$annotationWithPlaceholders = true;
}
} else {
$result = $this->prettifyTestMethod($test->getName(false));
}

if (!$annotationWithPlaceholders && $test->usesDataProvider()) {
$result .= $this->prettifyDataSet($test);
}

return $result;
}

public function prettifyDataSet(TestCase $test): string
{
if (!$this->useColor) {
return $test->getDataSetAsString(false);
}

if (is_int($test->dataName())) {
$data = Color::dim(' with data set ') . Color::colorize('fg-cyan', (string) $test->dataName());
} else {
$data = Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $test->dataName()));
}

return $data;
}




public function prettifyTestMethod(string $name): string
{
$buffer = '';

if ($name === '') {
return $buffer;
}

$string = (string) preg_replace('#\d+$#', '', $name, -1, $count);

if (in_array($string, $this->strings, true)) {
$name = $string;
} elseif ($count === 0) {
$this->strings[] = $string;
}

if (strpos($name, 'test_') === 0) {
$name = substr($name, 5);
} elseif (strpos($name, 'test') === 0) {
$name = substr($name, 4);
}

if ($name === '') {
return $buffer;
}

$name[0] = strtoupper($name[0]);

if (strpos($name, '_') !== false) {
return trim(str_replace('_', ' ', $name));
}

$wasNumeric = false;

foreach (range(0, strlen($name) - 1) as $i) {
if ($i > 0 && ord($name[$i]) >= 65 && ord($name[$i]) <= 90) {
$buffer .= ' ' . strtolower($name[$i]);
} else {
$isNumeric = is_numeric($name[$i]);

if (!$wasNumeric && $isNumeric) {
$buffer .= ' ';
$wasNumeric = true;
}

if ($wasNumeric && !$isNumeric) {
$wasNumeric = false;
}

$buffer .= $name[$i];
}
}

return $buffer;
}




private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test): array
{
try {
$reflector = new ReflectionMethod(get_class($test), $test->getName(false));

} catch (ReflectionException $e) {
throw new UtilException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


$providedData = [];
$providedDataValues = array_values($test->getProvidedData());
$i = 0;

$providedData['$_dataName'] = $test->dataName();

foreach ($reflector->getParameters() as $parameter) {
if (!array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) {
try {
$providedDataValues[$i] = $parameter->getDefaultValue();

} catch (ReflectionException $e) {
throw new UtilException(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}

$value = $providedDataValues[$i++] ?? null;

if (is_object($value)) {
$reflector = new ReflectionObject($value);

if ($reflector->hasMethod('__toString')) {
$value = (string) $value;
} else {
$value = get_class($value);
}
}

if (!is_scalar($value)) {
$value = gettype($value);
}

if (is_bool($value) || is_int($value) || is_float($value)) {
$value = (new Exporter)->export($value);
}

if (is_string($value) && $value === '') {
if ($this->useColor) {
$value = Color::colorize('dim,underlined', 'empty');
} else {
$value = "''";
}
}

$providedData['$' . $parameter->getName()] = $value;
}

if ($this->useColor) {
$providedData = array_map(static function ($value)
{
return Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, true));
}, $providedData);
}

return $providedData;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use function get_class;
use function in_array;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ErrorTestCase;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\TextUI\ResultPrinter as ResultPrinterInterface;
use PHPUnit\Util\Printer;
use Throwable;




abstract class ResultPrinter extends Printer implements ResultPrinterInterface
{



protected $prettifier;




protected $testClass = '';




protected $testStatus;




protected $tests = [];




protected $successful = 0;




protected $warned = 0;




protected $failed = 0;




protected $risky = 0;




protected $skipped = 0;




protected $incomplete = 0;




protected $currentTestClassPrettified;




protected $currentTestMethodPrettified;




private $groups;




private $excludeGroups;






public function __construct($out = null, array $groups = [], array $excludeGroups = [])
{
parent::__construct($out);

$this->groups = $groups;
$this->excludeGroups = $excludeGroups;

$this->prettifier = new NamePrettifier;
$this->startRun();
}




public function flush(): void
{
$this->doEndClass();
$this->endRun();

parent::flush();
}




public function addError(Test $test, Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->testStatus = BaseTestRunner::STATUS_ERROR;
$this->failed++;
}




public function addWarning(Test $test, Warning $e, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->testStatus = BaseTestRunner::STATUS_WARNING;
$this->warned++;
}




public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->testStatus = BaseTestRunner::STATUS_FAILURE;
$this->failed++;
}




public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->testStatus = BaseTestRunner::STATUS_INCOMPLETE;
$this->incomplete++;
}




public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->testStatus = BaseTestRunner::STATUS_RISKY;
$this->risky++;
}




public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->testStatus = BaseTestRunner::STATUS_SKIPPED;
$this->skipped++;
}




public function startTestSuite(TestSuite $suite): void
{
}




public function endTestSuite(TestSuite $suite): void
{
}






public function startTest(Test $test): void
{
if (!$this->isOfInterest($test)) {
return;
}

$class = get_class($test);

if ($this->testClass !== $class) {
if ($this->testClass !== '') {
$this->doEndClass();
}

$this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class);
$this->testClass = $class;
$this->tests = [];

$this->startClass($class);
}

if ($test instanceof TestCase) {
$this->currentTestMethodPrettified = $this->prettifier->prettifyTestCase($test);
}

$this->testStatus = BaseTestRunner::STATUS_PASSED;
}




public function endTest(Test $test, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}

$this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus];

$this->currentTestClassPrettified = null;
$this->currentTestMethodPrettified = null;
}

protected function doEndClass(): void
{
foreach ($this->tests as $test) {
$this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED);
}

$this->endClass($this->testClass);
}




protected function startRun(): void
{
}




protected function startClass(string $name): void
{
}




protected function onTest(string $name, bool $success = true): void
{
}




protected function endClass(string $name): void
{
}




protected function endRun(): void
{
}

private function isOfInterest(Test $test): bool
{
if (!$test instanceof TestCase) {
return false;
}

if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) {
return false;
}

if (!empty($this->groups)) {
foreach ($test->getGroups() as $group) {
if (in_array($group, $this->groups, true)) {
return true;
}
}

return false;
}

if (!empty($this->excludeGroups)) {
foreach ($test->getGroups() as $group) {
if (in_array($group, $this->excludeGroups, true)) {
return false;
}
}

return true;
}

return true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\TestDox;

use function sprintf;
use PHPUnit\Framework\TestResult;




final class HtmlResultPrinter extends ResultPrinter
{



private const PAGE_HEADER = <<<'EOT'
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8"/>
        <title>Test Documentation</title>
        <style>
            body {
                text-rendering: optimizeLegibility;
                font-family: Source SansSerif Pro, Arial, sans-serif;
                font-variant-ligatures: common-ligatures;
                font-kerning: normal;
                margin-left: 2rem;
                background-color: #fff;
                color: #000;
            }

            body > ul > li {
                font-size: larger;
            }

            h2 {
                font-size: larger;
                text-decoration-line: underline;
                text-decoration-thickness: 2px;
                margin: 0;
                padding: 0.5rem 0;
            }

            ul {
                list-style: none;
                margin: 0 0 2rem;
                padding: 0 0 0 1rem;
                text-indent: -1rem;
            }

            .success:before {
                color: #4e9a06;
                content: '✓';
                padding-right: 0.5rem;
            }

            .defect {
                color: #a40000;
            }

            .defect:before {
                color: #a40000;
                content: '✗';
                padding-right: 0.5rem;
            }
        </style>
    </head>
    <body>
EOT;




private const CLASS_HEADER = <<<'EOT'

        <h2>%s</h2>
        <ul>

EOT;




private const CLASS_FOOTER = <<<'EOT'
        </ul>
EOT;




private const PAGE_FOOTER = <<<'EOT'

    </body>
</html>
EOT;

public function printResult(TestResult $result): void
{
}




protected function startRun(): void
{
$this->write(self::PAGE_HEADER);
}




protected function startClass(string $name): void
{
$this->write(
sprintf(
self::CLASS_HEADER,
$this->currentTestClassPrettified,
),
);
}




protected function onTest(string $name, bool $success = true): void
{
$this->write(
sprintf(
"            <li class=\"%s\">%s</li>\n",
$success ? 'success' : 'defect',
$name,
),
);
}




protected function endClass(string $name): void
{
$this->write(self::CLASS_FOOTER);
}




protected function endRun(): void
{
$this->write(self::PAGE_FOOTER);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;




final class SchemaDetector
{



public function detect(string $filename): SchemaDetectionResult
{
$document = (new Loader)->loadFile(
$filename,
false,
true,
true,
);

$schemaFinder = new SchemaFinder;

foreach ($schemaFinder->available() as $candidate) {
$schema = (new SchemaFinder)->find($candidate);

if (!(new Validator)->validate($document, $schema)->hasValidationErrors()) {
return new SuccessfulSchemaDetectionResult($candidate);
}
}

return new FailedSchemaDetectionResult;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;

use function count;
use ArrayIterator;
use Countable;
use DOMNode;
use DOMNodeList;
use IteratorAggregate;






final class SnapshotNodeList implements Countable, IteratorAggregate
{



private $nodes = [];

public static function fromNodeList(DOMNodeList $list): self
{
$snapshot = new self;

foreach ($list as $node) {
$snapshot->nodes[] = $node;
}

return $snapshot;
}

public function count(): int
{
return count($this->nodes);
}

public function getIterator(): ArrayIterator
{
return new ArrayIterator($this->nodes);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;

use RuntimeException;




final class Exception extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;

use function file_get_contents;
use function libxml_clear_errors;
use function libxml_get_errors;
use function libxml_use_internal_errors;
use DOMDocument;




final class Validator
{
public function validate(DOMDocument $document, string $xsdFilename): ValidationResult
{
$originalErrorHandling = libxml_use_internal_errors(true);

$document->schemaValidateSource(file_get_contents($xsdFilename));

$errors = libxml_get_errors();
libxml_clear_errors();
libxml_use_internal_errors($originalErrorHandling);

return ValidationResult::fromArray($errors);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;

use function assert;
use function defined;
use function is_file;
use function rsort;
use function sprintf;
use DirectoryIterator;
use PHPUnit\Runner\Version;




final class SchemaFinder
{



public function available(): array
{
$result = [Version::series()];

foreach ((new DirectoryIterator($this->path() . 'schema')) as $file) {
if ($file->isDot()) {
continue;
}

$version = $file->getBasename('.xsd');

assert(!empty($version));

$result[] = $version;
}

rsort($result);

return $result;
}




public function find(string $version): string
{
if ($version === Version::series()) {
$filename = $this->path() . 'phpunit.xsd';
} else {
$filename = $this->path() . 'schema/' . $version . '.xsd';
}

if (!is_file($filename)) {
throw new Exception(
sprintf(
'Schema for PHPUnit %s is not available',
$version,
),
);
}

return $filename;
}

private function path(): string
{
if (defined('__PHPUNIT_PHAR_ROOT__')) {
return __PHPUNIT_PHAR_ROOT__ . '/';
}

return __DIR__ . '/../../../';
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;

use function sprintf;
use function trim;






final class ValidationResult
{



private $validationErrors = [];




public static function fromArray(array $errors): self
{
$validationErrors = [];

foreach ($errors as $error) {
if (!isset($validationErrors[$error->line])) {
$validationErrors[$error->line] = [];
}

$validationErrors[$error->line][] = trim($error->message);
}

return new self($validationErrors);
}

private function __construct(array $validationErrors)
{
$this->validationErrors = $validationErrors;
}

public function hasValidationErrors(): bool
{
return !empty($this->validationErrors);
}

public function asString(): string
{
$buffer = '';

foreach ($this->validationErrors as $line => $validationErrorsOnLine) {
$buffer .= sprintf(PHP_EOL . '  Line %d:' . PHP_EOL, $line);

foreach ($validationErrorsOnLine as $validationError) {
$buffer .= sprintf('  - %s' . PHP_EOL, $validationError);
}
}

return $buffer;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;






abstract class SchemaDetectionResult
{



public function detected(): bool
{
return false;
}




public function version(): string
{
throw new Exception('No supported schema was detected');
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;






final class SuccessfulSchemaDetectionResult extends SchemaDetectionResult
{



private $version;




public function __construct(string $version)
{
$this->version = $version;
}




public function detected(): bool
{
return true;
}




public function version(): string
{
return $this->version;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;

use function chdir;
use function dirname;
use function error_reporting;
use function file_get_contents;
use function getcwd;
use function libxml_get_errors;
use function libxml_use_internal_errors;
use function sprintf;
use DOMDocument;




final class Loader
{



public function loadFile(string $filename, bool $isHtml = false, bool $xinclude = false, bool $strict = false): DOMDocument
{
$reporting = error_reporting(0);
$contents = file_get_contents($filename);

error_reporting($reporting);

if ($contents === false) {
throw new Exception(
sprintf(
'Could not read "%s".',
$filename,
),
);
}

return $this->load($contents, $isHtml, $filename, $xinclude, $strict);
}




public function load(string $actual, bool $isHtml = false, string $filename = '', bool $xinclude = false, bool $strict = false): DOMDocument
{
if ($actual === '') {
throw new Exception('Could not load XML from empty string');
}


if ($xinclude) {
$cwd = getcwd();
@chdir(dirname($filename));
}

$document = new DOMDocument;
$document->preserveWhiteSpace = false;

$internal = libxml_use_internal_errors(true);
$message = '';
$reporting = error_reporting(0);

if ($filename !== '') {

$document->documentURI = $filename;
}

if ($isHtml) {
$loaded = $document->loadHTML($actual);
} else {
$loaded = $document->loadXML($actual);
}

if (!$isHtml && $xinclude) {
$document->xinclude();
}

foreach (libxml_get_errors() as $error) {
$message .= "\n" . $error->message;
}

libxml_use_internal_errors($internal);
error_reporting($reporting);

if (isset($cwd)) {
@chdir($cwd);
}

if ($loaded === false || ($strict && $message !== '')) {
if ($filename !== '') {
throw new Exception(
sprintf(
'Could not load "%s".%s',
$filename,
$message !== '' ? "\n" . $message : '',
),
);
}

if ($message === '') {
$message = 'Could not load XML for unknown reason';
}

throw new Exception($message);
}

return $document;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util\Xml;






final class FailedSchemaDetectionResult extends SchemaDetectionResult
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use RuntimeException;




final class Exception extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
use function count;
use function is_array;
use function is_object;
use function json_decode;
use function json_encode;
use function json_last_error;
use function ksort;
use PHPUnit\Framework\Exception;




final class Json
{





public static function prettify(string $json): string
{
$decodedJson = json_decode($json, false);

if (json_last_error()) {
throw new Exception(
'Cannot prettify invalid json',
);
}

return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}









public static function canonicalize(string $json): array
{
$decodedJson = json_decode($json);

if (json_last_error()) {
return [true, null];
}

self::recursiveSort($decodedJson);

$reencodedJson = json_encode($decodedJson);

return [false, $reencodedJson];
}







private static function recursiveSort(&$json): void
{
if (!is_array($json)) {






if (is_object($json) && count((array) $json) > 0) {
$json = (array) $json;
} else {
return;
}
}

ksort($json);

foreach ($json as $key => &$value) {
self::recursiveSort($value);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const E_DEPRECATED;
use const E_NOTICE;
use const E_STRICT;
use const E_USER_DEPRECATED;
use const E_USER_NOTICE;
use const E_USER_WARNING;
use const E_WARNING;
use function error_reporting;
use function restore_error_handler;
use function set_error_handler;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;




final class ErrorHandler
{



private $convertDeprecationsToExceptions;




private $convertErrorsToExceptions;




private $convertNoticesToExceptions;




private $convertWarningsToExceptions;




private $registered = false;

public static function invokeIgnoringWarnings(callable $callable)
{
set_error_handler(
static function ($errorNumber, $errorString)
{
if ($errorNumber === E_WARNING) {
return;
}

return false;
},
);

$result = $callable();

restore_error_handler();

return $result;
}

public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions)
{
$this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions;
$this->convertErrorsToExceptions = $convertErrorsToExceptions;
$this->convertNoticesToExceptions = $convertNoticesToExceptions;
$this->convertWarningsToExceptions = $convertWarningsToExceptions;
}

public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool
{





if (!($errorNumber & error_reporting())) {
return false;
}

switch ($errorNumber) {
case E_NOTICE:
case E_USER_NOTICE:
case E_STRICT:
if (!$this->convertNoticesToExceptions) {
return false;
}

throw new Notice($errorString, $errorNumber, $errorFile, $errorLine);

case E_WARNING:
case E_USER_WARNING:
if (!$this->convertWarningsToExceptions) {
return false;
}

throw new Warning($errorString, $errorNumber, $errorFile, $errorLine);

case E_DEPRECATED:
case E_USER_DEPRECATED:
if (!$this->convertDeprecationsToExceptions) {
return false;
}

throw new Deprecated($errorString, $errorNumber, $errorFile, $errorLine);

default:
if (!$this->convertErrorsToExceptions) {
return false;
}

throw new Error($errorString, $errorNumber, $errorFile, $errorLine);
}
}

public function register(): void
{
if ($this->registered) {
return;
}

$oldErrorHandler = set_error_handler($this);

if ($oldErrorHandler !== null) {
restore_error_handler();

return;
}

$this->registered = true;
}

public function unregister(): void
{
if (!$this->registered) {
return;
}

restore_error_handler();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionMethod;




final class Reflection
{



public function publicMethodsInTestClass(ReflectionClass $class): array
{
return $this->filterMethods($class, ReflectionMethod::IS_PUBLIC);
}




public function methodsInTestClass(ReflectionClass $class): array
{
return $this->filterMethods($class, null);
}




private function filterMethods(ReflectionClass $class, ?int $filter): array
{
$methods = [];



$classMethods = $filter === null ? $class->getMethods() : $class->getMethods($filter);

foreach ($classMethods as $method) {
if ($method->getDeclaringClass()->getName() === TestCase::class) {
continue;
}

if ($method->getDeclaringClass()->getName() === Assert::class) {
continue;
}

$methods[] = $method;
}

return $methods;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use function in_array;
use function sprintf;






final class VersionComparisonOperator
{



private $operator;

public function __construct(string $operator)
{
$this->ensureOperatorIsValid($operator);

$this->operator = $operator;
}




public function asString(): string
{
return $this->operator;
}






private function ensureOperatorIsValid(string $operator): void
{
if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], true)) {
throw new Exception(
sprintf(
'"%s" is not a valid version_compare() operator',
$operator,
),
);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use function array_unshift;
use function defined;
use function in_array;
use function is_file;
use function realpath;
use function sprintf;
use function strpos;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
use Throwable;




final class Filter
{



public static function getFilteredStacktrace(Throwable $t): string
{
$filteredStacktrace = '';

if ($t instanceof SyntheticError) {
$eTrace = $t->getSyntheticTrace();
$eFile = $t->getSyntheticFile();
$eLine = $t->getSyntheticLine();
} elseif ($t instanceof Exception) {
$eTrace = $t->getSerializableTrace();
$eFile = $t->getFile();
$eLine = $t->getLine();
} else {
if ($t->getPrevious()) {
$t = $t->getPrevious();
}

$eTrace = $t->getTrace();
$eFile = $t->getFile();
$eLine = $t->getLine();
}

if (!self::frameExists($eTrace, $eFile, $eLine)) {
array_unshift(
$eTrace,
['file' => $eFile, 'line' => $eLine],
);
}

$prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : false;
$excludeList = new ExcludeList;

foreach ($eTrace as $frame) {
if (self::shouldPrintFrame($frame, $prefix, $excludeList)) {
$filteredStacktrace .= sprintf(
"%s:%s\n",
$frame['file'],
$frame['line'] ?? '?',
);
}
}

return $filteredStacktrace;
}

private static function shouldPrintFrame(array $frame, $prefix, ExcludeList $excludeList): bool
{
if (!isset($frame['file'])) {
return false;
}

$file = $frame['file'];
$fileIsNotPrefixed = $prefix === false || strpos($file, $prefix) !== 0;


if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) {
$script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']);
} else {
$script = '';
}

return is_file($file) &&
self::fileIsExcluded($file, $excludeList) &&
$fileIsNotPrefixed &&
$file !== $script;
}

private static function fileIsExcluded(string $file, ExcludeList $excludeList): bool
{
return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) ||
!in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) &&
!$excludeList->isExcluded($file);
}

private static function frameExists(array $trace, string $file, int $line): bool
{
foreach ($trace as $frame) {
if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) {
return true;
}
}

return false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use RuntimeException;




final class InvalidDataSetException extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const DIRECTORY_SEPARATOR;
use function addslashes;
use function array_map;
use function implode;
use function is_string;
use function realpath;
use function sprintf;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage as FilterConfiguration;






final class XdebugFilterScriptGenerator
{
public function generate(FilterConfiguration $filter): string
{
$files = array_map(
static function ($item)
{
return sprintf(
"        '%s'",
$item,
);
},
$this->getItems($filter),
);

$files = implode(",\n", $files);

return <<<EOF
<?php declare(strict_types=1);
if (!\\function_exists('xdebug_set_filter')) {
    return;
}

\\xdebug_set_filter(
    \\XDEBUG_FILTER_CODE_COVERAGE,
    \\XDEBUG_PATH_WHITELIST,
    [
{$files}
    ]
);

EOF;
}

private function getItems(FilterConfiguration $filter): array
{
$files = [];

foreach ($filter->directories() as $directory) {
$path = realpath($directory->path());

if (is_string($path)) {
$files[] = sprintf(
addslashes('%s' . DIRECTORY_SEPARATOR),
$path,
);
}
}

foreach ($filter->files() as $file) {
$files[] = $file->path();
}

return $files;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const DIRECTORY_SEPARATOR;
use function is_dir;
use function mkdir;
use function str_replace;




final class Filesystem
{






public static function classNameToFilename(string $className): string
{
return str_replace(
['_', '\\'],
DIRECTORY_SEPARATOR,
$className,
) . '.php';
}

public static function createDirectory(string $directory): bool
{
return !(!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory));
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const DIRECTORY_SEPARATOR;
use function class_exists;
use function defined;
use function dirname;
use function is_dir;
use function realpath;
use function sprintf;
use function strpos;
use function sys_get_temp_dir;
use Composer\Autoload\ClassLoader;
use DeepCopy\DeepCopy;
use Doctrine\Instantiator\Instantiator;
use PharIo\Manifest\Manifest;
use PharIo\Version\Version as PharIoVersion;
use PhpParser\Parser;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use SebastianBergmann\CliParser\Parser as CliParser;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeUnit\CodeUnit;
use SebastianBergmann\CodeUnitReverseLookup\Wizard;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Complexity\Calculator;
use SebastianBergmann\Diff\Diff;
use SebastianBergmann\Environment\Runtime;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
use SebastianBergmann\GlobalState\Snapshot;
use SebastianBergmann\Invoker\Invoker;
use SebastianBergmann\LinesOfCode\Counter;
use SebastianBergmann\ObjectEnumerator\Enumerator;
use SebastianBergmann\ObjectReflector\ObjectReflector;
use SebastianBergmann\RecursionContext\Context;
use SebastianBergmann\ResourceOperations\ResourceOperations;
use SebastianBergmann\Template\Template;
use SebastianBergmann\Timer\Timer;
use SebastianBergmann\Type\TypeName;
use SebastianBergmann\Version;
use TheSeer\Tokenizer\Tokenizer;




final class ExcludeList
{



private const EXCLUDED_CLASS_NAMES = [

ClassLoader::class => 1,


Instantiator::class => 1,


DeepCopy::class => 1,


Parser::class => 1,


Manifest::class => 1,


PharIoVersion::class => 1,


Type::class => 1,


TestCase::class => 2,


CodeCoverage::class => 1,


FileIteratorFacade::class => 1,


Invoker::class => 1,


Template::class => 1,


Timer::class => 1,


CliParser::class => 1,


CodeUnit::class => 1,


Wizard::class => 1,


Comparator::class => 1,


Calculator::class => 1,


Diff::class => 1,


Runtime::class => 1,


Exporter::class => 1,


Snapshot::class => 1,


Counter::class => 1,


Enumerator::class => 1,


ObjectReflector::class => 1,


Context::class => 1,


ResourceOperations::class => 1,


TypeName::class => 1,


Version::class => 1,


Tokenizer::class => 1,
];




private static $directories = [];




private static $initialized = false;

public static function addDirectory(string $directory): void
{
if (!is_dir($directory)) {
throw new Exception(
sprintf(
'"%s" is not a directory',
$directory,
),
);
}

self::$directories[] = realpath($directory);
}






public function getExcludedDirectories(): array
{
$this->initialize();

return self::$directories;
}




public function isExcluded(string $file): bool
{
if (defined('PHPUNIT_TESTSUITE')) {
return false;
}

$this->initialize();

foreach (self::$directories as $directory) {
if (strpos($file, $directory) === 0) {
return true;
}
}

return false;
}




private function initialize(): void
{
if (self::$initialized) {
return;
}

foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) {
if (!class_exists($className)) {
continue;
}

$directory = (new ReflectionClass($className))->getFileName();

for ($i = 0; $i < $parent; $i++) {
$directory = dirname($directory);
}

self::$directories[] = $directory;
}


if (DIRECTORY_SEPARATOR === '\\') {


self::$directories[] = sys_get_temp_dir() . '\\PHP';
}

self::$initialized = true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use Throwable;




final class Cloner
{







public static function clone(object $original): object
{
try {
return clone $original;
} catch (Throwable $t) {
return $original;
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const ENT_QUOTES;
use function assert;
use function class_exists;
use function htmlspecialchars;
use function mb_convert_encoding;
use function ord;
use function preg_replace;
use function settype;
use function strlen;
use DOMCharacterData;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMText;
use ReflectionClass;
use ReflectionException;




final class Xml
{



public static function import(DOMElement $element): DOMElement
{
return (new DOMDocument)->importNode($element, true);
}




public static function removeCharacterDataNodes(DOMNode $node): void
{
if ($node->hasChildNodes()) {
for ($i = $node->childNodes->length - 1; $i >= 0; $i--) {
if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) {
$node->removeChild($child);
}
}
}
}









public static function prepareString(string $string): string
{
return preg_replace(
'/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/',
'',
htmlspecialchars(
self::convertToUtf8($string),
ENT_QUOTES,
),
);
}




public static function xmlToVariable(DOMElement $element)
{
$variable = null;

switch ($element->tagName) {
case 'array':
$variable = [];

foreach ($element->childNodes as $entry) {
if (!$entry instanceof DOMElement || $entry->tagName !== 'element') {
continue;
}
$item = $entry->childNodes->item(0);

if ($item instanceof DOMText) {
$item = $entry->childNodes->item(1);
}

$value = self::xmlToVariable($item);

if ($entry->hasAttribute('key')) {
$variable[(string) $entry->getAttribute('key')] = $value;
} else {
$variable[] = $value;
}
}

break;

case 'object':
$className = $element->getAttribute('class');

if ($element->hasChildNodes()) {
$arguments = $element->childNodes->item(0)->childNodes;
$constructorArgs = [];

foreach ($arguments as $argument) {
if ($argument instanceof DOMElement) {
$constructorArgs[] = self::xmlToVariable($argument);
}
}

try {
assert(class_exists($className));

$variable = (new ReflectionClass($className))->newInstanceArgs($constructorArgs);

} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

} else {
$variable = new $className;
}

break;

case 'boolean':
$variable = $element->textContent === 'true';

break;

case 'integer':
case 'double':
case 'string':
$variable = $element->textContent;

settype($variable, $element->tagName);

break;
}

return $variable;
}

private static function convertToUtf8(string $string): string
{
if (!self::isUtf8($string)) {
$string = mb_convert_encoding($string, 'UTF-8');
}

return $string;
}

private static function isUtf8(string $string): bool
{
$length = strlen($string);

for ($i = 0; $i < $length; $i++) {
if (ord($string[$i]) < 0x80) {
$n = 0;
} elseif ((ord($string[$i]) & 0xE0) === 0xC0) {
$n = 1;
} elseif ((ord($string[$i]) & 0xF0) === 0xE0) {
$n = 2;
} elseif ((ord($string[$i]) & 0xF0) === 0xF0) {
$n = 3;
} else {
return false;
}

for ($j = 0; $j < $n; $j++) {
if ((++$i === $length) || ((ord($string[$i]) & 0xC0) !== 0x80)) {
return false;
}
}
}

return true;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const DIRECTORY_SEPARATOR;
use function array_diff;
use function array_keys;
use function fopen;
use function get_defined_vars;
use function sprintf;
use function stream_resolve_include_path;




final class FileLoader
{










public static function checkAndLoad(string $filename): string
{
$includePathFilename = stream_resolve_include_path($filename);

$localFile = __DIR__ . DIRECTORY_SEPARATOR . $filename;

if (!$includePathFilename ||
$includePathFilename === $localFile ||
!self::isReadable($includePathFilename)) {
throw new Exception(
sprintf('Cannot open file "%s".' . "\n", $filename),
);
}

self::load($includePathFilename);

return $includePathFilename;
}




public static function load(string $filename): void
{
$oldVariableNames = array_keys(get_defined_vars());






include_once $filename;

$newVariables = get_defined_vars();

foreach (array_diff(array_keys($newVariables), $oldVariableNames) as $variableName) {
if ($variableName !== 'oldVariableNames') {
$GLOBALS[$variableName] = $newVariables[$variableName];
}
}
}




private static function isReadable(string $filename): bool
{
return @fopen($filename, 'r') !== false;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;






final class Blacklist
{
public static function addDirectory(string $directory): void
{
ExcludeList::addDirectory($directory);
}






public function getBlacklistedDirectories(): array
{
return (new ExcludeList)->getExcludedDirectories();
}




public function isBlacklisted(string $file): bool
{
return (new ExcludeList)->isExcluded($file);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;




final class Type
{
public static function isType(string $type): bool
{
switch ($type) {
case 'numeric':
case 'integer':
case 'int':
case 'iterable':
case 'float':
case 'string':
case 'boolean':
case 'bool':
case 'null':
case 'array':
case 'object':
case 'resource':
case 'scalar':
return true;

default:
return false;
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\Util;

use const PHP_OS;
use const PHP_VERSION;
use function addcslashes;
use function array_flip;
use function array_key_exists;
use function array_merge;
use function array_unique;
use function array_unshift;
use function class_exists;
use function count;
use function explode;
use function extension_loaded;
use function function_exists;
use function get_class;
use function ini_get;
use function interface_exists;
use function is_array;
use function is_int;
use function method_exists;
use function phpversion;
use function preg_match;
use function preg_replace;
use function sprintf;
use function strncmp;
use function strpos;
use function strtolower;
use function trim;
use function version_compare;
use PHPUnit\Framework\CodeCoverageException;
use PHPUnit\Framework\ExecutionOrderDependency;
use PHPUnit\Framework\InvalidCoversTargetException;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\Version;
use PHPUnit\Util\Annotation\Registry;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use SebastianBergmann\CodeUnit\CodeUnitCollection;
use SebastianBergmann\CodeUnit\InvalidCodeUnitException;
use SebastianBergmann\CodeUnit\Mapper;
use SebastianBergmann\Environment\OperatingSystem;




final class Test
{



public const UNKNOWN = -1;




public const SMALL = 0;




public const MEDIUM = 1;




public const LARGE = 2;




private static $hookMethods = [];




public static function describe(\PHPUnit\Framework\Test $test): array
{
if ($test instanceof TestCase) {
return [get_class($test), $test->getName()];
}

if ($test instanceof SelfDescribing) {
return ['', $test->toString()];
}

return ['', get_class($test)];
}

public static function describeAsString(\PHPUnit\Framework\Test $test): string
{
if ($test instanceof SelfDescribing) {
return $test->toString();
}

return get_class($test);
}








public static function getLinesToBeCovered(string $className, string $methodName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

if (!self::shouldCoversAnnotationBeUsed($annotations)) {
return false;
}

return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
}








public static function getLinesToBeUsed(string $className, string $methodName): array
{
return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
}

public static function requiresCodeCoverageDataCollection(TestCase $test): bool
{
$annotations = self::parseTestMethodAnnotations(
get_class($test),
$test->getName(false),
);



if (isset($annotations['method']['coversNothing'])) {


}



if (isset($annotations['method']['covers'])) {
return true;
}



if (isset($annotations['class']['coversNothing'])) {


}



return true;
}






public static function getRequirements(string $className, string $methodName): array
{
return self::mergeArraysRecursively(
Registry::getInstance()->forClassName($className)->requirements(),
Registry::getInstance()->forMethod($className, $methodName)->requirements(),
);
}









public static function getMissingRequirements(string $className, string $methodName): array
{
$required = self::getRequirements($className, $methodName);
$missing = [];
$hint = null;

if (!empty($required['PHP'])) {
$operator = new VersionComparisonOperator(empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']);

if (!version_compare(PHP_VERSION, $required['PHP']['version'], $operator->asString())) {
$missing[] = sprintf('PHP %s %s is required.', $operator->asString(), $required['PHP']['version']);
$hint = 'PHP';
}
} elseif (!empty($required['PHP_constraint'])) {
$version = new \PharIo\Version\Version(self::sanitizeVersionNumber(PHP_VERSION));

if (!$required['PHP_constraint']['constraint']->complies($version)) {
$missing[] = sprintf(
'PHP version does not match the required constraint %s.',
$required['PHP_constraint']['constraint']->asString(),
);

$hint = 'PHP_constraint';
}
}

if (!empty($required['PHPUnit'])) {
$phpunitVersion = Version::id();

$operator = new VersionComparisonOperator(empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']);

if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator->asString())) {
$missing[] = sprintf('PHPUnit %s %s is required.', $operator->asString(), $required['PHPUnit']['version']);
$hint = $hint ?? 'PHPUnit';
}
} elseif (!empty($required['PHPUnit_constraint'])) {
$phpunitVersion = new \PharIo\Version\Version(self::sanitizeVersionNumber(Version::id()));

if (!$required['PHPUnit_constraint']['constraint']->complies($phpunitVersion)) {
$missing[] = sprintf(
'PHPUnit version does not match the required constraint %s.',
$required['PHPUnit_constraint']['constraint']->asString(),
);

$hint = $hint ?? 'PHPUnit_constraint';
}
}

if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem)->getFamily()) {
$missing[] = sprintf('Operating system %s is required.', $required['OSFAMILY']);
$hint = $hint ?? 'OSFAMILY';
}

if (!empty($required['OS'])) {
$requiredOsPattern = sprintf('/%s/i', addcslashes($required['OS'], '/'));

if (!preg_match($requiredOsPattern, PHP_OS)) {
$missing[] = sprintf('Operating system matching %s is required.', $requiredOsPattern);
$hint = $hint ?? 'OS';
}
}

if (!empty($required['functions'])) {
foreach ($required['functions'] as $function) {
$pieces = explode('::', $function);

if (count($pieces) === 2 && class_exists($pieces[0]) && method_exists($pieces[0], $pieces[1])) {
continue;
}

if (function_exists($function)) {
continue;
}

$missing[] = sprintf('Function %s is required.', $function);
$hint = $hint ?? 'function_' . $function;
}
}

if (!empty($required['setting'])) {
foreach ($required['setting'] as $setting => $value) {
if (ini_get($setting) !== $value) {
$missing[] = sprintf('Setting "%s" must be "%s".', $setting, $value);
$hint = $hint ?? '__SETTING_' . $setting;
}
}
}

if (!empty($required['extensions'])) {
foreach ($required['extensions'] as $extension) {
if (isset($required['extension_versions'][$extension])) {
continue;
}

if (!extension_loaded($extension)) {
$missing[] = sprintf('Extension %s is required.', $extension);
$hint = $hint ?? 'extension_' . $extension;
}
}
}

if (!empty($required['extension_versions'])) {
foreach ($required['extension_versions'] as $extension => $req) {
$actualVersion = phpversion($extension);

$operator = new VersionComparisonOperator(empty($req['operator']) ? '>=' : $req['operator']);

if ($actualVersion === false || !version_compare($actualVersion, $req['version'], $operator->asString())) {
$missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator->asString(), $req['version']);
$hint = $hint ?? 'extension_' . $extension;
}
}
}

if ($hint && isset($required['__OFFSET'])) {
array_unshift($missing, '__OFFSET_FILE=' . $required['__OFFSET']['__FILE']);
array_unshift($missing, '__OFFSET_LINE=' . ($required['__OFFSET'][$hint] ?? 1));
}

return $missing;
}








public static function getProvidedData(string $className, string $methodName): ?array
{
return Registry::getInstance()->forMethod($className, $methodName)->getProvidedData();
}




public static function parseTestMethodAnnotations(string $className, ?string $methodName = null): array
{
$registry = Registry::getInstance();

if ($methodName !== null) {
try {
return [
'method' => $registry->forMethod($className, $methodName)->symbolAnnotations(),
'class' => $registry->forClassName($className)->symbolAnnotations(),
];
} catch (Exception $methodNotFound) {

}
}

return [
'method' => null,
'class' => $registry->forClassName($className)->symbolAnnotations(),
];
}




public static function getInlineAnnotations(string $className, string $methodName): array
{
return Registry::getInstance()->forMethod($className, $methodName)->getInlineAnnotations();
}


public static function getBackupSettings(string $className, string $methodName): array
{
return [
'backupGlobals' => self::getBooleanAnnotationSetting(
$className,
$methodName,
'backupGlobals',
),
'backupStaticAttributes' => self::getBooleanAnnotationSetting(
$className,
$methodName,
'backupStaticAttributes',
),
];
}






public static function getDependencies(string $className, string $methodName): array
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

$dependsAnnotations = $annotations['class']['depends'] ?? [];

if (isset($annotations['method']['depends'])) {
$dependsAnnotations = array_merge(
$dependsAnnotations,
$annotations['method']['depends'],
);
}


$dependencies = [];

foreach ($dependsAnnotations as $value) {
$dependencies[] = ExecutionOrderDependency::createFromDependsAnnotation($className, $value);
}

return array_unique($dependencies);
}


public static function getGroups(string $className, ?string $methodName = ''): array
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

$groups = [];

if (isset($annotations['method']['author'])) {
$groups[] = $annotations['method']['author'];
} elseif (isset($annotations['class']['author'])) {
$groups[] = $annotations['class']['author'];
}

if (isset($annotations['class']['group'])) {
$groups[] = $annotations['class']['group'];
}

if (isset($annotations['method']['group'])) {
$groups[] = $annotations['method']['group'];
}

if (isset($annotations['class']['ticket'])) {
$groups[] = $annotations['class']['ticket'];
}

if (isset($annotations['method']['ticket'])) {
$groups[] = $annotations['method']['ticket'];
}

foreach (['method', 'class'] as $element) {
foreach (['small', 'medium', 'large'] as $size) {
if (isset($annotations[$element][$size])) {
$groups[] = [$size];

break 2;
}
}
}

foreach (['method', 'class'] as $element) {
if (isset($annotations[$element]['covers'])) {
foreach ($annotations[$element]['covers'] as $coversTarget) {
$groups[] = ['__phpunit_covers_' . self::canonicalizeName($coversTarget)];
}
}

if (isset($annotations[$element]['uses'])) {
foreach ($annotations[$element]['uses'] as $usesTarget) {
$groups[] = ['__phpunit_uses_' . self::canonicalizeName($usesTarget)];
}
}
}

return array_unique(array_merge([], ...$groups));
}


public static function getSize(string $className, ?string $methodName): int
{
$groups = array_flip(self::getGroups($className, $methodName));

if (isset($groups['large'])) {
return self::LARGE;
}

if (isset($groups['medium'])) {
return self::MEDIUM;
}

if (isset($groups['small'])) {
return self::SMALL;
}

return self::UNKNOWN;
}


public static function getProcessIsolationSettings(string $className, string $methodName): bool
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

return isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess']);
}


public static function getClassProcessIsolationSettings(string $className, string $methodName): bool
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

return isset($annotations['class']['runClassInSeparateProcess']);
}


public static function getPreserveGlobalStateSettings(string $className, string $methodName): ?bool
{
return self::getBooleanAnnotationSetting(
$className,
$methodName,
'preserveGlobalState',
);
}


public static function getHookMethods(string $className): array
{
if (!class_exists($className, false)) {
return self::emptyHookMethodsArray();
}

if (!isset(self::$hookMethods[$className])) {
self::$hookMethods[$className] = self::emptyHookMethodsArray();

try {
foreach ((new Reflection)->methodsInTestClass(new ReflectionClass($className)) as $method) {
$docBlock = Registry::getInstance()->forMethod($className, $method->getName());

if ($method->isStatic()) {
if ($docBlock->isHookToBeExecutedBeforeClass()) {
array_unshift(
self::$hookMethods[$className]['beforeClass'],
$method->getName(),
);
}

if ($docBlock->isHookToBeExecutedAfterClass()) {
self::$hookMethods[$className]['afterClass'][] = $method->getName();
}
}

if ($docBlock->isToBeExecutedBeforeTest()) {
array_unshift(
self::$hookMethods[$className]['before'],
$method->getName(),
);
}

if ($docBlock->isToBeExecutedAsPreCondition()) {
array_unshift(
self::$hookMethods[$className]['preCondition'],
$method->getName(),
);
}

if ($docBlock->isToBeExecutedAsPostCondition()) {
self::$hookMethods[$className]['postCondition'][] = $method->getName();
}

if ($docBlock->isToBeExecutedAfterTest()) {
self::$hookMethods[$className]['after'][] = $method->getName();
}
}
} catch (ReflectionException $e) {
}
}

return self::$hookMethods[$className];
}

public static function isTestMethod(ReflectionMethod $method): bool
{
if (!$method->isPublic()) {
return false;
}

if (strpos($method->getName(), 'test') === 0) {
return true;
}

return array_key_exists(
'test',
Registry::getInstance()->forMethod(
$method->getDeclaringClass()->getName(),
$method->getName(),
)
->symbolAnnotations(),
);
}






private static function getLinesToBeCoveredOrUsed(string $className, string $methodName, string $mode): array
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

$classShortcut = null;

if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
if (count($annotations['class'][$mode . 'DefaultClass']) > 1) {
throw new CodeCoverageException(
sprintf(
'More than one @%sClass annotation in class or interface "%s".',
$mode,
$className,
),
);
}

$classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
}

$list = $annotations['class'][$mode] ?? [];

if (isset($annotations['method'][$mode])) {
$list = array_merge($list, $annotations['method'][$mode]);
}

$codeUnits = CodeUnitCollection::fromArray([]);
$mapper = new Mapper;

foreach (array_unique($list) as $element) {
if ($classShortcut && strncmp($element, '::', 2) === 0) {
$element = $classShortcut . $element;
}

$element = preg_replace('/[\s()]+$/', '', $element);
$element = explode(' ', $element);
$element = $element[0];

if ($mode === 'covers' && interface_exists($element)) {
throw new InvalidCoversTargetException(
sprintf(
'Trying to @cover interface "%s".',
$element,
),
);
}

try {
$codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($element));
} catch (InvalidCodeUnitException $e) {
throw new InvalidCoversTargetException(
sprintf(
'"@%s %s" is invalid',
$mode,
$element,
),
$e->getCode(),
$e,
);
}
}

return $mapper->codeUnitsToSourceLines($codeUnits);
}

private static function emptyHookMethodsArray(): array
{
return [
'beforeClass' => ['setUpBeforeClass'],
'before' => ['setUp'],
'preCondition' => ['assertPreConditions'],
'postCondition' => ['assertPostConditions'],
'after' => ['tearDown'],
'afterClass' => ['tearDownAfterClass'],
];
}


private static function getBooleanAnnotationSetting(string $className, ?string $methodName, string $settingName): ?bool
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName,
);

if (isset($annotations['method'][$settingName])) {
if ($annotations['method'][$settingName][0] === 'enabled') {
return true;
}

if ($annotations['method'][$settingName][0] === 'disabled') {
return false;
}
}

if (isset($annotations['class'][$settingName])) {
if ($annotations['class'][$settingName][0] === 'enabled') {
return true;
}

if ($annotations['class'][$settingName][0] === 'disabled') {
return false;
}
}

return null;
}





private static function sanitizeVersionNumber(string $version)
{
return preg_replace(
'/^(\d+\.\d+(?:.\d+)?).*$/',
'$1',
$version,
);
}

private static function shouldCoversAnnotationBeUsed(array $annotations): bool
{
if (isset($annotations['method']['coversNothing'])) {
return false;
}

if (isset($annotations['method']['covers'])) {
return true;
}

if (isset($annotations['class']['coversNothing'])) {
return false;
}

return true;
}

















private static function mergeArraysRecursively(array $a, array $b): array
{
foreach ($b as $key => $value) {
if (array_key_exists($key, $a)) {
if (is_int($key)) {
$a[] = $value;
} elseif (is_array($value) && is_array($a[$key])) {
$a[$key] = self::mergeArraysRecursively($a[$key], $value);
} else {
$a[$key] = $value;
}
} else {
$a[$key] = $value;
}
}

return $a;
}

private static function canonicalizeName(string $name): string
{
return strtolower(trim($name, '\\'));
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use const PHP_EOL;
use const PHP_SAPI;
use const PHP_VERSION;
use function array_diff;
use function array_map;
use function array_merge;
use function assert;
use function class_exists;
use function count;
use function dirname;
use function file_put_contents;
use function htmlspecialchars;
use function is_array;
use function is_int;
use function is_string;
use function mt_srand;
use function range;
use function realpath;
use function sort;
use function sprintf;
use function time;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\AfterLastTestHook;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\BeforeFirstTestHook;
use PHPUnit\Runner\DefaultTestResultCache;
use PHPUnit\Runner\Extension\ExtensionHandler;
use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator;
use PHPUnit\Runner\Filter\Factory;
use PHPUnit\Runner\Filter\IncludeGroupFilterIterator;
use PHPUnit\Runner\Filter\NameFilterIterator;
use PHPUnit\Runner\Hook;
use PHPUnit\Runner\NullTestResultCache;
use PHPUnit\Runner\ResultCacheExtension;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Runner\TestHook;
use PHPUnit\Runner\TestListenerAdapter;
use PHPUnit\Runner\TestSuiteLoader;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\Runner\Version;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper;
use PHPUnit\TextUI\XmlConfiguration\Configuration;
use PHPUnit\TextUI\XmlConfiguration\Loader;
use PHPUnit\TextUI\XmlConfiguration\PhpHandler;
use PHPUnit\Util\Filesystem;
use PHPUnit\Util\Log\JUnit;
use PHPUnit\Util\Log\TeamCity;
use PHPUnit\Util\Printer;
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
use PHPUnit\Util\TestDox\HtmlResultPrinter;
use PHPUnit\Util\TestDox\TextResultPrinter;
use PHPUnit\Util\TestDox\XmlResultPrinter;
use PHPUnit\Util\XdebugFilterScriptGenerator;
use PHPUnit\Util\Xml\SchemaDetector;
use ReflectionClass;
use ReflectionException;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
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 HtmlReport;
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Environment\Runtime;
use SebastianBergmann\Invoker\Invoker;
use SebastianBergmann\Timer\Timer;




final class TestRunner extends BaseTestRunner
{
public const SUCCESS_EXIT = 0;
public const FAILURE_EXIT = 1;
public const EXCEPTION_EXIT = 2;




private $codeCoverageFilter;




private $loader;




private $printer;




private $messagePrinted = false;




private $extensions = [];




private $timer;

public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null)
{
if ($filter === null) {
$filter = new CodeCoverageFilter;
}

$this->codeCoverageFilter = $filter;
$this->loader = $loader;
$this->timer = new Timer;
}






public function run(TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = true): TestResult
{
if (isset($arguments['configuration'])) {
$GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration'];
}

$this->handleConfiguration($arguments);

$warnings = array_merge($warnings, $arguments['warnings']);

if (is_int($arguments['columns']) && $arguments['columns'] < 16) {
$arguments['columns'] = 16;
$tooFewColumnsRequested = true;
}

if (isset($arguments['bootstrap'])) {
$GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap'];
}

if ($arguments['backupGlobals'] === true) {
$suite->setBackupGlobals(true);
}

if ($arguments['backupStaticAttributes'] === true) {
$suite->setBackupStaticAttributes(true);
}

if ($arguments['beStrictAboutChangesToGlobalState'] === true) {
$suite->setBeStrictAboutChangesToGlobalState(true);
}

if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) {
mt_srand($arguments['randomOrderSeed']);
}

if ($arguments['cacheResult']) {
if (!isset($arguments['cacheResultFile'])) {
if (isset($arguments['configurationObject'])) {
assert($arguments['configurationObject'] instanceof Configuration);

$cacheLocation = $arguments['configurationObject']->filename();
} else {
$cacheLocation = $_SERVER['PHP_SELF'];
}

$arguments['cacheResultFile'] = null;

$cacheResultFile = realpath($cacheLocation);

if ($cacheResultFile !== false) {
$arguments['cacheResultFile'] = dirname($cacheResultFile);
}
}

$cache = new DefaultTestResultCache($arguments['cacheResultFile']);

$this->addExtension(new ResultCacheExtension($cache));
}

if ($arguments['executionOrder'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) {
$cache = $cache ?? new NullTestResultCache;

$cache->load();

$sorter = new TestSuiteSorter($cache);

$sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']);
$originalExecutionOrder = $sorter->getOriginalExecutionOrder();

unset($sorter);
}

if (is_int($arguments['repeat']) && $arguments['repeat'] > 0) {
$_suite = new TestSuite;


foreach (range(1, $arguments['repeat']) as $step) {
$_suite->addTest($suite);
}

$suite = $_suite;

unset($_suite);
}

$result = $this->createTestResult();

$listener = new TestListenerAdapter;
$listenerNeeded = false;

foreach ($this->extensions as $extension) {
if ($extension instanceof TestHook) {
$listener->add($extension);

$listenerNeeded = true;
}
}

if ($listenerNeeded) {
$result->addListener($listener);
}

unset($listener, $listenerNeeded);

if ($arguments['convertDeprecationsToExceptions']) {
$result->convertDeprecationsToExceptions(true);
}

if (!$arguments['convertErrorsToExceptions']) {
$result->convertErrorsToExceptions(false);
}

if (!$arguments['convertNoticesToExceptions']) {
$result->convertNoticesToExceptions(false);
}

if (!$arguments['convertWarningsToExceptions']) {
$result->convertWarningsToExceptions(false);
}

if ($arguments['stopOnError']) {
$result->stopOnError(true);
}

if ($arguments['stopOnFailure']) {
$result->stopOnFailure(true);
}

if ($arguments['stopOnWarning']) {
$result->stopOnWarning(true);
}

if ($arguments['stopOnIncomplete']) {
$result->stopOnIncomplete(true);
}

if ($arguments['stopOnRisky']) {
$result->stopOnRisky(true);
}

if ($arguments['stopOnSkipped']) {
$result->stopOnSkipped(true);
}

if ($arguments['stopOnDefect']) {
$result->stopOnDefect(true);
}

if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) {
$result->setRegisterMockObjectsFromTestArgumentsRecursively(true);
}

if ($this->printer === null) {
if (isset($arguments['printer'])) {
if ($arguments['printer'] instanceof ResultPrinter) {
$this->printer = $arguments['printer'];
} elseif (is_string($arguments['printer']) && class_exists($arguments['printer'], false)) {
try {
$reflector = new ReflectionClass($arguments['printer']);

if ($reflector->implementsInterface(ResultPrinter::class)) {
$this->printer = $this->createPrinter($arguments['printer'], $arguments);
}


} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

}
} else {
$this->printer = $this->createPrinter(DefaultResultPrinter::class, $arguments);
}
}

if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) {
assert($this->printer instanceof CliTestDoxPrinter);

$this->printer->setOriginalExecutionOrder($originalExecutionOrder);
$this->printer->setShowProgressAnimation(!$arguments['noInteraction']);
}

$this->write(Version::getVersionString() . "\n");

foreach ($arguments['listeners'] as $listener) {
$result->addListener($listener);
}

$result->addListener($this->printer);

$coverageFilterFromConfigurationFile = false;
$coverageFilterFromOption = false;
$codeCoverageReports = 0;

if (isset($arguments['testdoxHTMLFile'])) {
$result->addListener(
new HtmlResultPrinter(
$arguments['testdoxHTMLFile'],
$arguments['testdoxGroups'],
$arguments['testdoxExcludeGroups'],
),
);
}

if (isset($arguments['testdoxTextFile'])) {
$result->addListener(
new TextResultPrinter(
$arguments['testdoxTextFile'],
$arguments['testdoxGroups'],
$arguments['testdoxExcludeGroups'],
),
);
}

if (isset($arguments['testdoxXMLFile'])) {
$result->addListener(
new XmlResultPrinter(
$arguments['testdoxXMLFile'],
),
);
}

if (isset($arguments['teamcityLogfile'])) {
$result->addListener(
new TeamCity($arguments['teamcityLogfile']),
);
}

if (isset($arguments['junitLogfile'])) {
$result->addListener(
new JUnit(
$arguments['junitLogfile'],
$arguments['reportUselessTests'],
),
);
}

if (isset($arguments['coverageClover'])) {
$codeCoverageReports++;
}

if (isset($arguments['coverageCobertura'])) {
$codeCoverageReports++;
}

if (isset($arguments['coverageCrap4J'])) {
$codeCoverageReports++;
}

if (isset($arguments['coverageHtml'])) {
$codeCoverageReports++;
}

if (isset($arguments['coveragePHP'])) {
$codeCoverageReports++;
}

if (isset($arguments['coverageText'])) {
$codeCoverageReports++;
}

if (isset($arguments['coverageXml'])) {
$codeCoverageReports++;
}

if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) {
if (isset($arguments['coverageFilter'])) {
if (!is_array($arguments['coverageFilter'])) {
$coverageFilterDirectories = [$arguments['coverageFilter']];
} else {
$coverageFilterDirectories = $arguments['coverageFilter'];
}

foreach ($coverageFilterDirectories as $coverageFilterDirectory) {
$this->codeCoverageFilter->includeDirectory($coverageFilterDirectory);
}

$coverageFilterFromOption = true;
}

if (isset($arguments['configurationObject'])) {
assert($arguments['configurationObject'] instanceof Configuration);

$codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage();

if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) {
$coverageFilterFromConfigurationFile = true;

(new FilterMapper)->map(
$this->codeCoverageFilter,
$codeCoverageConfiguration,
);
}
}
}

if ($codeCoverageReports > 0) {
try {
if (isset($codeCoverageConfiguration) &&
($codeCoverageConfiguration->pathCoverage() || (isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === true))) {
$codeCoverageDriver = (new Selector)->forLineAndPathCoverage($this->codeCoverageFilter);
} else {
$codeCoverageDriver = (new Selector)->forLineCoverage($this->codeCoverageFilter);
}

$codeCoverage = new CodeCoverage(
$codeCoverageDriver,
$this->codeCoverageFilter,
);

if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) {
$codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path());
}

if (isset($arguments['coverageCacheDirectory'])) {
$codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']);
}

$codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class);

if ($arguments['strictCoverage']) {
$codeCoverage->enableCheckForUnintentionallyCoveredCode();
}

if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) {
if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) {
$codeCoverage->ignoreDeprecatedCode();
} else {
$codeCoverage->doNotIgnoreDeprecatedCode();
}
}

if (isset($arguments['disableCodeCoverageIgnore'])) {
if ($arguments['disableCodeCoverageIgnore']) {
$codeCoverage->disableAnnotationsForIgnoringCode();
} else {
$codeCoverage->enableAnnotationsForIgnoringCode();
}
}

if (isset($arguments['configurationObject'])) {
$codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage();

if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) {
if ($codeCoverageConfiguration->includeUncoveredFiles()) {
$codeCoverage->includeUncoveredFiles();
} else {
$codeCoverage->excludeUncoveredFiles();
}

if ($codeCoverageConfiguration->processUncoveredFiles()) {
$codeCoverage->processUncoveredFiles();
} else {
$codeCoverage->doNotProcessUncoveredFiles();
}
}
}

if ($this->codeCoverageFilter->isEmpty()) {
if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) {
$warnings[] = 'No filter is configured, code coverage will not be processed';
} else {
$warnings[] = 'Incorrect filter configuration, code coverage will not be processed';
}

unset($codeCoverage);
}
} catch (CodeCoverageException $e) {
$warnings[] = $e->getMessage();
}
}

if ($arguments['verbose']) {
if (PHP_SAPI === 'phpdbg') {
$this->writeMessage('Runtime', 'PHPDBG ' . PHP_VERSION);
} else {
$runtime = 'PHP ' . PHP_VERSION;

if (isset($codeCoverageDriver)) {
$runtime .= ' with ' . $codeCoverageDriver->nameAndVersion();
}

$this->writeMessage('Runtime', $runtime);
}

if (isset($arguments['configurationObject'])) {
assert($arguments['configurationObject'] instanceof Configuration);

$this->writeMessage(
'Configuration',
$arguments['configurationObject']->filename(),
);
}

foreach ($arguments['loadedExtensions'] as $extension) {
$this->writeMessage(
'Extension',
$extension,
);
}

foreach ($arguments['notLoadedExtensions'] as $extension) {
$this->writeMessage(
'Extension',
$extension,
);
}
}

if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) {
$this->writeMessage(
'Random Seed',
(string) $arguments['randomOrderSeed'],
);
}

if (isset($tooFewColumnsRequested)) {
$warnings[] = 'Less than 16 columns requested, number of columns set to 16';
}

if ((new Runtime)->discardsComments()) {
$warnings[] = 'opcache.save_comments=0 set; annotations will not work';
}

if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) {
$warnings[] = 'Directives printerClass and testdox are mutually exclusive';
}

$warnings = array_merge($warnings, $suite->warnings());
sort($warnings);

foreach ($warnings as $warning) {
$this->writeMessage('Warning', $warning);
}

if (isset($arguments['configurationObject'])) {
assert($arguments['configurationObject'] instanceof Configuration);

if ($arguments['configurationObject']->hasValidationErrors()) {
if ((new SchemaDetector)->detect($arguments['configurationObject']->filename())->detected()) {
$this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.');
$this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!');
} else {
$this->write(
"\n  Warning - The configuration file did not pass validation!\n  The following problems have been detected:\n",
);

$this->write($arguments['configurationObject']->validationErrors());

$this->write("\n  Test results may not be as expected.\n\n");
}
}
}

if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) {
$this->write(PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . PHP_EOL);

$script = (new XdebugFilterScriptGenerator)->generate($codeCoverageConfiguration);

if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !Filesystem::createDirectory(dirname($arguments['xdebugFilterFile']))) {
$this->write(sprintf('Cannot write Xdebug filter script to %s ' . PHP_EOL, $arguments['xdebugFilterFile']));

exit(self::EXCEPTION_EXIT);
}

file_put_contents($arguments['xdebugFilterFile'], $script);

$this->write(sprintf('Wrote Xdebug filter script to %s ' . PHP_EOL . PHP_EOL, $arguments['xdebugFilterFile']));

exit(self::SUCCESS_EXIT);
}

$this->write("\n");

if (isset($codeCoverage)) {
$result->setCodeCoverage($codeCoverage);
}

$result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']);
$result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']);
$result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']);
$result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']);

if ($arguments['enforceTimeLimit'] === true && !(new Invoker)->canInvokeWithTimeout()) {
$this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits');
}

$result->enforceTimeLimit($arguments['enforceTimeLimit']);
$result->setDefaultTimeLimit($arguments['defaultTimeLimit']);
$result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']);
$result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']);
$result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']);

if (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === true) {
$result->forceCoversAnnotation();
}

$this->processSuiteFilters($suite, $arguments);
$suite->setRunTestInSeparateProcess($arguments['processIsolation']);

foreach ($this->extensions as $extension) {
if ($extension instanceof BeforeFirstTestHook) {
$extension->executeBeforeFirstTest();
}
}

$suite->run($result);

foreach ($this->extensions as $extension) {
if ($extension instanceof AfterLastTestHook) {
$extension->executeAfterLastTest();
}
}

$result->flushListeners();
$this->printer->printResult($result);

if (isset($codeCoverage)) {
if (isset($arguments['coveragePHP'])) {
$this->codeCoverageGenerationStart('PHP');

try {
$writer = new PhpReport;
$writer->process($codeCoverage, $arguments['coveragePHP']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}

if (isset($arguments['coverageClover'])) {
$this->codeCoverageGenerationStart('Clover XML');

try {
$writer = new CloverReport;
$writer->process($codeCoverage, $arguments['coverageClover']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}

if (isset($arguments['coverageCobertura'])) {
$this->codeCoverageGenerationStart('Cobertura XML');

try {
$writer = new CoberturaReport;
$writer->process($codeCoverage, $arguments['coverageCobertura']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}

if (isset($arguments['coverageCrap4J'])) {
$this->codeCoverageGenerationStart('Crap4J XML');

try {
$writer = new Crap4jReport($arguments['crap4jThreshold']);
$writer->process($codeCoverage, $arguments['coverageCrap4J']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}

if (isset($arguments['coverageHtml'])) {
$this->codeCoverageGenerationStart('HTML');

try {
$writer = new HtmlReport(
$arguments['reportLowUpperBound'],
$arguments['reportHighLowerBound'],
sprintf(
' and <a href="https://phpunit.de/">PHPUnit %s</a>',
Version::id(),
),
);

$writer->process($codeCoverage, $arguments['coverageHtml']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}

if (isset($arguments['coverageText'])) {
if ($arguments['coverageText'] === 'php://stdout') {
$outputStream = $this->printer;
$colors = $arguments['colors'] && $arguments['colors'] !== DefaultResultPrinter::COLOR_NEVER;
} else {
$outputStream = new Printer($arguments['coverageText']);
$colors = false;
}

$processor = new TextReport(
$arguments['reportLowUpperBound'],
$arguments['reportHighLowerBound'],
$arguments['coverageTextShowUncoveredFiles'],
$arguments['coverageTextShowOnlySummary'],
);

$outputStream->write(
$processor->process($codeCoverage, $colors),
);
}

if (isset($arguments['coverageXml'])) {
$this->codeCoverageGenerationStart('PHPUnit XML');

try {
$writer = new XmlReport(Version::id());
$writer->process($codeCoverage, $arguments['coverageXml']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}
}

if ($exit) {
if (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === true && count($result) === 0) {
exit(self::FAILURE_EXIT);
}

if ($result->wasSuccessfulIgnoringWarnings()) {
if ($arguments['failOnRisky'] && !$result->allHarmless()) {
exit(self::FAILURE_EXIT);
}

if ($arguments['failOnWarning'] && $result->warningCount() > 0) {
exit(self::FAILURE_EXIT);
}

if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) {
exit(self::FAILURE_EXIT);
}

if ($arguments['failOnSkipped'] && $result->skippedCount() > 0) {
exit(self::FAILURE_EXIT);
}

exit(self::SUCCESS_EXIT);
}

if ($result->errorCount() > 0) {
exit(self::EXCEPTION_EXIT);
}

if ($result->failureCount() > 0) {
exit(self::FAILURE_EXIT);
}
}

return $result;
}




public function getLoader(): TestSuiteLoader
{
if ($this->loader === null) {
$this->loader = new StandardTestSuiteLoader;
}

return $this->loader;
}

public function addExtension(Hook $extension): void
{
$this->extensions[] = $extension;
}





protected function runFailed(string $message): void
{
$this->write($message . PHP_EOL);

exit(self::FAILURE_EXIT);
}

private function createTestResult(): TestResult
{
return new TestResult;
}

private function write(string $buffer): void
{
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
$buffer = htmlspecialchars($buffer);
}

if ($this->printer !== null) {
$this->printer->write($buffer);
} else {
print $buffer;
}
}





private function handleConfiguration(array &$arguments): void
{
if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) {
$arguments['configurationObject'] = (new Loader)->load($arguments['configuration']);
}

if (!isset($arguments['warnings'])) {
$arguments['warnings'] = [];
}

$arguments['debug'] = $arguments['debug'] ?? false;
$arguments['filter'] = $arguments['filter'] ?? false;
$arguments['listeners'] = $arguments['listeners'] ?? [];

if (isset($arguments['configurationObject'])) {
(new PhpHandler)->handle($arguments['configurationObject']->php());

$codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage();

if (!isset($arguments['noCoverage'])) {
if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) {
$arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path();
}

if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) {
$arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path();
}

if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) {
$arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path();

if (!isset($arguments['crap4jThreshold'])) {
$arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold();
}
}

if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) {
$arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path();

if (!isset($arguments['reportLowUpperBound'])) {
$arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound();
}

if (!isset($arguments['reportHighLowerBound'])) {
$arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound();
}
}

if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) {
$arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path();
}

if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) {
$arguments['coverageText'] = $codeCoverageConfiguration->text()->target()->path();
$arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles();
$arguments['coverageTextShowOnlySummary'] = $codeCoverageConfiguration->text()->showOnlySummary();
}

if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) {
$arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path();
}
}

$phpunitConfiguration = $arguments['configurationObject']->phpunit();

$arguments['backupGlobals'] = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals();
$arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes();
$arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState();
$arguments['cacheResult'] = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult();
$arguments['colors'] = $arguments['colors'] ?? $phpunitConfiguration->colors();
$arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions();
$arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions();
$arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions();
$arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions();
$arguments['processIsolation'] = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation();
$arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect();
$arguments['stopOnError'] = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError();
$arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure();
$arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning();
$arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete();
$arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky();
$arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped();
$arguments['failOnEmptyTestSuite'] = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite();
$arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete();
$arguments['failOnRisky'] = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky();
$arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped();
$arguments['failOnWarning'] = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning();
$arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit();
$arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit();
$arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests();
$arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests();
$arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests();
$arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything();
$arguments['strictCoverage'] = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation();
$arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits();
$arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests();
$arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests();
$arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests();
$arguments['verbose'] = $arguments['verbose'] ?? $phpunitConfiguration->verbose();
$arguments['reverseDefectList'] = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList();
$arguments['forceCoversAnnotation'] = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation();
$arguments['disableCodeCoverageIgnore'] = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore();
$arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively();
$arguments['noInteraction'] = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction();
$arguments['executionOrder'] = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder();
$arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies();

if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) {
$arguments['bootstrap'] = $phpunitConfiguration->bootstrap();
}

if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) {
$arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile();
}

if (!isset($arguments['executionOrderDefects'])) {
$arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? TestSuiteSorter::ORDER_DEFECTS_FIRST : TestSuiteSorter::ORDER_DEFAULT;
}

if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) {
$arguments['conflictBetweenPrinterClassAndTestdox'] = true;
}

$groupCliArgs = [];

if (!empty($arguments['groups'])) {
$groupCliArgs = $arguments['groups'];
}

$groupConfiguration = $arguments['configurationObject']->groups();

if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) {
$arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings();
}

if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) {
$arguments['excludeGroups'] = array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs);
}

if (!isset($arguments['noExtensions'])) {
$extensionHandler = new ExtensionHandler;

foreach ($arguments['configurationObject']->extensions() as $extension) {
$extensionHandler->registerExtension($extension, $this);
}

foreach ($arguments['configurationObject']->listeners() as $listener) {
$arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener);
}

unset($extensionHandler);
}

foreach ($arguments['unavailableExtensions'] as $extension) {
$arguments['warnings'][] = sprintf(
'Extension "%s" is not available',
$extension,
);
}

$loggingConfiguration = $arguments['configurationObject']->logging();

if (!isset($arguments['noLogging'])) {
if ($loggingConfiguration->hasText()) {
$arguments['listeners'][] = new DefaultResultPrinter(
$loggingConfiguration->text()->target()->path(),
true,
);
}

if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) {
$arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path();
}

if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) {
$arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path();
}

if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) {
$arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path();
}

if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) {
$arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path();
}

if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) {
$arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path();
}
}

$testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups();

if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) {
$arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings();
}

if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) {
$arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings();
}
}

$extensionHandler = new ExtensionHandler;

foreach ($arguments['extensions'] as $extension) {
$extensionHandler->registerExtension($extension, $this);
}

unset($extensionHandler);

$arguments['backupGlobals'] = $arguments['backupGlobals'] ?? null;
$arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? null;
$arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? null;
$arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? false;
$arguments['cacheResult'] = $arguments['cacheResult'] ?? true;
$arguments['colors'] = $arguments['colors'] ?? DefaultResultPrinter::COLOR_DEFAULT;
$arguments['columns'] = $arguments['columns'] ?? 80;
$arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? false;
$arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? true;
$arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? true;
$arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? true;
$arguments['crap4jThreshold'] = $arguments['crap4jThreshold'] ?? 30;
$arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? false;
$arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? false;
$arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? 0;
$arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? false;
$arguments['excludeGroups'] = $arguments['excludeGroups'] ?? [];
$arguments['executionOrder'] = $arguments['executionOrder'] ?? TestSuiteSorter::ORDER_DEFAULT;
$arguments['executionOrderDefects'] = $arguments['executionOrderDefects'] ?? TestSuiteSorter::ORDER_DEFAULT;
$arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? false;
$arguments['failOnRisky'] = $arguments['failOnRisky'] ?? false;
$arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? false;
$arguments['failOnWarning'] = $arguments['failOnWarning'] ?? false;
$arguments['groups'] = $arguments['groups'] ?? [];
$arguments['noInteraction'] = $arguments['noInteraction'] ?? false;
$arguments['processIsolation'] = $arguments['processIsolation'] ?? false;
$arguments['randomOrderSeed'] = $arguments['randomOrderSeed'] ?? time();
$arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? false;
$arguments['repeat'] = $arguments['repeat'] ?? false;
$arguments['reportHighLowerBound'] = $arguments['reportHighLowerBound'] ?? 90;
$arguments['reportLowUpperBound'] = $arguments['reportLowUpperBound'] ?? 50;
$arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? true;
$arguments['reverseList'] = $arguments['reverseList'] ?? false;
$arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? true;
$arguments['stopOnError'] = $arguments['stopOnError'] ?? false;
$arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? false;
$arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? false;
$arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? false;
$arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? false;
$arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? false;
$arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? false;
$arguments['strictCoverage'] = $arguments['strictCoverage'] ?? false;
$arguments['testdoxExcludeGroups'] = $arguments['testdoxExcludeGroups'] ?? [];
$arguments['testdoxGroups'] = $arguments['testdoxGroups'] ?? [];
$arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? 60;
$arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? 10;
$arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? 1;
$arguments['verbose'] = $arguments['verbose'] ?? false;

if ($arguments['reportLowUpperBound'] > $arguments['reportHighLowerBound']) {
$arguments['reportLowUpperBound'] = 50;
$arguments['reportHighLowerBound'] = 90;
}
}

private function processSuiteFilters(TestSuite $suite, array $arguments): void
{
if (!$arguments['filter'] &&
empty($arguments['groups']) &&
empty($arguments['excludeGroups']) &&
empty($arguments['testsCovering']) &&
empty($arguments['testsUsing'])) {
return;
}

$filterFactory = new Factory;

if (!empty($arguments['excludeGroups'])) {
$filterFactory->addFilter(
new ReflectionClass(ExcludeGroupFilterIterator::class),
$arguments['excludeGroups'],
);
}

if (!empty($arguments['groups'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
$arguments['groups'],
);
}

if (!empty($arguments['testsCovering'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
array_map(
static function (string $name): string
{
return '__phpunit_covers_' . $name;
},
$arguments['testsCovering'],
),
);
}

if (!empty($arguments['testsUsing'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
array_map(
static function (string $name): string
{
return '__phpunit_uses_' . $name;
},
$arguments['testsUsing'],
),
);
}

if ($arguments['filter']) {
$filterFactory->addFilter(
new ReflectionClass(NameFilterIterator::class),
$arguments['filter'],
);
}

$suite->injectFilter($filterFactory);
}

private function writeMessage(string $type, string $message): void
{
if (!$this->messagePrinted) {
$this->write("\n");
}

$this->write(
sprintf(
"%-15s%s\n",
$type . ':',
$message,
),
);

$this->messagePrinted = true;
}

private function createPrinter(string $class, array $arguments): ResultPrinter
{
$object = new $class(
(isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null,
$arguments['verbose'],
$arguments['colors'],
$arguments['debug'],
$arguments['columns'],
$arguments['reverseList'],
);

assert($object instanceof ResultPrinter);

return $object;
}

private function codeCoverageGenerationStart(string $format): void
{
$this->write(
sprintf(
"\nGenerating code coverage report in %s format ... ",
$format,
),
);

$this->timer->start();
}

private function codeCoverageGenerationSucceeded(): void
{
$this->write(
sprintf(
"done [%s]\n",
$this->timer->stop()->asString(),
),
);
}

private function codeCoverageGenerationFailed(\Exception $e): void
{
$this->write(
sprintf(
"failed [%s]\n%s\n",
$this->timer->stop()->asString(),
$e->getMessage(),
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use const PHP_EOL;
use function count;
use function defined;
use function explode;
use function max;
use function preg_replace_callback;
use function str_pad;
use function str_repeat;
use function strlen;
use function wordwrap;
use PHPUnit\Util\Color;
use SebastianBergmann\Environment\Console;




final class Help
{
private const LEFT_MARGIN = '  ';




private $maxArgLength = 0;




private $maxDescLength;




private $hasColor = false;

public function __construct(?int $width = null, ?bool $withColor = null)
{
if ($width === null) {
$width = (new Console)->getNumberOfColumns();
}

if ($withColor === null) {
$this->hasColor = (new Console)->hasColorSupport();
} else {
$this->hasColor = $withColor;
}

foreach ($this->elements() as $options) {
foreach ($options as $option) {
if (isset($option['arg'])) {
$this->maxArgLength = max($this->maxArgLength, isset($option['arg']) ? strlen($option['arg']) : 0);
}
}
}

$this->maxDescLength = $width - $this->maxArgLength - 4;
}




public function writeToConsole(): void
{
if ($this->hasColor) {
$this->writeWithColor();
} else {
$this->writePlaintext();
}
}

private function writePlaintext(): void
{
foreach ($this->elements() as $section => $options) {
print "{$section}:" . PHP_EOL;

if ($section !== 'Usage') {
print PHP_EOL;
}

foreach ($options as $option) {
if (isset($option['spacer'])) {
print PHP_EOL;
}

if (isset($option['text'])) {
print self::LEFT_MARGIN . $option['text'] . PHP_EOL;
}

if (isset($option['arg'])) {
$arg = str_pad($option['arg'], $this->maxArgLength);
print self::LEFT_MARGIN . $arg . ' ' . $option['desc'] . PHP_EOL;
}
}

print PHP_EOL;
}
}

private function writeWithColor(): void
{
foreach ($this->elements() as $section => $options) {
print Color::colorize('fg-yellow', "{$section}:") . PHP_EOL;

foreach ($options as $option) {
if (isset($option['spacer'])) {
print PHP_EOL;
}

if (isset($option['text'])) {
print self::LEFT_MARGIN . $option['text'] . PHP_EOL;
}

if (isset($option['arg'])) {
$arg = Color::colorize('fg-green', str_pad($option['arg'], $this->maxArgLength));
$arg = preg_replace_callback(
'/(<[^>]+>)/',
static function ($matches)
{
return Color::colorize('fg-cyan', $matches[0]);
},
$arg,
);
$desc = explode(PHP_EOL, wordwrap($option['desc'], $this->maxDescLength, PHP_EOL));

print self::LEFT_MARGIN . $arg . ' ' . $desc[0] . PHP_EOL;

for ($i = 1; $i < count($desc); $i++) {
print str_repeat(' ', $this->maxArgLength + 3) . $desc[$i] . PHP_EOL;
}
}
}

print PHP_EOL;
}
}




private function elements(): array
{
$elements = [
'Usage' => [
['text' => 'phpunit [options] UnitTest.php'],
['text' => 'phpunit [options] <directory>'],
],

'Code Coverage Options' => [
['arg' => '--coverage-clover <file>', 'desc' => 'Generate code coverage report in Clover XML format'],
['arg' => '--coverage-cobertura <file>', 'desc' => 'Generate code coverage report in Cobertura XML format'],
['arg' => '--coverage-crap4j <file>', 'desc' => 'Generate code coverage report in Crap4J XML format'],
['arg' => '--coverage-html <dir>', 'desc' => 'Generate code coverage report in HTML format'],
['arg' => '--coverage-php <file>', 'desc' => 'Export PHP_CodeCoverage object to file'],
['arg' => '--coverage-text=<file>', 'desc' => 'Generate code coverage report in text format [default: standard output]'],
['arg' => '--coverage-xml <dir>', 'desc' => 'Generate code coverage report in PHPUnit XML format'],
['arg' => '--coverage-cache <dir>', 'desc' => 'Cache static analysis results'],
['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'],
['arg' => '--coverage-filter <dir>', 'desc' => 'Include <dir> in code coverage analysis'],
['arg' => '--path-coverage', 'desc' => 'Perform path coverage analysis'],
['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'],
['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration'],
],

'Logging Options' => [
['arg' => '--log-junit <file>', 'desc' => 'Log test execution in JUnit XML format to file'],
['arg' => '--log-teamcity <file>', 'desc' => 'Log test execution in TeamCity format to file'],
['arg' => '--testdox-html <file>', 'desc' => 'Write agile documentation in HTML format to file'],
['arg' => '--testdox-text <file>', 'desc' => 'Write agile documentation in Text format to file'],
['arg' => '--testdox-xml <file>', 'desc' => 'Write agile documentation in XML format to file'],
['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'],
['arg' => '--no-logging', 'desc' => 'Ignore logging configuration'],
],

'Test Selection Options' => [
['arg' => '--list-suites', 'desc' => 'List available test suites'],
['arg' => '--testsuite <name>', 'desc' => 'Filter which testsuite to run'],
['arg' => '--list-groups', 'desc' => 'List available test groups'],
['arg' => '--group <name>', 'desc' => 'Only runs tests from the specified group(s)'],
['arg' => '--exclude-group <name>', 'desc' => 'Exclude tests from the specified group(s)'],
['arg' => '--covers <name>', 'desc' => 'Only runs tests annotated with "@covers <name>"'],
['arg' => '--uses <name>', 'desc' => 'Only runs tests annotated with "@uses <name>"'],
['arg' => '--list-tests', 'desc' => 'List available tests'],
['arg' => '--list-tests-xml <file>', 'desc' => 'List available tests in XML format'],
['arg' => '--filter <pattern>', 'desc' => 'Filter which tests to run'],
['arg' => '--test-suffix <suffixes>', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt'],
],

'Test Execution Options' => [
['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'],
['arg' => '--strict-coverage', 'desc' => 'Be strict about @covers annotation usage'],
['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'],
['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'],
['arg' => '--disallow-resource-usage', 'desc' => 'Be strict about resource usage during small tests'],
['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'],
['arg' => '--default-time-limit <sec>', 'desc' => 'Timeout in seconds for tests without @small, @medium or @large'],
['arg' => '--disallow-todo-tests', 'desc' => 'Disallow @todo-annotated tests'],
['spacer' => ''],

['arg' => '--process-isolation', 'desc' => 'Run each test in a separate PHP process'],
['arg' => '--globals-backup', 'desc' => 'Backup and restore $GLOBALS for each test'],
['arg' => '--static-backup', 'desc' => 'Backup and restore static attributes for each test'],
['spacer' => ''],

['arg' => '--colors <flag>', 'desc' => 'Use colors in output ("never", "auto" or "always")'],
['arg' => '--columns <n>', 'desc' => 'Number of columns to use for progress output'],
['arg' => '--columns max', 'desc' => 'Use maximum number of columns for progress output'],
['arg' => '--stderr', 'desc' => 'Write to STDERR instead of STDOUT'],
['arg' => '--stop-on-defect', 'desc' => 'Stop execution upon first not-passed test'],
['arg' => '--stop-on-error', 'desc' => 'Stop execution upon first error'],
['arg' => '--stop-on-failure', 'desc' => 'Stop execution upon first error or failure'],
['arg' => '--stop-on-warning', 'desc' => 'Stop execution upon first warning'],
['arg' => '--stop-on-risky', 'desc' => 'Stop execution upon first risky test'],
['arg' => '--stop-on-skipped', 'desc' => 'Stop execution upon first skipped test'],
['arg' => '--stop-on-incomplete', 'desc' => 'Stop execution upon first incomplete test'],
['arg' => '--fail-on-incomplete', 'desc' => 'Treat incomplete tests as failures'],
['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'],
['arg' => '--fail-on-skipped', 'desc' => 'Treat skipped tests as failures'],
['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'],
['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'],
['arg' => '--debug', 'desc' => 'Display debugging information'],
['spacer' => ''],

['arg' => '--repeat <times>', 'desc' => 'Runs the test(s) repeatedly'],
['arg' => '--teamcity', 'desc' => 'Report test execution progress in TeamCity format'],
['arg' => '--testdox', 'desc' => 'Report test execution progress in TestDox format'],
['arg' => '--testdox-group', 'desc' => 'Only include tests from the specified group(s)'],
['arg' => '--testdox-exclude-group', 'desc' => 'Exclude tests from the specified group(s)'],
['arg' => '--no-interaction', 'desc' => 'Disable TestDox progress animation'],
['arg' => '--printer <printer>', 'desc' => 'TestListener implementation to use'],
['spacer' => ''],

['arg' => '--order-by <order>', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'],
['arg' => '--random-order-seed <N>', 'desc' => 'Use a specific random seed <N> for random order'],
['arg' => '--cache-result', 'desc' => 'Write test results to cache file'],
['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file'],
],

'Configuration Options' => [
['arg' => '--prepend <file>', 'desc' => 'A PHP script that is included as early as possible'],
['arg' => '--bootstrap <file>', 'desc' => 'A PHP script that is included before the tests run'],
['arg' => '-c|--configuration <file>', 'desc' => 'Read configuration from XML file'],
['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'],
['arg' => '--extensions <extensions>', 'desc' => 'A comma separated list of PHPUnit extensions to load'],
['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'],
['arg' => '--include-path <path(s)>', 'desc' => 'Prepend PHP\'s include_path with given path(s)'],
['arg' => '-d <key[=value]>', 'desc' => 'Sets a php.ini value'],
['arg' => '--cache-result-file <file>', 'desc' => 'Specify result cache path and filename'],
['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'],
['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format'],
],
];

if (defined('__PHPUNIT_PHAR__')) {
$elements['PHAR Options'] = [
['arg' => '--manifest', 'desc' => 'Print Software Bill of Materials (SBOM) in plain-text format'],
['arg' => '--sbom', 'desc' => 'Print Software Bill of Materials (SBOM) in CycloneDX XML format'],
['arg' => '--composer-lock', 'desc' => 'Print composer.lock file used to build the PHAR'],
];
}

$elements['Miscellaneous Options'] = [
['arg' => '-h|--help', 'desc' => 'Prints this usage information'],
['arg' => '--version', 'desc' => 'Prints the version and exits'],
['arg' => '--atleast-version <min>', 'desc' => 'Checks that version is greater than min and exits'],
['arg' => '--check-version', 'desc' => 'Checks whether PHPUnit is the latest version and exits'],
];

return $elements;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Php
{



private $includePaths;




private $iniSettings;




private $constants;




private $globalVariables;




private $envVariables;




private $postVariables;




private $getVariables;




private $cookieVariables;




private $serverVariables;




private $filesVariables;




private $requestVariables;

public function __construct(DirectoryCollection $includePaths, IniSettingCollection $iniSettings, ConstantCollection $constants, VariableCollection $globalVariables, VariableCollection $envVariables, VariableCollection $postVariables, VariableCollection $getVariables, VariableCollection $cookieVariables, VariableCollection $serverVariables, VariableCollection $filesVariables, VariableCollection $requestVariables)
{
$this->includePaths = $includePaths;
$this->iniSettings = $iniSettings;
$this->constants = $constants;
$this->globalVariables = $globalVariables;
$this->envVariables = $envVariables;
$this->postVariables = $postVariables;
$this->getVariables = $getVariables;
$this->cookieVariables = $cookieVariables;
$this->serverVariables = $serverVariables;
$this->filesVariables = $filesVariables;
$this->requestVariables = $requestVariables;
}

public function includePaths(): DirectoryCollection
{
return $this->includePaths;
}

public function iniSettings(): IniSettingCollection
{
return $this->iniSettings;
}

public function constants(): ConstantCollection
{
return $this->constants;
}

public function globalVariables(): VariableCollection
{
return $this->globalVariables;
}

public function envVariables(): VariableCollection
{
return $this->envVariables;
}

public function postVariables(): VariableCollection
{
return $this->postVariables;
}

public function getVariables(): VariableCollection
{
return $this->getVariables;
}

public function cookieVariables(): VariableCollection
{
return $this->cookieVariables;
}

public function serverVariables(): VariableCollection
{
return $this->serverVariables;
}

public function filesVariables(): VariableCollection
{
return $this->filesVariables;
}

public function requestVariables(): VariableCollection
{
return $this->requestVariables;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class ConstantCollectionIterator implements Countable, Iterator
{



private $constants;




private $position;

public function __construct(ConstantCollection $constants)
{
$this->constants = $constants->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->constants);
}

public function key(): int
{
return $this->position;
}

public function current(): Constant
{
return $this->constants[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use const PATH_SEPARATOR;
use function constant;
use function define;
use function defined;
use function getenv;
use function implode;
use function ini_get;
use function ini_set;
use function putenv;




final class PhpHandler
{
public function handle(Php $configuration): void
{
$this->handleIncludePaths($configuration->includePaths());
$this->handleIniSettings($configuration->iniSettings());
$this->handleConstants($configuration->constants());
$this->handleGlobalVariables($configuration->globalVariables());
$this->handleServerVariables($configuration->serverVariables());
$this->handleEnvVariables($configuration->envVariables());
$this->handleVariables('_POST', $configuration->postVariables());
$this->handleVariables('_GET', $configuration->getVariables());
$this->handleVariables('_COOKIE', $configuration->cookieVariables());
$this->handleVariables('_FILES', $configuration->filesVariables());
$this->handleVariables('_REQUEST', $configuration->requestVariables());
}

private function handleIncludePaths(DirectoryCollection $includePaths): void
{
if (!$includePaths->isEmpty()) {
$includePathsAsStrings = [];

foreach ($includePaths as $includePath) {
$includePathsAsStrings[] = $includePath->path();
}

ini_set(
'include_path',
implode(PATH_SEPARATOR, $includePathsAsStrings) .
PATH_SEPARATOR .
ini_get('include_path'),
);
}
}

private function handleIniSettings(IniSettingCollection $iniSettings): void
{
foreach ($iniSettings as $iniSetting) {
$value = $iniSetting->value();

if (defined($value)) {
$value = (string) constant($value);
}

ini_set($iniSetting->name(), $value);
}
}

private function handleConstants(ConstantCollection $constants): void
{
foreach ($constants as $constant) {
if (!defined($constant->name())) {
define($constant->name(), $constant->value());
}
}
}

private function handleGlobalVariables(VariableCollection $variables): void
{
foreach ($variables as $variable) {
$GLOBALS[$variable->name()] = $variable->value();
}
}

private function handleServerVariables(VariableCollection $variables): void
{
foreach ($variables as $variable) {
$_SERVER[$variable->name()] = $variable->value();
}
}

private function handleVariables(string $target, VariableCollection $variables): void
{
foreach ($variables as $variable) {
$GLOBALS[$target][$variable->name()] = $variable->value();
}
}

private function handleEnvVariables(VariableCollection $variables): void
{
foreach ($variables as $variable) {
$name = $variable->name();
$value = $variable->value();
$force = $variable->force();

if ($force || getenv($name) === false) {
putenv("{$name}={$value}");
}

$value = getenv($name);

if ($force || !isset($_ENV[$name])) {
$_ENV[$name] = $value;
}
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Constant
{



private $name;




private $value;

public function __construct(string $name, $value)
{
$this->name = $name;
$this->value = $value;
}

public function name(): string
{
return $this->name;
}

public function value()
{
return $this->value;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class IniSettingCollection implements Countable, IteratorAggregate
{



private $iniSettings;




public static function fromArray(array $iniSettings): self
{
return new self(...$iniSettings);
}

private function __construct(IniSetting ...$iniSettings)
{
$this->iniSettings = $iniSettings;
}




public function asArray(): array
{
return $this->iniSettings;
}

public function count(): int
{
return count($this->iniSettings);
}

public function getIterator(): IniSettingCollectionIterator
{
return new IniSettingCollectionIterator($this);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class VariableCollectionIterator implements Countable, Iterator
{



private $variables;




private $position;

public function __construct(VariableCollection $variables)
{
$this->variables = $variables->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->variables);
}

public function key(): int
{
return $this->position;
}

public function current(): Variable
{
return $this->variables[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class IniSetting
{



private $name;




private $value;

public function __construct(string $name, string $value)
{
$this->name = $name;
$this->value = $value;
}

public function name(): string
{
return $this->name;
}

public function value(): string
{
return $this->value;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class ConstantCollection implements Countable, IteratorAggregate
{



private $constants;




public static function fromArray(array $constants): self
{
return new self(...$constants);
}

private function __construct(Constant ...$constants)
{
$this->constants = $constants;
}




public function asArray(): array
{
return $this->constants;
}

public function count(): int
{
return count($this->constants);
}

public function getIterator(): ConstantCollectionIterator
{
return new ConstantCollectionIterator($this);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class VariableCollection implements Countable, IteratorAggregate
{



private $variables;




public static function fromArray(array $variables): self
{
return new self(...$variables);
}

private function __construct(Variable ...$variables)
{
$this->variables = $variables;
}




public function asArray(): array
{
return $this->variables;
}

public function count(): int
{
return count($this->variables);
}

public function getIterator(): VariableCollectionIterator
{
return new VariableCollectionIterator($this);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class IniSettingCollectionIterator implements Countable, Iterator
{



private $iniSettings;




private $position;

public function __construct(IniSettingCollection $iniSettings)
{
$this->iniSettings = $iniSettings->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->iniSettings);
}

public function key(): int
{
return $this->position;
}

public function current(): IniSetting
{
return $this->iniSettings[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Variable
{



private $name;




private $value;




private $force;

public function __construct(string $name, $value, bool $force)
{
$this->name = $name;
$this->value = $value;
$this->force = $force;
}

public function name(): string
{
return $this->name;
}

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

public function force(): bool
{
return $this->force;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function sprintf;
use PHPUnit\Util\Xml\Exception as XmlException;
use PHPUnit\Util\Xml\Loader as XmlLoader;
use PHPUnit\Util\Xml\SchemaDetector;




final class Migrator
{






public function migrate(string $filename): string
{
$origin = (new SchemaDetector)->detect($filename);

if (!$origin->detected()) {
throw new Exception(
sprintf(
'"%s" is not a valid PHPUnit XML configuration file that can be migrated',
$filename,
),
);
}

$configurationDocument = (new XmlLoader)->loadFile(
$filename,
false,
true,
true,
);

foreach ((new MigrationBuilder)->build($origin->version()) as $migration) {
$migration->migrate($configurationDocument);
}

$configurationDocument->formatOutput = true;
$configurationDocument->preserveWhiteSpace = false;

return $configurationDocument->saveXML();
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use RuntimeException;




final class MigrationBuilderException extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use RuntimeException;




final class MigrationException extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMElement;




final class CoveragePhpToReport extends LogToReportMigration
{
protected function forType(): string
{
return 'coverage-php';
}

protected function toReportFormat(DOMElement $logNode): DOMElement
{
$php = $logNode->ownerDocument->createElement('php');
$php->setAttribute('outputFile', $logNode->getAttribute('target'));

return $php;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;




final class IntroduceCoverageElement implements Migration
{
public function migrate(DOMDocument $document): void
{
$coverage = $document->createElement('coverage');

$document->documentElement->insertBefore(
$coverage,
$document->documentElement->firstChild,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function assert;
use function in_array;
use DOMDocument;
use DOMElement;
use PHPUnit\Util\Xml\SnapshotNodeList;




final class MoveWhitelistExcludesToCoverage implements Migration
{



public function migrate(DOMDocument $document): void
{
$whitelist = $document->getElementsByTagName('whitelist')->item(0);

if ($whitelist === null) {
return;
}

$excludeNodes = SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude'));

if ($excludeNodes->count() === 0) {
return;
}

$coverage = $document->getElementsByTagName('coverage')->item(0);

if (!$coverage instanceof DOMElement) {
throw new MigrationException('Unexpected state - No coverage element');
}

$targetExclude = $coverage->getElementsByTagName('exclude')->item(0);

if ($targetExclude === null) {
$targetExclude = $coverage->appendChild(
$document->createElement('exclude'),
);
}

foreach ($excludeNodes as $excludeNode) {
assert($excludeNode instanceof DOMElement);

foreach (SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) {
if (!$child instanceof DOMElement || !in_array($child->nodeName, ['directory', 'file'], true)) {
continue;
}

$targetExclude->appendChild($child);
}

if ($excludeNode->getElementsByTagName('*')->count() !== 0) {
throw new MigrationException('Dangling child elements in exclude found.');
}

$whitelist->removeChild($excludeNode);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMElement;




final class CoverageHtmlToReport extends LogToReportMigration
{
protected function forType(): string
{
return 'coverage-html';
}

protected function toReportFormat(DOMElement $logNode): DOMElement
{
$html = $logNode->ownerDocument->createElement('html');
$html->setAttribute('outputDirectory', $logNode->getAttribute('target'));

$this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']);

return $html;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMElement;




final class CoverageTextToReport extends LogToReportMigration
{
protected function forType(): string
{
return 'coverage-text';
}

protected function toReportFormat(DOMElement $logNode): DOMElement
{
$text = $logNode->ownerDocument->createElement('text');
$text->setAttribute('outputFile', $logNode->getAttribute('target'));

$this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']);

return $text;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMElement;




final class CoverageCloverToReport extends LogToReportMigration
{
protected function forType(): string
{
return 'coverage-clover';
}

protected function toReportFormat(DOMElement $logNode): DOMElement
{
$clover = $logNode->ownerDocument->createElement('clover');
$clover->setAttribute('outputFile', $logNode->getAttribute('target'));

return $clover;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMElement;




final class CoverageCrap4jToReport extends LogToReportMigration
{
protected function forType(): string
{
return 'coverage-crap4j';
}

protected function toReportFormat(DOMElement $logNode): DOMElement
{
$crap4j = $logNode->ownerDocument->createElement('crap4j');
$crap4j->setAttribute('outputFile', $logNode->getAttribute('target'));

$this->migrateAttributes($logNode, $crap4j, ['threshold']);

return $crap4j;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;
use DOMElement;




final class MoveAttributesFromRootToCoverage implements Migration
{



public function migrate(DOMDocument $document): void
{
$map = [
'disableCodeCoverageIgnore' => 'disableCodeCoverageIgnore',
'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits',
];

$root = $document->documentElement;

$coverage = $document->getElementsByTagName('coverage')->item(0);

if (!$coverage instanceof DOMElement) {
throw new MigrationException('Unexpected state - No coverage element');
}

foreach ($map as $old => $new) {
if (!$root->hasAttribute($old)) {
continue;
}

$coverage->setAttribute($new, $root->getAttribute($old));
$root->removeAttribute($old);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;
use DOMElement;




final class MoveAttributesFromFilterWhitelistToCoverage implements Migration
{



public function migrate(DOMDocument $document): void
{
$whitelist = $document->getElementsByTagName('whitelist')->item(0);

if (!$whitelist) {
return;
}

$coverage = $document->getElementsByTagName('coverage')->item(0);

if (!$coverage instanceof DOMElement) {
throw new MigrationException('Unexpected state - No coverage element');
}

$map = [
'addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles',
'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles',
];

foreach ($map as $old => $new) {
if (!$whitelist->hasAttribute($old)) {
continue;
}

$coverage->setAttribute($new, $whitelist->getAttribute($old));
$whitelist->removeAttribute($old);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function sprintf;
use DOMDocument;
use DOMElement;




final class RemoveEmptyFilter implements Migration
{



public function migrate(DOMDocument $document): void
{
$whitelist = $document->getElementsByTagName('whitelist')->item(0);

if ($whitelist instanceof DOMElement) {
$this->ensureEmpty($whitelist);
$whitelist->parentNode->removeChild($whitelist);
}

$filter = $document->getElementsByTagName('filter')->item(0);

if ($filter instanceof DOMElement) {
$this->ensureEmpty($filter);
$filter->parentNode->removeChild($filter);
}
}




private function ensureEmpty(DOMElement $element): void
{
if ($element->attributes->length > 0) {
throw new MigrationException(sprintf('%s element has unexpected attributes', $element->nodeName));
}

if ($element->getElementsByTagName('*')->length > 0) {
throw new MigrationException(sprintf('%s element has unexpected children', $element->nodeName));
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;
use DOMElement;




final class ConvertLogTypes implements Migration
{
public function migrate(DOMDocument $document): void
{
$logging = $document->getElementsByTagName('logging')->item(0);

if (!$logging instanceof DOMElement) {
return;
}
$types = [
'junit' => 'junit',
'teamcity' => 'teamcity',
'testdox-html' => 'testdoxHtml',
'testdox-text' => 'testdoxText',
'testdox-xml' => 'testdoxXml',
'plain' => 'text',
];

$logNodes = [];

foreach ($logging->getElementsByTagName('log') as $logNode) {
if (!isset($types[$logNode->getAttribute('type')])) {
continue;
}

$logNodes[] = $logNode;
}

foreach ($logNodes as $oldNode) {
$newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]);
$newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target'));

$logging->replaceChild($newLogNode, $oldNode);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMElement;




final class CoverageXmlToReport extends LogToReportMigration
{
protected function forType(): string
{
return 'coverage-xml';
}

protected function toReportFormat(DOMElement $logNode): DOMElement
{
$xml = $logNode->ownerDocument->createElement('xml');
$xml->setAttribute('outputDirectory', $logNode->getAttribute('target'));

return $xml;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;




final class RemoveCacheTokensAttribute implements Migration
{
public function migrate(DOMDocument $document): void
{
$root = $document->documentElement;

if ($root->hasAttribute('cacheTokens')) {
$root->removeAttribute('cacheTokens');
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function sprintf;
use DOMDocument;
use DOMElement;
use DOMXPath;




abstract class LogToReportMigration implements Migration
{



public function migrate(DOMDocument $document): void
{
$coverage = $document->getElementsByTagName('coverage')->item(0);

if (!$coverage instanceof DOMElement) {
throw new MigrationException('Unexpected state - No coverage element');
}

$logNode = $this->findLogNode($document);

if ($logNode === null) {
return;
}

$reportChild = $this->toReportFormat($logNode);

$report = $coverage->getElementsByTagName('report')->item(0);

if ($report === null) {
$report = $coverage->appendChild($document->createElement('report'));
}

$report->appendChild($reportChild);
$logNode->parentNode->removeChild($logNode);
}

protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes): void
{
foreach ($attributes as $attr) {
if (!$src->hasAttribute($attr)) {
continue;
}

$dest->setAttribute($attr, $src->getAttribute($attr));
$src->removeAttribute($attr);
}
}

abstract protected function forType(): string;

abstract protected function toReportFormat(DOMElement $logNode): DOMElement;

private function findLogNode(DOMDocument $document): ?DOMElement
{
$logNode = (new DOMXPath($document))->query(
sprintf('//logging/log[@type="%s"]', $this->forType()),
)->item(0);

if (!$logNode instanceof DOMElement) {
return null;
}

return $logNode;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function assert;
use DOMDocument;
use DOMElement;
use PHPUnit\Util\Xml\SnapshotNodeList;




final class RemoveLogTypes implements Migration
{
public function migrate(DOMDocument $document): void
{
$logging = $document->getElementsByTagName('logging')->item(0);

if (!$logging instanceof DOMElement) {
return;
}

foreach (SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) {
assert($logNode instanceof DOMElement);

switch ($logNode->getAttribute('type')) {
case 'json':
case 'tap':
$logging->removeChild($logNode);
}
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;




final class UpdateSchemaLocationTo93 implements Migration
{
public function migrate(DOMDocument $document): void
{
$document->documentElement->setAttributeNS(
'http://www.w3.org/2001/XMLSchema-instance',
'xsi:noNamespaceSchemaLocation',
'https://schema.phpunit.de/9.3/phpunit.xsd',
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;
use DOMElement;
use PHPUnit\Util\Xml\SnapshotNodeList;




final class MoveWhitelistIncludesToCoverage implements Migration
{



public function migrate(DOMDocument $document): void
{
$whitelist = $document->getElementsByTagName('whitelist')->item(0);

if ($whitelist === null) {
return;
}

$coverage = $document->getElementsByTagName('coverage')->item(0);

if (!$coverage instanceof DOMElement) {
throw new MigrationException('Unexpected state - No coverage element');
}

$include = $document->createElement('include');
$coverage->appendChild($include);

foreach (SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) {
if (!$child instanceof DOMElement) {
continue;
}

if (!($child->nodeName === 'directory' || $child->nodeName === 'file')) {
continue;
}

$include->appendChild($child);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use DOMDocument;




interface Migration
{
public function migrate(DOMDocument $document): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function version_compare;




final class MigrationBuilder
{
private const AVAILABLE_MIGRATIONS = [
'8.5' => [
RemoveLogTypes::class,
],

'9.2' => [
RemoveCacheTokensAttribute::class,
IntroduceCoverageElement::class,
MoveAttributesFromRootToCoverage::class,
MoveAttributesFromFilterWhitelistToCoverage::class,
MoveWhitelistIncludesToCoverage::class,
MoveWhitelistExcludesToCoverage::class,
RemoveEmptyFilter::class,
CoverageCloverToReport::class,
CoverageCrap4jToReport::class,
CoverageHtmlToReport::class,
CoveragePhpToReport::class,
CoverageTextToReport::class,
CoverageXmlToReport::class,
ConvertLogTypes::class,
UpdateSchemaLocationTo93::class,
],
];




public function build(string $fromVersion): array
{
$stack = [];

foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) {
if (version_compare($version, $fromVersion, '<')) {
continue;
}

foreach ($migrations as $migration) {
$stack[] = new $migration;
}
}

return $stack;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function str_replace;




final class Generator
{



private const TEMPLATE = <<<'EOT'
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/{phpunit_version}/phpunit.xsd"
         bootstrap="{bootstrap_script}"
         cacheResultFile="{cache_directory}/test-results"
         executionOrder="depends,defects"
         forceCoversAnnotation="true"
         beStrictAboutCoversAnnotation="true"
         beStrictAboutOutputDuringTests="true"
         beStrictAboutTodoAnnotatedTests="true"
         convertDeprecationsToExceptions="true"
         failOnRisky="true"
         failOnWarning="true"
         verbose="true">
    <testsuites>
        <testsuite name="default">
            <directory>{tests_directory}</directory>
        </testsuite>
    </testsuites>

    <coverage cacheDirectory="{cache_directory}/code-coverage"
              processUncoveredFiles="true">
        <include>
            <directory suffix=".php">{src_directory}</directory>
        </include>
    </coverage>
</phpunit>

EOT;

public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory): string
{
return str_replace(
[
'{phpunit_version}',
'{bootstrap_script}',
'{tests_directory}',
'{src_directory}',
'{cache_directory}',
],
[
$phpunitVersion,
$bootstrapScript,
$testsDirectory,
$srcDirectory,
$cacheDirectory,
],
self::TEMPLATE,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Directory
{



private $path;

public function __construct(string $path)
{
$this->path = $path;
}

public function path(): string
{
return $this->path;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class FileCollectionIterator implements Countable, Iterator
{



private $files;




private $position;

public function __construct(FileCollection $files)
{
$this->files = $files->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->files);
}

public function key(): int
{
return $this->position;
}

public function current(): File
{
return $this->files[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class FileCollection implements Countable, IteratorAggregate
{



private $files;




public static function fromArray(array $files): self
{
return new self(...$files);
}

private function __construct(File ...$files)
{
$this->files = $files;
}




public function asArray(): array
{
return $this->files;
}

public function count(): int
{
return count($this->files);
}

public function getIterator(): FileCollectionIterator
{
return new FileCollectionIterator($this);
}

public function isEmpty(): bool
{
return $this->count() === 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class DirectoryCollection implements Countable, IteratorAggregate
{



private $directories;




public static function fromArray(array $directories): self
{
return new self(...$directories);
}

private function __construct(Directory ...$directories)
{
$this->directories = $directories;
}




public function asArray(): array
{
return $this->directories;
}

public function count(): int
{
return count($this->directories);
}

public function getIterator(): DirectoryCollectionIterator
{
return new DirectoryCollectionIterator($this);
}

public function isEmpty(): bool
{
return $this->count() === 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class File
{



private $path;

public function __construct(string $path)
{
$this->path = $path;
}

public function path(): string
{
return $this->path;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class DirectoryCollectionIterator implements Countable, Iterator
{



private $directories;




private $position;

public function __construct(DirectoryCollection $directories)
{
$this->directories = $directories->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->directories);
}

public function key(): int
{
return $this->position;
}

public function current(): Directory
{
return $this->directories[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage;

use function count;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml;
use PHPUnit\TextUI\XmlConfiguration\Directory;
use PHPUnit\TextUI\XmlConfiguration\Exception;
use PHPUnit\TextUI\XmlConfiguration\FileCollection;






final class CodeCoverage
{



private $cacheDirectory;




private $directories;




private $files;




private $excludeDirectories;




private $excludeFiles;




private $pathCoverage;




private $includeUncoveredFiles;




private $processUncoveredFiles;




private $ignoreDeprecatedCodeUnits;




private $disableCodeCoverageIgnore;




private $clover;




private $cobertura;




private $crap4j;




private $html;




private $php;




private $text;




private $xml;

public function __construct(?Directory $cacheDirectory, DirectoryCollection $directories, FileCollection $files, DirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml)
{
$this->cacheDirectory = $cacheDirectory;
$this->directories = $directories;
$this->files = $files;
$this->excludeDirectories = $excludeDirectories;
$this->excludeFiles = $excludeFiles;
$this->pathCoverage = $pathCoverage;
$this->includeUncoveredFiles = $includeUncoveredFiles;
$this->processUncoveredFiles = $processUncoveredFiles;
$this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits;
$this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore;
$this->clover = $clover;
$this->cobertura = $cobertura;
$this->crap4j = $crap4j;
$this->html = $html;
$this->php = $php;
$this->text = $text;
$this->xml = $xml;
}




public function hasCacheDirectory(): bool
{
return $this->cacheDirectory !== null;
}




public function cacheDirectory(): Directory
{
if (!$this->hasCacheDirectory()) {
throw new Exception(
'No cache directory has been configured',
);
}

return $this->cacheDirectory;
}

public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport(): bool
{
return count($this->directories) > 0 || count($this->files) > 0;
}

public function directories(): DirectoryCollection
{
return $this->directories;
}

public function files(): FileCollection
{
return $this->files;
}

public function excludeDirectories(): DirectoryCollection
{
return $this->excludeDirectories;
}

public function excludeFiles(): FileCollection
{
return $this->excludeFiles;
}

public function pathCoverage(): bool
{
return $this->pathCoverage;
}

public function includeUncoveredFiles(): bool
{
return $this->includeUncoveredFiles;
}

public function ignoreDeprecatedCodeUnits(): bool
{
return $this->ignoreDeprecatedCodeUnits;
}

public function disableCodeCoverageIgnore(): bool
{
return $this->disableCodeCoverageIgnore;
}

public function processUncoveredFiles(): bool
{
return $this->processUncoveredFiles;
}




public function hasClover(): bool
{
return $this->clover !== null;
}




public function clover(): Clover
{
if (!$this->hasClover()) {
throw new Exception(
'Code Coverage report "Clover XML" has not been configured',
);
}

return $this->clover;
}




public function hasCobertura(): bool
{
return $this->cobertura !== null;
}




public function cobertura(): Cobertura
{
if (!$this->hasCobertura()) {
throw new Exception(
'Code Coverage report "Cobertura XML" has not been configured',
);
}

return $this->cobertura;
}




public function hasCrap4j(): bool
{
return $this->crap4j !== null;
}




public function crap4j(): Crap4j
{
if (!$this->hasCrap4j()) {
throw new Exception(
'Code Coverage report "Crap4J" has not been configured',
);
}

return $this->crap4j;
}




public function hasHtml(): bool
{
return $this->html !== null;
}




public function html(): Html
{
if (!$this->hasHtml()) {
throw new Exception(
'Code Coverage report "HTML" has not been configured',
);
}

return $this->html;
}




public function hasPhp(): bool
{
return $this->php !== null;
}




public function php(): Php
{
if (!$this->hasPhp()) {
throw new Exception(
'Code Coverage report "PHP" has not been configured',
);
}

return $this->php;
}




public function hasText(): bool
{
return $this->text !== null;
}




public function text(): Text
{
if (!$this->hasText()) {
throw new Exception(
'Code Coverage report "Text" has not been configured',
);
}

return $this->text;
}




public function hasXml(): bool
{
return $this->xml !== null;
}




public function xml(): Xml
{
if (!$this->hasXml()) {
throw new Exception(
'Code Coverage report "XML" has not been configured',
);
}

return $this->xml;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage;

use SebastianBergmann\CodeCoverage\Filter;




final class FilterMapper
{
public function map(Filter $filter, CodeCoverage $configuration): void
{
foreach ($configuration->directories() as $directory) {
$filter->includeDirectory(
$directory->path(),
$directory->suffix(),
$directory->prefix(),
);
}

foreach ($configuration->files() as $file) {
$filter->includeFile($file->path());
}

foreach ($configuration->excludeDirectories() as $directory) {
$filter->excludeDirectory(
$directory->path(),
$directory->suffix(),
$directory->prefix(),
);
}

foreach ($configuration->excludeFiles() as $file) {
$filter->excludeFile($file->path());
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Php
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Cobertura
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Text
{



private $target;




private $showUncoveredFiles;




private $showOnlySummary;

public function __construct(File $target, bool $showUncoveredFiles, bool $showOnlySummary)
{
$this->target = $target;
$this->showUncoveredFiles = $showUncoveredFiles;
$this->showOnlySummary = $showOnlySummary;
}

public function target(): File
{
return $this->target;
}

public function showUncoveredFiles(): bool
{
return $this->showUncoveredFiles;
}

public function showOnlySummary(): bool
{
return $this->showOnlySummary;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Clover
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Crap4j
{



private $target;




private $threshold;

public function __construct(File $target, int $threshold)
{
$this->target = $target;
$this->threshold = $threshold;
}

public function target(): File
{
return $this->target;
}

public function threshold(): int
{
return $this->threshold;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\Directory;






final class Xml
{



private $target;

public function __construct(Directory $target)
{
$this->target = $target;
}

public function target(): Directory
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\Directory;






final class Html
{



private $target;




private $lowUpperBound;




private $highLowerBound;

public function __construct(Directory $target, int $lowUpperBound, int $highLowerBound)
{
$this->target = $target;
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
}

public function target(): Directory
{
return $this->target;
}

public function lowUpperBound(): int
{
return $this->lowUpperBound;
}

public function highLowerBound(): int
{
return $this->highLowerBound;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter;






final class Directory
{



private $path;




private $prefix;




private $suffix;




private $group;

public function __construct(string $path, string $prefix, string $suffix, string $group)
{
$this->path = $path;
$this->prefix = $prefix;
$this->suffix = $suffix;
$this->group = $group;
}

public function path(): string
{
return $this->path;
}

public function prefix(): string
{
return $this->prefix;
}

public function suffix(): string
{
return $this->suffix;
}

public function group(): string
{
return $this->group;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter;

use function count;
use Countable;
use IteratorAggregate;








final class DirectoryCollection implements Countable, IteratorAggregate
{



private $directories;




public static function fromArray(array $directories): self
{
return new self(...$directories);
}

private function __construct(Directory ...$directories)
{
$this->directories = $directories;
}




public function asArray(): array
{
return $this->directories;
}

public function count(): int
{
return count($this->directories);
}

public function getIterator(): DirectoryCollectionIterator
{
return new DirectoryCollectionIterator($this);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class DirectoryCollectionIterator implements Countable, Iterator
{



private $directories;




private $position;

public function __construct(DirectoryCollection $directories)
{
$this->directories = $directories->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->directories);
}

public function key(): int
{
return $this->position;
}

public function current(): Directory
{
return $this->directories[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Junit
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Text
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Xml
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Html
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging;

use PHPUnit\TextUI\XmlConfiguration\File;






final class Text
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging;

use PHPUnit\TextUI\XmlConfiguration\File;






final class TeamCity
{



private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration\Logging;

use PHPUnit\TextUI\XmlConfiguration\Exception;
use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml;
use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText;
use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml;






final class Logging
{



private $junit;




private $text;




private $teamCity;




private $testDoxHtml;




private $testDoxText;




private $testDoxXml;

public function __construct(?Junit $junit, ?Text $text, ?TeamCity $teamCity, ?TestDoxHtml $testDoxHtml, ?TestDoxText $testDoxText, ?TestDoxXml $testDoxXml)
{
$this->junit = $junit;
$this->text = $text;
$this->teamCity = $teamCity;
$this->testDoxHtml = $testDoxHtml;
$this->testDoxText = $testDoxText;
$this->testDoxXml = $testDoxXml;
}

public function hasJunit(): bool
{
return $this->junit !== null;
}

public function junit(): Junit
{
if ($this->junit === null) {
throw new Exception('Logger "JUnit XML" is not configured');
}

return $this->junit;
}

public function hasText(): bool
{
return $this->text !== null;
}

public function text(): Text
{
if ($this->text === null) {
throw new Exception('Logger "Text" is not configured');
}

return $this->text;
}

public function hasTeamCity(): bool
{
return $this->teamCity !== null;
}

public function teamCity(): TeamCity
{
if ($this->teamCity === null) {
throw new Exception('Logger "Team City" is not configured');
}

return $this->teamCity;
}

public function hasTestDoxHtml(): bool
{
return $this->testDoxHtml !== null;
}

public function testDoxHtml(): TestDoxHtml
{
if ($this->testDoxHtml === null) {
throw new Exception('Logger "TestDox HTML" is not configured');
}

return $this->testDoxHtml;
}

public function hasTestDoxText(): bool
{
return $this->testDoxText !== null;
}

public function testDoxText(): TestDoxText
{
if ($this->testDoxText === null) {
throw new Exception('Logger "TestDox Text" is not configured');
}

return $this->testDoxText;
}

public function hasTestDoxXml(): bool
{
return $this->testDoxXml !== null;
}

public function testDoxXml(): TestDoxXml
{
if ($this->testDoxXml === null) {
throw new Exception('Logger "TestDox XML" is not configured');
}

return $this->testDoxXml;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use PHPUnit\Util\VersionComparisonOperator;






final class TestDirectory
{



private $path;




private $prefix;




private $suffix;




private $phpVersion;




private $phpVersionOperator;

public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, VersionComparisonOperator $phpVersionOperator)
{
$this->path = $path;
$this->prefix = $prefix;
$this->suffix = $suffix;
$this->phpVersion = $phpVersion;
$this->phpVersionOperator = $phpVersionOperator;
}

public function path(): string
{
return $this->path;
}

public function prefix(): string
{
return $this->prefix;
}

public function suffix(): string
{
return $this->suffix;
}

public function phpVersion(): string
{
return $this->phpVersion;
}

public function phpVersionOperator(): VersionComparisonOperator
{
return $this->phpVersionOperator;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use PHPUnit\Util\VersionComparisonOperator;






final class TestFile
{



private $path;




private $phpVersion;




private $phpVersionOperator;

public function __construct(string $path, string $phpVersion, VersionComparisonOperator $phpVersionOperator)
{
$this->path = $path;
$this->phpVersion = $phpVersion;
$this->phpVersionOperator = $phpVersionOperator;
}

public function path(): string
{
return $this->path;
}

public function phpVersion(): string
{
return $this->phpVersion;
}

public function phpVersionOperator(): VersionComparisonOperator
{
return $this->phpVersionOperator;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class TestDirectoryCollectionIterator implements Countable, Iterator
{



private $directories;




private $position;

public function __construct(TestDirectoryCollection $directories)
{
$this->directories = $directories->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->directories);
}

public function key(): int
{
return $this->position;
}

public function current(): TestDirectory
{
return $this->directories[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class TestSuiteCollection implements Countable, IteratorAggregate
{



private $testSuites;




public static function fromArray(array $testSuites): self
{
return new self(...$testSuites);
}

private function __construct(TestSuite ...$testSuites)
{
$this->testSuites = $testSuites;
}




public function asArray(): array
{
return $this->testSuites;
}

public function count(): int
{
return count($this->testSuites);
}

public function getIterator(): TestSuiteCollectionIterator
{
return new TestSuiteCollectionIterator($this);
}

public function isEmpty(): bool
{
return $this->count() === 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class TestFileCollection implements Countable, IteratorAggregate
{



private $files;




public static function fromArray(array $files): self
{
return new self(...$files);
}

private function __construct(TestFile ...$files)
{
$this->files = $files;
}




public function asArray(): array
{
return $this->files;
}

public function count(): int
{
return count($this->files);
}

public function getIterator(): TestFileCollectionIterator
{
return new TestFileCollectionIterator($this);
}

public function isEmpty(): bool
{
return $this->count() === 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class TestSuiteCollectionIterator implements Countable, Iterator
{



private $testSuites;




private $position;

public function __construct(TestSuiteCollection $testSuites)
{
$this->testSuites = $testSuites->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->testSuites);
}

public function key(): int
{
return $this->position;
}

public function current(): TestSuite
{
return $this->testSuites[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use Countable;
use IteratorAggregate;








final class TestDirectoryCollection implements Countable, IteratorAggregate
{



private $directories;




public static function fromArray(array $directories): self
{
return new self(...$directories);
}

private function __construct(TestDirectory ...$directories)
{
$this->directories = $directories;
}




public function asArray(): array
{
return $this->directories;
}

public function count(): int
{
return count($this->directories);
}

public function getIterator(): TestDirectoryCollectionIterator
{
return new TestDirectoryCollectionIterator($this);
}

public function isEmpty(): bool
{
return $this->count() === 0;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class TestFileCollectionIterator implements Countable, Iterator
{



private $files;




private $position;

public function __construct(TestFileCollection $files)
{
$this->files = $files->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->files);
}

public function key(): int
{
return $this->position;
}

public function current(): TestFile
{
return $this->files[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class TestSuite
{



private $name;




private $directories;




private $files;




private $exclude;

public function __construct(string $name, TestDirectoryCollection $directories, TestFileCollection $files, FileCollection $exclude)
{
$this->name = $name;
$this->directories = $directories;
$this->files = $files;
$this->exclude = $exclude;
}

public function name(): string
{
return $this->name;
}

public function directories(): TestDirectoryCollection
{
return $this->directories;
}

public function files(): TestFileCollection
{
return $this->files;
}

public function exclude(): FileCollection
{
return $this->exclude;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use IteratorAggregate;








final class GroupCollection implements IteratorAggregate
{



private $groups;




public static function fromArray(array $groups): self
{
return new self(...$groups);
}

private function __construct(Group ...$groups)
{
$this->groups = $groups;
}




public function asArray(): array
{
return $this->groups;
}




public function asArrayOfStrings(): array
{
$result = [];

foreach ($this->groups as $group) {
$result[] = $group->name();
}

return $result;
}

public function isEmpty(): bool
{
return empty($this->groups);
}

public function getIterator(): GroupCollectionIterator
{
return new GroupCollectionIterator($this);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Group
{



private $name;

public function __construct(string $name)
{
$this->name = $name;
}

public function name(): string
{
return $this->name;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Groups
{



private $include;




private $exclude;

public function __construct(GroupCollection $include, GroupCollection $exclude)
{
$this->include = $include;
$this->exclude = $exclude;
}

public function hasInclude(): bool
{
return !$this->include->isEmpty();
}

public function include(): GroupCollection
{
return $this->include;
}

public function hasExclude(): bool
{
return !$this->exclude->isEmpty();
}

public function exclude(): GroupCollection
{
return $this->exclude;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class GroupCollectionIterator implements Countable, Iterator
{



private $groups;




private $position;

public function __construct(GroupCollection $groups)
{
$this->groups = $groups->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->groups);
}

public function key(): int
{
return $this->position;
}

public function current(): Group
{
return $this->groups[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use RuntimeException;




final class Exception extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use IteratorAggregate;








final class ExtensionCollection implements IteratorAggregate
{



private $extensions;




public static function fromArray(array $extensions): self
{
return new self(...$extensions);
}

private function __construct(Extension ...$extensions)
{
$this->extensions = $extensions;
}




public function asArray(): array
{
return $this->extensions;
}

public function getIterator(): ExtensionCollectionIterator
{
return new ExtensionCollectionIterator($this);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use function count;
use function iterator_count;
use Countable;
use Iterator;






final class ExtensionCollectionIterator implements Countable, Iterator
{



private $extensions;




private $position;

public function __construct(ExtensionCollection $extensions)
{
$this->extensions = $extensions->asArray();
}

public function count(): int
{
return iterator_count($this);
}

public function rewind(): void
{
$this->position = 0;
}

public function valid(): bool
{
return $this->position < count($this->extensions);
}

public function key(): int
{
return $this->position;
}

public function current(): Extension
{
return $this->extensions[$this->position];
}

public function next(): void
{
$this->position++;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class PHPUnit
{



private $cacheResult;




private $cacheResultFile;




private $columns;




private $colors;




private $stderr;




private $noInteraction;




private $verbose;




private $reverseDefectList;




private $convertDeprecationsToExceptions;




private $convertErrorsToExceptions;




private $convertNoticesToExceptions;




private $convertWarningsToExceptions;




private $forceCoversAnnotation;




private $bootstrap;




private $processIsolation;




private $failOnEmptyTestSuite;




private $failOnIncomplete;




private $failOnRisky;




private $failOnSkipped;




private $failOnWarning;




private $stopOnDefect;




private $stopOnError;




private $stopOnFailure;




private $stopOnWarning;




private $stopOnIncomplete;




private $stopOnRisky;




private $stopOnSkipped;




private $extensionsDirectory;






private $testSuiteLoaderClass;






private $testSuiteLoaderFile;




private $printerClass;




private $printerFile;




private $beStrictAboutChangesToGlobalState;




private $beStrictAboutOutputDuringTests;




private $beStrictAboutResourceUsageDuringSmallTests;




private $beStrictAboutTestsThatDoNotTestAnything;




private $beStrictAboutTodoAnnotatedTests;




private $beStrictAboutCoversAnnotation;




private $enforceTimeLimit;




private $defaultTimeLimit;




private $timeoutForSmallTests;




private $timeoutForMediumTests;




private $timeoutForLargeTests;




private $defaultTestSuite;




private $executionOrder;




private $resolveDependencies;




private $defectsFirst;




private $backupGlobals;




private $backupStaticAttributes;




private $registerMockObjectsFromTestArgumentsRecursively;




private $conflictBetweenPrinterClassAndTestdox;

public function __construct(bool $cacheResult, ?string $cacheResultFile, $columns, string $colors, bool $stderr, bool $noInteraction, bool $verbose, bool $reverseDefectList, bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions, bool $forceCoversAnnotation, ?string $bootstrap, bool $processIsolation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnError, bool $stopOnFailure, bool $stopOnWarning, bool $stopOnIncomplete, bool $stopOnRisky, bool $stopOnSkipped, ?string $extensionsDirectory, ?string $testSuiteLoaderClass, ?string $testSuiteLoaderFile, ?string $printerClass, ?string $printerFile, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutResourceUsageDuringSmallTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutTodoAnnotatedTests, bool $beStrictAboutCoversAnnotation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticAttributes, bool $registerMockObjectsFromTestArgumentsRecursively, bool $conflictBetweenPrinterClassAndTestdox)
{
$this->cacheResult = $cacheResult;
$this->cacheResultFile = $cacheResultFile;
$this->columns = $columns;
$this->colors = $colors;
$this->stderr = $stderr;
$this->noInteraction = $noInteraction;
$this->verbose = $verbose;
$this->reverseDefectList = $reverseDefectList;
$this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions;
$this->convertErrorsToExceptions = $convertErrorsToExceptions;
$this->convertNoticesToExceptions = $convertNoticesToExceptions;
$this->convertWarningsToExceptions = $convertWarningsToExceptions;
$this->forceCoversAnnotation = $forceCoversAnnotation;
$this->bootstrap = $bootstrap;
$this->processIsolation = $processIsolation;
$this->failOnEmptyTestSuite = $failOnEmptyTestSuite;
$this->failOnIncomplete = $failOnIncomplete;
$this->failOnRisky = $failOnRisky;
$this->failOnSkipped = $failOnSkipped;
$this->failOnWarning = $failOnWarning;
$this->stopOnDefect = $stopOnDefect;
$this->stopOnError = $stopOnError;
$this->stopOnFailure = $stopOnFailure;
$this->stopOnWarning = $stopOnWarning;
$this->stopOnIncomplete = $stopOnIncomplete;
$this->stopOnRisky = $stopOnRisky;
$this->stopOnSkipped = $stopOnSkipped;
$this->extensionsDirectory = $extensionsDirectory;
$this->testSuiteLoaderClass = $testSuiteLoaderClass;
$this->testSuiteLoaderFile = $testSuiteLoaderFile;
$this->printerClass = $printerClass;
$this->printerFile = $printerFile;
$this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
$this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests;
$this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests;
$this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything;
$this->beStrictAboutTodoAnnotatedTests = $beStrictAboutTodoAnnotatedTests;
$this->beStrictAboutCoversAnnotation = $beStrictAboutCoversAnnotation;
$this->enforceTimeLimit = $enforceTimeLimit;
$this->defaultTimeLimit = $defaultTimeLimit;
$this->timeoutForSmallTests = $timeoutForSmallTests;
$this->timeoutForMediumTests = $timeoutForMediumTests;
$this->timeoutForLargeTests = $timeoutForLargeTests;
$this->defaultTestSuite = $defaultTestSuite;
$this->executionOrder = $executionOrder;
$this->resolveDependencies = $resolveDependencies;
$this->defectsFirst = $defectsFirst;
$this->backupGlobals = $backupGlobals;
$this->backupStaticAttributes = $backupStaticAttributes;
$this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively;
$this->conflictBetweenPrinterClassAndTestdox = $conflictBetweenPrinterClassAndTestdox;
}

public function cacheResult(): bool
{
return $this->cacheResult;
}




public function hasCacheResultFile(): bool
{
return $this->cacheResultFile !== null;
}




public function cacheResultFile(): string
{
if (!$this->hasCacheResultFile()) {
throw new Exception('Cache result file is not configured');
}

return (string) $this->cacheResultFile;
}

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

public function colors(): string
{
return $this->colors;
}

public function stderr(): bool
{
return $this->stderr;
}

public function noInteraction(): bool
{
return $this->noInteraction;
}

public function verbose(): bool
{
return $this->verbose;
}

public function reverseDefectList(): bool
{
return $this->reverseDefectList;
}

public function convertDeprecationsToExceptions(): bool
{
return $this->convertDeprecationsToExceptions;
}

public function convertErrorsToExceptions(): bool
{
return $this->convertErrorsToExceptions;
}

public function convertNoticesToExceptions(): bool
{
return $this->convertNoticesToExceptions;
}

public function convertWarningsToExceptions(): bool
{
return $this->convertWarningsToExceptions;
}

public function forceCoversAnnotation(): bool
{
return $this->forceCoversAnnotation;
}




public function hasBootstrap(): bool
{
return $this->bootstrap !== null;
}




public function bootstrap(): string
{
if (!$this->hasBootstrap()) {
throw new Exception('Bootstrap script is not configured');
}

return (string) $this->bootstrap;
}

public function processIsolation(): bool
{
return $this->processIsolation;
}

public function failOnEmptyTestSuite(): bool
{
return $this->failOnEmptyTestSuite;
}

public function failOnIncomplete(): bool
{
return $this->failOnIncomplete;
}

public function failOnRisky(): bool
{
return $this->failOnRisky;
}

public function failOnSkipped(): bool
{
return $this->failOnSkipped;
}

public function failOnWarning(): bool
{
return $this->failOnWarning;
}

public function stopOnDefect(): bool
{
return $this->stopOnDefect;
}

public function stopOnError(): bool
{
return $this->stopOnError;
}

public function stopOnFailure(): bool
{
return $this->stopOnFailure;
}

public function stopOnWarning(): bool
{
return $this->stopOnWarning;
}

public function stopOnIncomplete(): bool
{
return $this->stopOnIncomplete;
}

public function stopOnRisky(): bool
{
return $this->stopOnRisky;
}

public function stopOnSkipped(): bool
{
return $this->stopOnSkipped;
}




public function hasExtensionsDirectory(): bool
{
return $this->extensionsDirectory !== null;
}




public function extensionsDirectory(): string
{
if (!$this->hasExtensionsDirectory()) {
throw new Exception('Extensions directory is not configured');
}

return (string) $this->extensionsDirectory;
}






public function hasTestSuiteLoaderClass(): bool
{
return $this->testSuiteLoaderClass !== null;
}






public function testSuiteLoaderClass(): string
{
if (!$this->hasTestSuiteLoaderClass()) {
throw new Exception('TestSuiteLoader class is not configured');
}

return (string) $this->testSuiteLoaderClass;
}






public function hasTestSuiteLoaderFile(): bool
{
return $this->testSuiteLoaderFile !== null;
}






public function testSuiteLoaderFile(): string
{
if (!$this->hasTestSuiteLoaderFile()) {
throw new Exception('TestSuiteLoader sourcecode file is not configured');
}

return (string) $this->testSuiteLoaderFile;
}




public function hasPrinterClass(): bool
{
return $this->printerClass !== null;
}




public function printerClass(): string
{
if (!$this->hasPrinterClass()) {
throw new Exception('ResultPrinter class is not configured');
}

return (string) $this->printerClass;
}




public function hasPrinterFile(): bool
{
return $this->printerFile !== null;
}




public function printerFile(): string
{
if (!$this->hasPrinterFile()) {
throw new Exception('ResultPrinter sourcecode file is not configured');
}

return (string) $this->printerFile;
}

public function beStrictAboutChangesToGlobalState(): bool
{
return $this->beStrictAboutChangesToGlobalState;
}

public function beStrictAboutOutputDuringTests(): bool
{
return $this->beStrictAboutOutputDuringTests;
}

public function beStrictAboutResourceUsageDuringSmallTests(): bool
{
return $this->beStrictAboutResourceUsageDuringSmallTests;
}

public function beStrictAboutTestsThatDoNotTestAnything(): bool
{
return $this->beStrictAboutTestsThatDoNotTestAnything;
}

public function beStrictAboutTodoAnnotatedTests(): bool
{
return $this->beStrictAboutTodoAnnotatedTests;
}

public function beStrictAboutCoversAnnotation(): bool
{
return $this->beStrictAboutCoversAnnotation;
}

public function enforceTimeLimit(): bool
{
return $this->enforceTimeLimit;
}

public function defaultTimeLimit(): int
{
return $this->defaultTimeLimit;
}

public function timeoutForSmallTests(): int
{
return $this->timeoutForSmallTests;
}

public function timeoutForMediumTests(): int
{
return $this->timeoutForMediumTests;
}

public function timeoutForLargeTests(): int
{
return $this->timeoutForLargeTests;
}




public function hasDefaultTestSuite(): bool
{
return $this->defaultTestSuite !== null;
}




public function defaultTestSuite(): string
{
if (!$this->hasDefaultTestSuite()) {
throw new Exception('Default test suite is not configured');
}

return (string) $this->defaultTestSuite;
}

public function executionOrder(): int
{
return $this->executionOrder;
}

public function resolveDependencies(): bool
{
return $this->resolveDependencies;
}

public function defectsFirst(): bool
{
return $this->defectsFirst;
}

public function backupGlobals(): bool
{
return $this->backupGlobals;
}

public function backupStaticAttributes(): bool
{
return $this->backupStaticAttributes;
}

public function registerMockObjectsFromTestArgumentsRecursively(): bool
{
return $this->registerMockObjectsFromTestArgumentsRecursively;
}

public function conflictBetweenPrinterClassAndTestdox(): bool
{
return $this->conflictBetweenPrinterClassAndTestdox;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;






final class Extension
{





private $className;




private $sourceFile;




private $arguments;




public function __construct(string $className, string $sourceFile, array $arguments)
{
$this->className = $className;
$this->sourceFile = $sourceFile;
$this->arguments = $arguments;
}




public function className(): string
{
return $this->className;
}

public function hasSourceFile(): bool
{
return $this->sourceFile !== '';
}

public function sourceFile(): string
{
return $this->sourceFile;
}

public function hasArguments(): bool
{
return !empty($this->arguments);
}

public function arguments(): array
{
return $this->arguments;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage;
use PHPUnit\TextUI\XmlConfiguration\Logging\Logging;
use PHPUnit\Util\Xml\ValidationResult;






final class Configuration
{



private $filename;




private $validationResult;




private $extensions;




private $codeCoverage;




private $groups;




private $testdoxGroups;




private $listeners;




private $logging;




private $php;




private $phpunit;




private $testSuite;

public function __construct(string $filename, ValidationResult $validationResult, ExtensionCollection $extensions, CodeCoverage $codeCoverage, Groups $groups, Groups $testdoxGroups, ExtensionCollection $listeners, Logging $logging, Php $php, PHPUnit $phpunit, TestSuiteCollection $testSuite)
{
$this->filename = $filename;
$this->validationResult = $validationResult;
$this->extensions = $extensions;
$this->codeCoverage = $codeCoverage;
$this->groups = $groups;
$this->testdoxGroups = $testdoxGroups;
$this->listeners = $listeners;
$this->logging = $logging;
$this->php = $php;
$this->phpunit = $phpunit;
$this->testSuite = $testSuite;
}

public function filename(): string
{
return $this->filename;
}

public function hasValidationErrors(): bool
{
return $this->validationResult->hasValidationErrors();
}

public function validationErrors(): string
{
return $this->validationResult->asString();
}

public function extensions(): ExtensionCollection
{
return $this->extensions;
}

public function codeCoverage(): CodeCoverage
{
return $this->codeCoverage;
}

public function groups(): Groups
{
return $this->groups;
}

public function testdoxGroups(): Groups
{
return $this->testdoxGroups;
}

public function listeners(): ExtensionCollection
{
return $this->listeners;
}

public function logging(): Logging
{
return $this->logging;
}

public function php(): Php
{
return $this->php;
}

public function phpunit(): PHPUnit
{
return $this->phpunit;
}

public function testSuite(): TestSuiteCollection
{
return $this->testSuite;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\XmlConfiguration;

use const DIRECTORY_SEPARATOR;
use const PHP_VERSION;
use function assert;
use function defined;
use function dirname;
use function explode;
use function is_file;
use function is_numeric;
use function preg_match;
use function stream_resolve_include_path;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
use function trim;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMNodeList;
use DOMXPath;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\Runner\Version;
use PHPUnit\TextUI\DefaultResultPrinter;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory as FilterDirectory;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection as FilterDirectoryCollection;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html as CodeCoverageHtml;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php as CodeCoveragePhp;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text as CodeCoverageText;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml as CodeCoverageXml;
use PHPUnit\TextUI\XmlConfiguration\Logging\Junit;
use PHPUnit\TextUI\XmlConfiguration\Logging\Logging;
use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity;
use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml;
use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText;
use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml;
use PHPUnit\TextUI\XmlConfiguration\Logging\Text;
use PHPUnit\TextUI\XmlConfiguration\TestSuite as TestSuiteConfiguration;
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
use PHPUnit\Util\VersionComparisonOperator;
use PHPUnit\Util\Xml;
use PHPUnit\Util\Xml\Exception as XmlException;
use PHPUnit\Util\Xml\Loader as XmlLoader;
use PHPUnit\Util\Xml\SchemaFinder;
use PHPUnit\Util\Xml\Validator;




final class Loader
{



public function load(string $filename): Configuration
{
try {
$document = (new XmlLoader)->loadFile($filename, false, true, true);
} catch (XmlException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

$xpath = new DOMXPath($document);

try {
$xsdFilename = (new SchemaFinder)->find(Version::series());
} catch (XmlException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

return new Configuration(
$filename,
(new Validator)->validate($document, $xsdFilename),
$this->extensions($filename, $xpath),
$this->codeCoverage($filename, $xpath, $document),
$this->groups($xpath),
$this->testdoxGroups($xpath),
$this->listeners($filename, $xpath),
$this->logging($filename, $xpath),
$this->php($filename, $xpath),
$this->phpunit($filename, $document),
$this->testSuite($filename, $xpath),
);
}

public function logging(string $filename, DOMXPath $xpath): Logging
{
if ($xpath->query('logging/log')->length !== 0) {
return $this->legacyLogging($filename, $xpath);
}

$junit = null;
$element = $this->element($xpath, 'logging/junit');

if ($element) {
$junit = new Junit(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$text = null;
$element = $this->element($xpath, 'logging/text');

if ($element) {
$text = new Text(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$teamCity = null;
$element = $this->element($xpath, 'logging/teamcity');

if ($element) {
$teamCity = new TeamCity(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$testDoxHtml = null;
$element = $this->element($xpath, 'logging/testdoxHtml');

if ($element) {
$testDoxHtml = new TestDoxHtml(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$testDoxText = null;
$element = $this->element($xpath, 'logging/testdoxText');

if ($element) {
$testDoxText = new TestDoxText(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$testDoxXml = null;
$element = $this->element($xpath, 'logging/testdoxXml');

if ($element) {
$testDoxXml = new TestDoxXml(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

return new Logging(
$junit,
$text,
$teamCity,
$testDoxHtml,
$testDoxText,
$testDoxXml,
);
}

public function legacyLogging(string $filename, DOMXPath $xpath): Logging
{
$junit = null;
$teamCity = null;
$testDoxHtml = null;
$testDoxText = null;
$testDoxXml = null;
$text = null;

foreach ($xpath->query('logging/log') as $log) {
assert($log instanceof DOMElement);

$type = (string) $log->getAttribute('type');
$target = (string) $log->getAttribute('target');

if (!$target) {
continue;
}

$target = $this->toAbsolutePath($filename, $target);

switch ($type) {
case 'plain':
$text = new Text(
new File($target),
);

break;

case 'junit':
$junit = new Junit(
new File($target),
);

break;

case 'teamcity':
$teamCity = new TeamCity(
new File($target),
);

break;

case 'testdox-html':
$testDoxHtml = new TestDoxHtml(
new File($target),
);

break;

case 'testdox-text':
$testDoxText = new TestDoxText(
new File($target),
);

break;

case 'testdox-xml':
$testDoxXml = new TestDoxXml(
new File($target),
);

break;
}
}

return new Logging(
$junit,
$text,
$teamCity,
$testDoxHtml,
$testDoxText,
$testDoxXml,
);
}

private function extensions(string $filename, DOMXPath $xpath): ExtensionCollection
{
$extensions = [];

foreach ($xpath->query('extensions/extension') as $extension) {
assert($extension instanceof DOMElement);

$extensions[] = $this->getElementConfigurationParameters($filename, $extension);
}

return ExtensionCollection::fromArray($extensions);
}

private function getElementConfigurationParameters(string $filename, DOMElement $element): Extension
{

$class = (string) $element->getAttribute('class');
$file = '';
$arguments = $this->getConfigurationArguments($filename, $element->childNodes);

if ($element->getAttribute('file')) {
$file = $this->toAbsolutePath(
$filename,
(string) $element->getAttribute('file'),
true,
);
}

return new Extension($class, $file, $arguments);
}

private function toAbsolutePath(string $filename, string $path, bool $useIncludePath = false): string
{
$path = trim($path);

if (strpos($path, '/') === 0) {
return $path;
}









if (defined('PHP_WINDOWS_VERSION_BUILD') &&
($path[0] === '\\' || (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))))) {
return $path;
}

if (strpos($path, '://') !== false) {
return $path;
}

$file = dirname($filename) . DIRECTORY_SEPARATOR . $path;

if ($useIncludePath && !is_file($file)) {
$includePathFile = stream_resolve_include_path($path);

if ($includePathFile) {
$file = $includePathFile;
}
}

return $file;
}

private function getConfigurationArguments(string $filename, DOMNodeList $nodes): array
{
$arguments = [];

if ($nodes->length === 0) {
return $arguments;
}

foreach ($nodes as $node) {
if (!$node instanceof DOMElement) {
continue;
}

if ($node->tagName !== 'arguments') {
continue;
}

foreach ($node->childNodes as $argument) {
if (!$argument instanceof DOMElement) {
continue;
}

if ($argument->tagName === 'file' || $argument->tagName === 'directory') {
$arguments[] = $this->toAbsolutePath($filename, (string) $argument->textContent);
} else {
$arguments[] = Xml::xmlToVariable($argument);
}
}
}

return $arguments;
}

private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document): CodeCoverage
{
if ($xpath->query('filter/whitelist')->length !== 0) {
return $this->legacyCodeCoverage($filename, $xpath, $document);
}

$cacheDirectory = null;
$pathCoverage = false;
$includeUncoveredFiles = true;
$processUncoveredFiles = false;
$ignoreDeprecatedCodeUnits = false;
$disableCodeCoverageIgnore = false;

$element = $this->element($xpath, 'coverage');

if ($element) {
$cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory');

if ($cacheDirectory !== null) {
$cacheDirectory = new Directory(
$this->toAbsolutePath($filename, $cacheDirectory),
);
}

$pathCoverage = $this->getBooleanAttribute(
$element,
'pathCoverage',
false,
);

$includeUncoveredFiles = $this->getBooleanAttribute(
$element,
'includeUncoveredFiles',
true,
);

$processUncoveredFiles = $this->getBooleanAttribute(
$element,
'processUncoveredFiles',
false,
);

$ignoreDeprecatedCodeUnits = $this->getBooleanAttribute(
$element,
'ignoreDeprecatedCodeUnits',
false,
);

$disableCodeCoverageIgnore = $this->getBooleanAttribute(
$element,
'disableCodeCoverageIgnore',
false,
);
}

$clover = null;
$element = $this->element($xpath, 'coverage/report/clover');

if ($element) {
$clover = new Clover(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$cobertura = null;
$element = $this->element($xpath, 'coverage/report/cobertura');

if ($element) {
$cobertura = new Cobertura(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$crap4j = null;
$element = $this->element($xpath, 'coverage/report/crap4j');

if ($element) {
$crap4j = new Crap4j(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
$this->getIntegerAttribute($element, 'threshold', 30),
);
}

$html = null;
$element = $this->element($xpath, 'coverage/report/html');

if ($element) {
$html = new CodeCoverageHtml(
new Directory(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputDirectory'),
),
),
$this->getIntegerAttribute($element, 'lowUpperBound', 50),
$this->getIntegerAttribute($element, 'highLowerBound', 90),
);
}

$php = null;
$element = $this->element($xpath, 'coverage/report/php');

if ($element) {
$php = new CodeCoveragePhp(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
);
}

$text = null;
$element = $this->element($xpath, 'coverage/report/text');

if ($element) {
$text = new CodeCoverageText(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile'),
),
),
$this->getBooleanAttribute($element, 'showUncoveredFiles', false),
$this->getBooleanAttribute($element, 'showOnlySummary', false),
);
}

$xml = null;
$element = $this->element($xpath, 'coverage/report/xml');

if ($element) {
$xml = new CodeCoverageXml(
new Directory(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputDirectory'),
),
),
);
}

return new CodeCoverage(
$cacheDirectory,
$this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'),
$this->readFilterFiles($filename, $xpath, 'coverage/include/file'),
$this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'),
$this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'),
$pathCoverage,
$includeUncoveredFiles,
$processUncoveredFiles,
$ignoreDeprecatedCodeUnits,
$disableCodeCoverageIgnore,
$clover,
$cobertura,
$crap4j,
$html,
$php,
$text,
$xml,
);
}




private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document): CodeCoverage
{
$ignoreDeprecatedCodeUnits = $this->getBooleanAttribute(
$document->documentElement,
'ignoreDeprecatedCodeUnitsFromCodeCoverage',
false,
);

$disableCodeCoverageIgnore = $this->getBooleanAttribute(
$document->documentElement,
'disableCodeCoverageIgnore',
false,
);

$includeUncoveredFiles = true;
$processUncoveredFiles = false;

$element = $this->element($xpath, 'filter/whitelist');

if ($element) {
if ($element->hasAttribute('addUncoveredFilesFromWhitelist')) {
$includeUncoveredFiles = (bool) $this->getBoolean(
(string) $element->getAttribute('addUncoveredFilesFromWhitelist'),
true,
);
}

if ($element->hasAttribute('processUncoveredFilesFromWhitelist')) {
$processUncoveredFiles = (bool) $this->getBoolean(
(string) $element->getAttribute('processUncoveredFilesFromWhitelist'),
false,
);
}
}

$clover = null;
$cobertura = null;
$crap4j = null;
$html = null;
$php = null;
$text = null;
$xml = null;

foreach ($xpath->query('logging/log') as $log) {
assert($log instanceof DOMElement);

$type = (string) $log->getAttribute('type');
$target = (string) $log->getAttribute('target');

if (!$target) {
continue;
}

$target = $this->toAbsolutePath($filename, $target);

switch ($type) {
case 'coverage-clover':
$clover = new Clover(
new File($target),
);

break;

case 'coverage-cobertura':
$cobertura = new Cobertura(
new File($target),
);

break;

case 'coverage-crap4j':
$crap4j = new Crap4j(
new File($target),
$this->getIntegerAttribute($log, 'threshold', 30),
);

break;

case 'coverage-html':
$html = new CodeCoverageHtml(
new Directory($target),
$this->getIntegerAttribute($log, 'lowUpperBound', 50),
$this->getIntegerAttribute($log, 'highLowerBound', 90),
);

break;

case 'coverage-php':
$php = new CodeCoveragePhp(
new File($target),
);

break;

case 'coverage-text':
$text = new CodeCoverageText(
new File($target),
$this->getBooleanAttribute($log, 'showUncoveredFiles', false),
$this->getBooleanAttribute($log, 'showOnlySummary', false),
);

break;

case 'coverage-xml':
$xml = new CodeCoverageXml(
new Directory($target),
);

break;
}
}

return new CodeCoverage(
null,
$this->readFilterDirectories($filename, $xpath, 'filter/whitelist/directory'),
$this->readFilterFiles($filename, $xpath, 'filter/whitelist/file'),
$this->readFilterDirectories($filename, $xpath, 'filter/whitelist/exclude/directory'),
$this->readFilterFiles($filename, $xpath, 'filter/whitelist/exclude/file'),
false,
$includeUncoveredFiles,
$processUncoveredFiles,
$ignoreDeprecatedCodeUnits,
$disableCodeCoverageIgnore,
$clover,
$cobertura,
$crap4j,
$html,
$php,
$text,
$xml,
);
}











private function getBoolean(string $value, $default)
{
if (strtolower($value) === 'false') {
return false;
}

if (strtolower($value) === 'true') {
return true;
}

return $default;
}

private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query): FilterDirectoryCollection
{
$directories = [];

foreach ($xpath->query($query) as $directoryNode) {
assert($directoryNode instanceof DOMElement);

$directoryPath = (string) $directoryNode->textContent;

if (!$directoryPath) {
continue;
}

$directories[] = new FilterDirectory(
$this->toAbsolutePath($filename, $directoryPath),
$directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '',
$directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php',
$directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT',
);
}

return FilterDirectoryCollection::fromArray($directories);
}

private function readFilterFiles(string $filename, DOMXPath $xpath, string $query): FileCollection
{
$files = [];

foreach ($xpath->query($query) as $file) {
assert($file instanceof DOMNode);

$filePath = (string) $file->textContent;

if ($filePath) {
$files[] = new File($this->toAbsolutePath($filename, $filePath));
}
}

return FileCollection::fromArray($files);
}

private function groups(DOMXPath $xpath): Groups
{
return $this->parseGroupConfiguration($xpath, 'groups');
}

private function testdoxGroups(DOMXPath $xpath): Groups
{
return $this->parseGroupConfiguration($xpath, 'testdoxGroups');
}

private function parseGroupConfiguration(DOMXPath $xpath, string $root): Groups
{
$include = [];
$exclude = [];

foreach ($xpath->query($root . '/include/group') as $group) {
assert($group instanceof DOMNode);

$include[] = new Group((string) $group->textContent);
}

foreach ($xpath->query($root . '/exclude/group') as $group) {
assert($group instanceof DOMNode);

$exclude[] = new Group((string) $group->textContent);
}

return new Groups(
GroupCollection::fromArray($include),
GroupCollection::fromArray($exclude),
);
}

private function listeners(string $filename, DOMXPath $xpath): ExtensionCollection
{
$listeners = [];

foreach ($xpath->query('listeners/listener') as $listener) {
assert($listener instanceof DOMElement);

$listeners[] = $this->getElementConfigurationParameters($filename, $listener);
}

return ExtensionCollection::fromArray($listeners);
}

private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default): bool
{
if (!$element->hasAttribute($attribute)) {
return $default;
}

return (bool) $this->getBoolean(
(string) $element->getAttribute($attribute),
false,
);
}

private function getIntegerAttribute(DOMElement $element, string $attribute, int $default): int
{
if (!$element->hasAttribute($attribute)) {
return $default;
}

return $this->getInteger(
(string) $element->getAttribute($attribute),
$default,
);
}

private function getStringAttribute(DOMElement $element, string $attribute): ?string
{
if (!$element->hasAttribute($attribute)) {
return null;
}

return (string) $element->getAttribute($attribute);
}

private function getInteger(string $value, int $default): int
{
if (is_numeric($value)) {
return (int) $value;
}

return $default;
}

private function php(string $filename, DOMXPath $xpath): Php
{
$includePaths = [];

foreach ($xpath->query('php/includePath') as $includePath) {
assert($includePath instanceof DOMNode);

$path = (string) $includePath->textContent;

if ($path) {
$includePaths[] = new Directory($this->toAbsolutePath($filename, $path));
}
}

$iniSettings = [];

foreach ($xpath->query('php/ini') as $ini) {
assert($ini instanceof DOMElement);

$iniSettings[] = new IniSetting(
(string) $ini->getAttribute('name'),
(string) $ini->getAttribute('value'),
);
}

$constants = [];

foreach ($xpath->query('php/const') as $const) {
assert($const instanceof DOMElement);

$value = (string) $const->getAttribute('value');

$constants[] = new Constant(
(string) $const->getAttribute('name'),
$this->getBoolean($value, $value),
);
}

$variables = [
'var' => [],
'env' => [],
'post' => [],
'get' => [],
'cookie' => [],
'server' => [],
'files' => [],
'request' => [],
];

foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
foreach ($xpath->query('php/' . $array) as $var) {
assert($var instanceof DOMElement);

$name = (string) $var->getAttribute('name');
$value = (string) $var->getAttribute('value');
$force = false;
$verbatim = false;

if ($var->hasAttribute('force')) {
$force = (bool) $this->getBoolean($var->getAttribute('force'), false);
}

if ($var->hasAttribute('verbatim')) {
$verbatim = $this->getBoolean($var->getAttribute('verbatim'), false);
}

if (!$verbatim) {
$value = $this->getBoolean($value, $value);
}

$variables[$array][] = new Variable($name, $value, $force);
}
}

return new Php(
DirectoryCollection::fromArray($includePaths),
IniSettingCollection::fromArray($iniSettings),
ConstantCollection::fromArray($constants),
VariableCollection::fromArray($variables['var']),
VariableCollection::fromArray($variables['env']),
VariableCollection::fromArray($variables['post']),
VariableCollection::fromArray($variables['get']),
VariableCollection::fromArray($variables['cookie']),
VariableCollection::fromArray($variables['server']),
VariableCollection::fromArray($variables['files']),
VariableCollection::fromArray($variables['request']),
);
}

private function phpunit(string $filename, DOMDocument $document): PHPUnit
{
$executionOrder = TestSuiteSorter::ORDER_DEFAULT;
$defectsFirst = false;
$resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', true);

if ($document->documentElement->hasAttribute('executionOrder')) {
foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) {
switch ($order) {
case 'default':
$executionOrder = TestSuiteSorter::ORDER_DEFAULT;
$defectsFirst = false;
$resolveDependencies = true;

break;

case 'depends':
$resolveDependencies = true;

break;

case 'no-depends':
$resolveDependencies = false;

break;

case 'defects':
$defectsFirst = true;

break;

case 'duration':
$executionOrder = TestSuiteSorter::ORDER_DURATION;

break;

case 'random':
$executionOrder = TestSuiteSorter::ORDER_RANDOMIZED;

break;

case 'reverse':
$executionOrder = TestSuiteSorter::ORDER_REVERSED;

break;

case 'size':
$executionOrder = TestSuiteSorter::ORDER_SIZE;

break;
}
}
}

$printerClass = $this->getStringAttribute($document->documentElement, 'printerClass');
$testdox = $this->getBooleanAttribute($document->documentElement, 'testdox', false);
$conflictBetweenPrinterClassAndTestdox = false;

if ($testdox) {
if ($printerClass !== null) {
$conflictBetweenPrinterClassAndTestdox = true;
}

$printerClass = CliTestDoxPrinter::class;
}

$cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile');

if ($cacheResultFile !== null) {
$cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile);
}

$bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap');

if ($bootstrap !== null) {
$bootstrap = $this->toAbsolutePath($filename, $bootstrap);
}

$extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory');

if ($extensionsDirectory !== null) {
$extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory);
}

$testSuiteLoaderFile = $this->getStringAttribute($document->documentElement, 'testSuiteLoaderFile');

if ($testSuiteLoaderFile !== null) {
$testSuiteLoaderFile = $this->toAbsolutePath($filename, $testSuiteLoaderFile);
}

$printerFile = $this->getStringAttribute($document->documentElement, 'printerFile');

if ($printerFile !== null) {
$printerFile = $this->toAbsolutePath($filename, $printerFile);
}

return new PHPUnit(
$this->getBooleanAttribute($document->documentElement, 'cacheResult', true),
$cacheResultFile,
$this->getColumns($document),
$this->getColors($document),
$this->getBooleanAttribute($document->documentElement, 'stderr', false),
$this->getBooleanAttribute($document->documentElement, 'noInteraction', false),
$this->getBooleanAttribute($document->documentElement, 'verbose', false),
$this->getBooleanAttribute($document->documentElement, 'reverseDefectList', false),
$this->getBooleanAttribute($document->documentElement, 'convertDeprecationsToExceptions', false),
$this->getBooleanAttribute($document->documentElement, 'convertErrorsToExceptions', true),
$this->getBooleanAttribute($document->documentElement, 'convertNoticesToExceptions', true),
$this->getBooleanAttribute($document->documentElement, 'convertWarningsToExceptions', true),
$this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', false),
$bootstrap,
$this->getBooleanAttribute($document->documentElement, 'processIsolation', false),
$this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', false),
$this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', false),
$this->getBooleanAttribute($document->documentElement, 'failOnRisky', false),
$this->getBooleanAttribute($document->documentElement, 'failOnSkipped', false),
$this->getBooleanAttribute($document->documentElement, 'failOnWarning', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnDefect', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnError', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnFailure', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnWarning', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnRisky', false),
$this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', false),
$extensionsDirectory,
$this->getStringAttribute($document->documentElement, 'testSuiteLoaderClass'),
$testSuiteLoaderFile,
$printerClass,
$printerFile,
$this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', false),
$this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', false),
$this->getBooleanAttribute($document->documentElement, 'beStrictAboutResourceUsageDuringSmallTests', false),
$this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', true),
$this->getBooleanAttribute($document->documentElement, 'beStrictAboutTodoAnnotatedTests', false),
$this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', false),
$this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', false),
$this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1),
$this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1),
$this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10),
$this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60),
$this->getStringAttribute($document->documentElement, 'defaultTestSuite'),
$executionOrder,
$resolveDependencies,
$defectsFirst,
$this->getBooleanAttribute($document->documentElement, 'backupGlobals', false),
$this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', false),
$this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', false),
$conflictBetweenPrinterClassAndTestdox,
);
}

private function getColors(DOMDocument $document): string
{
$colors = DefaultResultPrinter::COLOR_DEFAULT;

if ($document->documentElement->hasAttribute('colors')) {


if ($this->getBoolean($document->documentElement->getAttribute('colors'), false)) {
$colors = DefaultResultPrinter::COLOR_AUTO;
} else {
$colors = DefaultResultPrinter::COLOR_NEVER;
}
}

return $colors;
}




private function getColumns(DOMDocument $document)
{
$columns = 80;

if ($document->documentElement->hasAttribute('columns')) {
$columns = (string) $document->documentElement->getAttribute('columns');

if ($columns !== 'max') {
$columns = $this->getInteger($columns, 80);
}
}

return $columns;
}

private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollection
{
$testSuites = [];

foreach ($this->getTestSuiteElements($xpath) as $element) {
$exclude = [];

foreach ($element->getElementsByTagName('exclude') as $excludeNode) {
$excludeFile = (string) $excludeNode->textContent;

if ($excludeFile) {
$exclude[] = new File($this->toAbsolutePath($filename, $excludeFile));
}
}

$directories = [];

foreach ($element->getElementsByTagName('directory') as $directoryNode) {
assert($directoryNode instanceof DOMElement);

$directory = (string) $directoryNode->textContent;

if (empty($directory)) {
continue;
}

$prefix = '';

if ($directoryNode->hasAttribute('prefix')) {
$prefix = (string) $directoryNode->getAttribute('prefix');
}

$suffix = 'Test.php';

if ($directoryNode->hasAttribute('suffix')) {
$suffix = (string) $directoryNode->getAttribute('suffix');
}

$phpVersion = PHP_VERSION;

if ($directoryNode->hasAttribute('phpVersion')) {
$phpVersion = (string) $directoryNode->getAttribute('phpVersion');
}

$phpVersionOperator = new VersionComparisonOperator('>=');

if ($directoryNode->hasAttribute('phpVersionOperator')) {
$phpVersionOperator = new VersionComparisonOperator((string) $directoryNode->getAttribute('phpVersionOperator'));
}

$directories[] = new TestDirectory(
$this->toAbsolutePath($filename, $directory),
$prefix,
$suffix,
$phpVersion,
$phpVersionOperator,
);
}

$files = [];

foreach ($element->getElementsByTagName('file') as $fileNode) {
assert($fileNode instanceof DOMElement);

$file = (string) $fileNode->textContent;

if (empty($file)) {
continue;
}

$phpVersion = PHP_VERSION;

if ($fileNode->hasAttribute('phpVersion')) {
$phpVersion = (string) $fileNode->getAttribute('phpVersion');
}

$phpVersionOperator = new VersionComparisonOperator('>=');

if ($fileNode->hasAttribute('phpVersionOperator')) {
$phpVersionOperator = new VersionComparisonOperator((string) $fileNode->getAttribute('phpVersionOperator'));
}

$files[] = new TestFile(
$this->toAbsolutePath($filename, $file),
$phpVersion,
$phpVersionOperator,
);
}

$testSuites[] = new TestSuiteConfiguration(
(string) $element->getAttribute('name'),
TestDirectoryCollection::fromArray($directories),
TestFileCollection::fromArray($files),
FileCollection::fromArray($exclude),
);
}

return TestSuiteCollection::fromArray($testSuites);
}




private function getTestSuiteElements(DOMXPath $xpath): array
{

$elements = [];

$testSuiteNodes = $xpath->query('testsuites/testsuite');

if ($testSuiteNodes->length === 0) {
$testSuiteNodes = $xpath->query('testsuite');
}

if ($testSuiteNodes->length === 1) {
$element = $testSuiteNodes->item(0);

assert($element instanceof DOMElement);

$elements[] = $element;
} else {
foreach ($testSuiteNodes as $testSuiteNode) {
assert($testSuiteNode instanceof DOMElement);

$elements[] = $testSuiteNode;
}
}

return $elements;
}

private function element(DOMXPath $xpath, string $element): ?DOMElement
{
$nodes = $xpath->query($element);

if ($nodes->length === 1) {
$node = $nodes->item(0);

assert($node instanceof DOMElement);

return $node;
}

return null;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestResult;




interface ResultPrinter extends TestListener
{
public function printResult(TestResult $result): void;

public function write(string $buffer): void;
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\CliArguments;

use RuntimeException;




final class Exception extends RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\CliArguments;

use PHPUnit\TextUI\XmlConfiguration\Extension;






final class Configuration
{



private $argument;




private $atLeastVersion;




private $backupGlobals;




private $backupStaticAttributes;




private $beStrictAboutChangesToGlobalState;




private $beStrictAboutResourceUsageDuringSmallTests;




private $bootstrap;




private $cacheResult;




private $cacheResultFile;




private $checkVersion;




private $colors;




private $columns;




private $configuration;




private $coverageFilter;




private $coverageClover;




private $coverageCobertura;




private $coverageCrap4J;




private $coverageHtml;




private $coveragePhp;




private $coverageText;




private $coverageTextShowUncoveredFiles;




private $coverageTextShowOnlySummary;




private $coverageXml;




private $pathCoverage;




private $coverageCacheDirectory;




private $warmCoverageCache;




private $debug;




private $defaultTimeLimit;




private $disableCodeCoverageIgnore;




private $disallowTestOutput;




private $disallowTodoAnnotatedTests;




private $enforceTimeLimit;




private $excludeGroups;




private $executionOrder;




private $executionOrderDefects;




private $extensions;




private $unavailableExtensions;




private $failOnEmptyTestSuite;




private $failOnIncomplete;




private $failOnRisky;




private $failOnSkipped;




private $failOnWarning;




private $filter;




private $generateConfiguration;




private $migrateConfiguration;




private $groups;




private $testsCovering;




private $testsUsing;




private $help;




private $includePath;




private $iniSettings;




private $junitLogfile;




private $listGroups;




private $listSuites;




private $listTests;




private $listTestsXml;




private $loader;




private $noCoverage;




private $noExtensions;




private $noInteraction;




private $noLogging;




private $printer;




private $processIsolation;




private $randomOrderSeed;




private $repeat;




private $reportUselessTests;




private $resolveDependencies;




private $reverseList;




private $stderr;




private $strictCoverage;




private $stopOnDefect;




private $stopOnError;




private $stopOnFailure;




private $stopOnIncomplete;




private $stopOnRisky;




private $stopOnSkipped;




private $stopOnWarning;




private $teamcityLogfile;




private $testdoxExcludeGroups;




private $testdoxGroups;




private $testdoxHtmlFile;




private $testdoxTextFile;




private $testdoxXmlFile;




private $testSuffixes;




private $testSuite;




private $unrecognizedOptions;




private $unrecognizedOrderBy;




private $useDefaultConfiguration;




private $verbose;




private $version;




private $xdebugFilterFile;




public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile)
{
$this->argument = $argument;
$this->atLeastVersion = $atLeastVersion;
$this->backupGlobals = $backupGlobals;
$this->backupStaticAttributes = $backupStaticAttributes;
$this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
$this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests;
$this->bootstrap = $bootstrap;
$this->cacheResult = $cacheResult;
$this->cacheResultFile = $cacheResultFile;
$this->checkVersion = $checkVersion;
$this->colors = $colors;
$this->columns = $columns;
$this->configuration = $configuration;
$this->coverageFilter = $coverageFilter;
$this->coverageClover = $coverageClover;
$this->coverageCobertura = $coverageCobertura;
$this->coverageCrap4J = $coverageCrap4J;
$this->coverageHtml = $coverageHtml;
$this->coveragePhp = $coveragePhp;
$this->coverageText = $coverageText;
$this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles;
$this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary;
$this->coverageXml = $coverageXml;
$this->pathCoverage = $pathCoverage;
$this->coverageCacheDirectory = $coverageCacheDirectory;
$this->warmCoverageCache = $warmCoverageCache;
$this->debug = $debug;
$this->defaultTimeLimit = $defaultTimeLimit;
$this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore;
$this->disallowTestOutput = $disallowTestOutput;
$this->disallowTodoAnnotatedTests = $disallowTodoAnnotatedTests;
$this->enforceTimeLimit = $enforceTimeLimit;
$this->excludeGroups = $excludeGroups;
$this->executionOrder = $executionOrder;
$this->executionOrderDefects = $executionOrderDefects;
$this->extensions = $extensions;
$this->unavailableExtensions = $unavailableExtensions;
$this->failOnEmptyTestSuite = $failOnEmptyTestSuite;
$this->failOnIncomplete = $failOnIncomplete;
$this->failOnRisky = $failOnRisky;
$this->failOnSkipped = $failOnSkipped;
$this->failOnWarning = $failOnWarning;
$this->filter = $filter;
$this->generateConfiguration = $generateConfiguration;
$this->migrateConfiguration = $migrateConfiguration;
$this->groups = $groups;
$this->testsCovering = $testsCovering;
$this->testsUsing = $testsUsing;
$this->help = $help;
$this->includePath = $includePath;
$this->iniSettings = $iniSettings;
$this->junitLogfile = $junitLogfile;
$this->listGroups = $listGroups;
$this->listSuites = $listSuites;
$this->listTests = $listTests;
$this->listTestsXml = $listTestsXml;
$this->loader = $loader;
$this->noCoverage = $noCoverage;
$this->noExtensions = $noExtensions;
$this->noInteraction = $noInteraction;
$this->noLogging = $noLogging;
$this->printer = $printer;
$this->processIsolation = $processIsolation;
$this->randomOrderSeed = $randomOrderSeed;
$this->repeat = $repeat;
$this->reportUselessTests = $reportUselessTests;
$this->resolveDependencies = $resolveDependencies;
$this->reverseList = $reverseList;
$this->stderr = $stderr;
$this->strictCoverage = $strictCoverage;
$this->stopOnDefect = $stopOnDefect;
$this->stopOnError = $stopOnError;
$this->stopOnFailure = $stopOnFailure;
$this->stopOnIncomplete = $stopOnIncomplete;
$this->stopOnRisky = $stopOnRisky;
$this->stopOnSkipped = $stopOnSkipped;
$this->stopOnWarning = $stopOnWarning;
$this->teamcityLogfile = $teamcityLogfile;
$this->testdoxExcludeGroups = $testdoxExcludeGroups;
$this->testdoxGroups = $testdoxGroups;
$this->testdoxHtmlFile = $testdoxHtmlFile;
$this->testdoxTextFile = $testdoxTextFile;
$this->testdoxXmlFile = $testdoxXmlFile;
$this->testSuffixes = $testSuffixes;
$this->testSuite = $testSuite;
$this->unrecognizedOptions = $unrecognizedOptions;
$this->unrecognizedOrderBy = $unrecognizedOrderBy;
$this->useDefaultConfiguration = $useDefaultConfiguration;
$this->verbose = $verbose;
$this->version = $version;
$this->xdebugFilterFile = $xdebugFilterFile;
}

public function hasArgument(): bool
{
return $this->argument !== null;
}




public function argument(): string
{
if ($this->argument === null) {
throw new Exception;
}

return $this->argument;
}

public function hasAtLeastVersion(): bool
{
return $this->atLeastVersion !== null;
}




public function atLeastVersion(): string
{
if ($this->atLeastVersion === null) {
throw new Exception;
}

return $this->atLeastVersion;
}

public function hasBackupGlobals(): bool
{
return $this->backupGlobals !== null;
}




public function backupGlobals(): bool
{
if ($this->backupGlobals === null) {
throw new Exception;
}

return $this->backupGlobals;
}

public function hasBackupStaticAttributes(): bool
{
return $this->backupStaticAttributes !== null;
}




public function backupStaticAttributes(): bool
{
if ($this->backupStaticAttributes === null) {
throw new Exception;
}

return $this->backupStaticAttributes;
}

public function hasBeStrictAboutChangesToGlobalState(): bool
{
return $this->beStrictAboutChangesToGlobalState !== null;
}




public function beStrictAboutChangesToGlobalState(): bool
{
if ($this->beStrictAboutChangesToGlobalState === null) {
throw new Exception;
}

return $this->beStrictAboutChangesToGlobalState;
}

public function hasBeStrictAboutResourceUsageDuringSmallTests(): bool
{
return $this->beStrictAboutResourceUsageDuringSmallTests !== null;
}




public function beStrictAboutResourceUsageDuringSmallTests(): bool
{
if ($this->beStrictAboutResourceUsageDuringSmallTests === null) {
throw new Exception;
}

return $this->beStrictAboutResourceUsageDuringSmallTests;
}

public function hasBootstrap(): bool
{
return $this->bootstrap !== null;
}




public function bootstrap(): string
{
if ($this->bootstrap === null) {
throw new Exception;
}

return $this->bootstrap;
}

public function hasCacheResult(): bool
{
return $this->cacheResult !== null;
}




public function cacheResult(): bool
{
if ($this->cacheResult === null) {
throw new Exception;
}

return $this->cacheResult;
}

public function hasCacheResultFile(): bool
{
return $this->cacheResultFile !== null;
}




public function cacheResultFile(): string
{
if ($this->cacheResultFile === null) {
throw new Exception;
}

return $this->cacheResultFile;
}

public function hasCheckVersion(): bool
{
return $this->checkVersion !== null;
}




public function checkVersion(): bool
{
if ($this->checkVersion === null) {
throw new Exception;
}

return $this->checkVersion;
}

public function hasColors(): bool
{
return $this->colors !== null;
}




public function colors(): string
{
if ($this->colors === null) {
throw new Exception;
}

return $this->colors;
}

public function hasColumns(): bool
{
return $this->columns !== null;
}




public function columns()
{
if ($this->columns === null) {
throw new Exception;
}

return $this->columns;
}

public function hasConfiguration(): bool
{
return $this->configuration !== null;
}




public function configuration(): string
{
if ($this->configuration === null) {
throw new Exception;
}

return $this->configuration;
}

public function hasCoverageFilter(): bool
{
return $this->coverageFilter !== null;
}




public function coverageFilter(): array
{
if ($this->coverageFilter === null) {
throw new Exception;
}

return $this->coverageFilter;
}

public function hasCoverageClover(): bool
{
return $this->coverageClover !== null;
}




public function coverageClover(): string
{
if ($this->coverageClover === null) {
throw new Exception;
}

return $this->coverageClover;
}

public function hasCoverageCobertura(): bool
{
return $this->coverageCobertura !== null;
}




public function coverageCobertura(): string
{
if ($this->coverageCobertura === null) {
throw new Exception;
}

return $this->coverageCobertura;
}

public function hasCoverageCrap4J(): bool
{
return $this->coverageCrap4J !== null;
}




public function coverageCrap4J(): string
{
if ($this->coverageCrap4J === null) {
throw new Exception;
}

return $this->coverageCrap4J;
}

public function hasCoverageHtml(): bool
{
return $this->coverageHtml !== null;
}




public function coverageHtml(): string
{
if ($this->coverageHtml === null) {
throw new Exception;
}

return $this->coverageHtml;
}

public function hasCoveragePhp(): bool
{
return $this->coveragePhp !== null;
}




public function coveragePhp(): string
{
if ($this->coveragePhp === null) {
throw new Exception;
}

return $this->coveragePhp;
}

public function hasCoverageText(): bool
{
return $this->coverageText !== null;
}




public function coverageText(): string
{
if ($this->coverageText === null) {
throw new Exception;
}

return $this->coverageText;
}

public function hasCoverageTextShowUncoveredFiles(): bool
{
return $this->coverageTextShowUncoveredFiles !== null;
}




public function coverageTextShowUncoveredFiles(): bool
{
if ($this->coverageTextShowUncoveredFiles === null) {
throw new Exception;
}

return $this->coverageTextShowUncoveredFiles;
}

public function hasCoverageTextShowOnlySummary(): bool
{
return $this->coverageTextShowOnlySummary !== null;
}




public function coverageTextShowOnlySummary(): bool
{
if ($this->coverageTextShowOnlySummary === null) {
throw new Exception;
}

return $this->coverageTextShowOnlySummary;
}

public function hasCoverageXml(): bool
{
return $this->coverageXml !== null;
}




public function coverageXml(): string
{
if ($this->coverageXml === null) {
throw new Exception;
}

return $this->coverageXml;
}

public function hasPathCoverage(): bool
{
return $this->pathCoverage !== null;
}




public function pathCoverage(): bool
{
if ($this->pathCoverage === null) {
throw new Exception;
}

return $this->pathCoverage;
}

public function hasCoverageCacheDirectory(): bool
{
return $this->coverageCacheDirectory !== null;
}




public function coverageCacheDirectory(): string
{
if ($this->coverageCacheDirectory === null) {
throw new Exception;
}

return $this->coverageCacheDirectory;
}

public function hasWarmCoverageCache(): bool
{
return $this->warmCoverageCache !== null;
}




public function warmCoverageCache(): bool
{
if ($this->warmCoverageCache === null) {
throw new Exception;
}

return $this->warmCoverageCache;
}

public function hasDebug(): bool
{
return $this->debug !== null;
}




public function debug(): bool
{
if ($this->debug === null) {
throw new Exception;
}

return $this->debug;
}

public function hasDefaultTimeLimit(): bool
{
return $this->defaultTimeLimit !== null;
}




public function defaultTimeLimit(): int
{
if ($this->defaultTimeLimit === null) {
throw new Exception;
}

return $this->defaultTimeLimit;
}

public function hasDisableCodeCoverageIgnore(): bool
{
return $this->disableCodeCoverageIgnore !== null;
}




public function disableCodeCoverageIgnore(): bool
{
if ($this->disableCodeCoverageIgnore === null) {
throw new Exception;
}

return $this->disableCodeCoverageIgnore;
}

public function hasDisallowTestOutput(): bool
{
return $this->disallowTestOutput !== null;
}




public function disallowTestOutput(): bool
{
if ($this->disallowTestOutput === null) {
throw new Exception;
}

return $this->disallowTestOutput;
}

public function hasDisallowTodoAnnotatedTests(): bool
{
return $this->disallowTodoAnnotatedTests !== null;
}




public function disallowTodoAnnotatedTests(): bool
{
if ($this->disallowTodoAnnotatedTests === null) {
throw new Exception;
}

return $this->disallowTodoAnnotatedTests;
}

public function hasEnforceTimeLimit(): bool
{
return $this->enforceTimeLimit !== null;
}




public function enforceTimeLimit(): bool
{
if ($this->enforceTimeLimit === null) {
throw new Exception;
}

return $this->enforceTimeLimit;
}

public function hasExcludeGroups(): bool
{
return $this->excludeGroups !== null;
}




public function excludeGroups(): array
{
if ($this->excludeGroups === null) {
throw new Exception;
}

return $this->excludeGroups;
}

public function hasExecutionOrder(): bool
{
return $this->executionOrder !== null;
}




public function executionOrder(): int
{
if ($this->executionOrder === null) {
throw new Exception;
}

return $this->executionOrder;
}

public function hasExecutionOrderDefects(): bool
{
return $this->executionOrderDefects !== null;
}




public function executionOrderDefects(): int
{
if ($this->executionOrderDefects === null) {
throw new Exception;
}

return $this->executionOrderDefects;
}

public function hasFailOnEmptyTestSuite(): bool
{
return $this->failOnEmptyTestSuite !== null;
}




public function failOnEmptyTestSuite(): bool
{
if ($this->failOnEmptyTestSuite === null) {
throw new Exception;
}

return $this->failOnEmptyTestSuite;
}

public function hasFailOnIncomplete(): bool
{
return $this->failOnIncomplete !== null;
}




public function failOnIncomplete(): bool
{
if ($this->failOnIncomplete === null) {
throw new Exception;
}

return $this->failOnIncomplete;
}

public function hasFailOnRisky(): bool
{
return $this->failOnRisky !== null;
}




public function failOnRisky(): bool
{
if ($this->failOnRisky === null) {
throw new Exception;
}

return $this->failOnRisky;
}

public function hasFailOnSkipped(): bool
{
return $this->failOnSkipped !== null;
}




public function failOnSkipped(): bool
{
if ($this->failOnSkipped === null) {
throw new Exception;
}

return $this->failOnSkipped;
}

public function hasFailOnWarning(): bool
{
return $this->failOnWarning !== null;
}




public function failOnWarning(): bool
{
if ($this->failOnWarning === null) {
throw new Exception;
}

return $this->failOnWarning;
}

public function hasFilter(): bool
{
return $this->filter !== null;
}




public function filter(): string
{
if ($this->filter === null) {
throw new Exception;
}

return $this->filter;
}

public function hasGenerateConfiguration(): bool
{
return $this->generateConfiguration !== null;
}




public function generateConfiguration(): bool
{
if ($this->generateConfiguration === null) {
throw new Exception;
}

return $this->generateConfiguration;
}

public function hasMigrateConfiguration(): bool
{
return $this->migrateConfiguration !== null;
}




public function migrateConfiguration(): bool
{
if ($this->migrateConfiguration === null) {
throw new Exception;
}

return $this->migrateConfiguration;
}

public function hasGroups(): bool
{
return $this->groups !== null;
}




public function groups(): array
{
if ($this->groups === null) {
throw new Exception;
}

return $this->groups;
}

public function hasTestsCovering(): bool
{
return $this->testsCovering !== null;
}




public function testsCovering(): array
{
if ($this->testsCovering === null) {
throw new Exception;
}

return $this->testsCovering;
}

public function hasTestsUsing(): bool
{
return $this->testsUsing !== null;
}




public function testsUsing(): array
{
if ($this->testsUsing === null) {
throw new Exception;
}

return $this->testsUsing;
}

public function hasHelp(): bool
{
return $this->help !== null;
}




public function help(): bool
{
if ($this->help === null) {
throw new Exception;
}

return $this->help;
}

public function hasIncludePath(): bool
{
return $this->includePath !== null;
}




public function includePath(): string
{
if ($this->includePath === null) {
throw new Exception;
}

return $this->includePath;
}

public function hasIniSettings(): bool
{
return $this->iniSettings !== null;
}




public function iniSettings(): array
{
if ($this->iniSettings === null) {
throw new Exception;
}

return $this->iniSettings;
}

public function hasJunitLogfile(): bool
{
return $this->junitLogfile !== null;
}




public function junitLogfile(): string
{
if ($this->junitLogfile === null) {
throw new Exception;
}

return $this->junitLogfile;
}

public function hasListGroups(): bool
{
return $this->listGroups !== null;
}




public function listGroups(): bool
{
if ($this->listGroups === null) {
throw new Exception;
}

return $this->listGroups;
}

public function hasListSuites(): bool
{
return $this->listSuites !== null;
}




public function listSuites(): bool
{
if ($this->listSuites === null) {
throw new Exception;
}

return $this->listSuites;
}

public function hasListTests(): bool
{
return $this->listTests !== null;
}




public function listTests(): bool
{
if ($this->listTests === null) {
throw new Exception;
}

return $this->listTests;
}

public function hasListTestsXml(): bool
{
return $this->listTestsXml !== null;
}




public function listTestsXml(): string
{
if ($this->listTestsXml === null) {
throw new Exception;
}

return $this->listTestsXml;
}

public function hasLoader(): bool
{
return $this->loader !== null;
}




public function loader(): string
{
if ($this->loader === null) {
throw new Exception;
}

return $this->loader;
}

public function hasNoCoverage(): bool
{
return $this->noCoverage !== null;
}




public function noCoverage(): bool
{
if ($this->noCoverage === null) {
throw new Exception;
}

return $this->noCoverage;
}

public function hasNoExtensions(): bool
{
return $this->noExtensions !== null;
}




public function noExtensions(): bool
{
if ($this->noExtensions === null) {
throw new Exception;
}

return $this->noExtensions;
}

public function hasExtensions(): bool
{
return $this->extensions !== null;
}




public function extensions(): array
{
if ($this->extensions === null) {
throw new Exception;
}

return $this->extensions;
}

public function hasUnavailableExtensions(): bool
{
return $this->unavailableExtensions !== null;
}




public function unavailableExtensions(): array
{
if ($this->unavailableExtensions === null) {
throw new Exception;
}

return $this->unavailableExtensions;
}

public function hasNoInteraction(): bool
{
return $this->noInteraction !== null;
}




public function noInteraction(): bool
{
if ($this->noInteraction === null) {
throw new Exception;
}

return $this->noInteraction;
}

public function hasNoLogging(): bool
{
return $this->noLogging !== null;
}




public function noLogging(): bool
{
if ($this->noLogging === null) {
throw new Exception;
}

return $this->noLogging;
}

public function hasPrinter(): bool
{
return $this->printer !== null;
}




public function printer(): string
{
if ($this->printer === null) {
throw new Exception;
}

return $this->printer;
}

public function hasProcessIsolation(): bool
{
return $this->processIsolation !== null;
}




public function processIsolation(): bool
{
if ($this->processIsolation === null) {
throw new Exception;
}

return $this->processIsolation;
}

public function hasRandomOrderSeed(): bool
{
return $this->randomOrderSeed !== null;
}




public function randomOrderSeed(): int
{
if ($this->randomOrderSeed === null) {
throw new Exception;
}

return $this->randomOrderSeed;
}

public function hasRepeat(): bool
{
return $this->repeat !== null;
}




public function repeat(): int
{
if ($this->repeat === null) {
throw new Exception;
}

return $this->repeat;
}

public function hasReportUselessTests(): bool
{
return $this->reportUselessTests !== null;
}




public function reportUselessTests(): bool
{
if ($this->reportUselessTests === null) {
throw new Exception;
}

return $this->reportUselessTests;
}

public function hasResolveDependencies(): bool
{
return $this->resolveDependencies !== null;
}




public function resolveDependencies(): bool
{
if ($this->resolveDependencies === null) {
throw new Exception;
}

return $this->resolveDependencies;
}

public function hasReverseList(): bool
{
return $this->reverseList !== null;
}




public function reverseList(): bool
{
if ($this->reverseList === null) {
throw new Exception;
}

return $this->reverseList;
}

public function hasStderr(): bool
{
return $this->stderr !== null;
}




public function stderr(): bool
{
if ($this->stderr === null) {
throw new Exception;
}

return $this->stderr;
}

public function hasStrictCoverage(): bool
{
return $this->strictCoverage !== null;
}




public function strictCoverage(): bool
{
if ($this->strictCoverage === null) {
throw new Exception;
}

return $this->strictCoverage;
}

public function hasStopOnDefect(): bool
{
return $this->stopOnDefect !== null;
}




public function stopOnDefect(): bool
{
if ($this->stopOnDefect === null) {
throw new Exception;
}

return $this->stopOnDefect;
}

public function hasStopOnError(): bool
{
return $this->stopOnError !== null;
}




public function stopOnError(): bool
{
if ($this->stopOnError === null) {
throw new Exception;
}

return $this->stopOnError;
}

public function hasStopOnFailure(): bool
{
return $this->stopOnFailure !== null;
}




public function stopOnFailure(): bool
{
if ($this->stopOnFailure === null) {
throw new Exception;
}

return $this->stopOnFailure;
}

public function hasStopOnIncomplete(): bool
{
return $this->stopOnIncomplete !== null;
}




public function stopOnIncomplete(): bool
{
if ($this->stopOnIncomplete === null) {
throw new Exception;
}

return $this->stopOnIncomplete;
}

public function hasStopOnRisky(): bool
{
return $this->stopOnRisky !== null;
}




public function stopOnRisky(): bool
{
if ($this->stopOnRisky === null) {
throw new Exception;
}

return $this->stopOnRisky;
}

public function hasStopOnSkipped(): bool
{
return $this->stopOnSkipped !== null;
}




public function stopOnSkipped(): bool
{
if ($this->stopOnSkipped === null) {
throw new Exception;
}

return $this->stopOnSkipped;
}

public function hasStopOnWarning(): bool
{
return $this->stopOnWarning !== null;
}




public function stopOnWarning(): bool
{
if ($this->stopOnWarning === null) {
throw new Exception;
}

return $this->stopOnWarning;
}

public function hasTeamcityLogfile(): bool
{
return $this->teamcityLogfile !== null;
}




public function teamcityLogfile(): string
{
if ($this->teamcityLogfile === null) {
throw new Exception;
}

return $this->teamcityLogfile;
}

public function hasTestdoxExcludeGroups(): bool
{
return $this->testdoxExcludeGroups !== null;
}




public function testdoxExcludeGroups(): array
{
if ($this->testdoxExcludeGroups === null) {
throw new Exception;
}

return $this->testdoxExcludeGroups;
}

public function hasTestdoxGroups(): bool
{
return $this->testdoxGroups !== null;
}




public function testdoxGroups(): array
{
if ($this->testdoxGroups === null) {
throw new Exception;
}

return $this->testdoxGroups;
}

public function hasTestdoxHtmlFile(): bool
{
return $this->testdoxHtmlFile !== null;
}




public function testdoxHtmlFile(): string
{
if ($this->testdoxHtmlFile === null) {
throw new Exception;
}

return $this->testdoxHtmlFile;
}

public function hasTestdoxTextFile(): bool
{
return $this->testdoxTextFile !== null;
}




public function testdoxTextFile(): string
{
if ($this->testdoxTextFile === null) {
throw new Exception;
}

return $this->testdoxTextFile;
}

public function hasTestdoxXmlFile(): bool
{
return $this->testdoxXmlFile !== null;
}




public function testdoxXmlFile(): string
{
if ($this->testdoxXmlFile === null) {
throw new Exception;
}

return $this->testdoxXmlFile;
}

public function hasTestSuffixes(): bool
{
return $this->testSuffixes !== null;
}




public function testSuffixes(): array
{
if ($this->testSuffixes === null) {
throw new Exception;
}

return $this->testSuffixes;
}

public function hasTestSuite(): bool
{
return $this->testSuite !== null;
}




public function testSuite(): string
{
if ($this->testSuite === null) {
throw new Exception;
}

return $this->testSuite;
}

public function unrecognizedOptions(): array
{
return $this->unrecognizedOptions;
}

public function hasUnrecognizedOrderBy(): bool
{
return $this->unrecognizedOrderBy !== null;
}




public function unrecognizedOrderBy(): string
{
if ($this->unrecognizedOrderBy === null) {
throw new Exception;
}

return $this->unrecognizedOrderBy;
}

public function hasUseDefaultConfiguration(): bool
{
return $this->useDefaultConfiguration !== null;
}




public function useDefaultConfiguration(): bool
{
if ($this->useDefaultConfiguration === null) {
throw new Exception;
}

return $this->useDefaultConfiguration;
}

public function hasVerbose(): bool
{
return $this->verbose !== null;
}




public function verbose(): bool
{
if ($this->verbose === null) {
throw new Exception;
}

return $this->verbose;
}

public function hasVersion(): bool
{
return $this->version !== null;
}




public function version(): bool
{
if ($this->version === null) {
throw new Exception;
}

return $this->version;
}

public function hasXdebugFilterFile(): bool
{
return $this->xdebugFilterFile !== null;
}




public function xdebugFilterFile(): string
{
if ($this->xdebugFilterFile === null) {
throw new Exception;
}

return $this->xdebugFilterFile;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\CliArguments;




final class Mapper
{



public function mapToLegacyArray(Configuration $arguments): array
{
$result = [
'extensions' => [],
'listGroups' => false,
'listSuites' => false,
'listTests' => false,
'listTestsXml' => false,
'loader' => null,
'useDefaultConfiguration' => true,
'loadedExtensions' => [],
'unavailableExtensions' => [],
'notLoadedExtensions' => [],
];

if ($arguments->hasColors()) {
$result['colors'] = $arguments->colors();
}

if ($arguments->hasBootstrap()) {
$result['bootstrap'] = $arguments->bootstrap();
}

if ($arguments->hasCacheResult()) {
$result['cacheResult'] = $arguments->cacheResult();
}

if ($arguments->hasCacheResultFile()) {
$result['cacheResultFile'] = $arguments->cacheResultFile();
}

if ($arguments->hasColumns()) {
$result['columns'] = $arguments->columns();
}

if ($arguments->hasConfiguration()) {
$result['configuration'] = $arguments->configuration();
}

if ($arguments->hasCoverageCacheDirectory()) {
$result['coverageCacheDirectory'] = $arguments->coverageCacheDirectory();
}

if ($arguments->hasWarmCoverageCache()) {
$result['warmCoverageCache'] = $arguments->warmCoverageCache();
}

if ($arguments->hasCoverageClover()) {
$result['coverageClover'] = $arguments->coverageClover();
}

if ($arguments->hasCoverageCobertura()) {
$result['coverageCobertura'] = $arguments->coverageCobertura();
}

if ($arguments->hasCoverageCrap4J()) {
$result['coverageCrap4J'] = $arguments->coverageCrap4J();
}

if ($arguments->hasCoverageHtml()) {
$result['coverageHtml'] = $arguments->coverageHtml();
}

if ($arguments->hasCoveragePhp()) {
$result['coveragePHP'] = $arguments->coveragePhp();
}

if ($arguments->hasCoverageText()) {
$result['coverageText'] = $arguments->coverageText();
}

if ($arguments->hasCoverageTextShowUncoveredFiles()) {
$result['coverageTextShowUncoveredFiles'] = $arguments->hasCoverageTextShowUncoveredFiles();
}

if ($arguments->hasCoverageTextShowOnlySummary()) {
$result['coverageTextShowOnlySummary'] = $arguments->coverageTextShowOnlySummary();
}

if ($arguments->hasCoverageXml()) {
$result['coverageXml'] = $arguments->coverageXml();
}

if ($arguments->hasPathCoverage()) {
$result['pathCoverage'] = $arguments->pathCoverage();
}

if ($arguments->hasDebug()) {
$result['debug'] = $arguments->debug();
}

if ($arguments->hasHelp()) {
$result['help'] = $arguments->help();
}

if ($arguments->hasFilter()) {
$result['filter'] = $arguments->filter();
}

if ($arguments->hasTestSuite()) {
$result['testsuite'] = $arguments->testSuite();
}

if ($arguments->hasGroups()) {
$result['groups'] = $arguments->groups();
}

if ($arguments->hasExcludeGroups()) {
$result['excludeGroups'] = $arguments->excludeGroups();
}

if ($arguments->hasTestsCovering()) {
$result['testsCovering'] = $arguments->testsCovering();
}

if ($arguments->hasTestsUsing()) {
$result['testsUsing'] = $arguments->testsUsing();
}

if ($arguments->hasTestSuffixes()) {
$result['testSuffixes'] = $arguments->testSuffixes();
}

if ($arguments->hasIncludePath()) {
$result['includePath'] = $arguments->includePath();
}

if ($arguments->hasListGroups()) {
$result['listGroups'] = $arguments->listGroups();
}

if ($arguments->hasListSuites()) {
$result['listSuites'] = $arguments->listSuites();
}

if ($arguments->hasListTests()) {
$result['listTests'] = $arguments->listTests();
}

if ($arguments->hasListTestsXml()) {
$result['listTestsXml'] = $arguments->listTestsXml();
}

if ($arguments->hasPrinter()) {
$result['printer'] = $arguments->printer();
}

if ($arguments->hasLoader()) {
$result['loader'] = $arguments->loader();
}

if ($arguments->hasJunitLogfile()) {
$result['junitLogfile'] = $arguments->junitLogfile();
}

if ($arguments->hasTeamcityLogfile()) {
$result['teamcityLogfile'] = $arguments->teamcityLogfile();
}

if ($arguments->hasExecutionOrder()) {
$result['executionOrder'] = $arguments->executionOrder();
}

if ($arguments->hasExecutionOrderDefects()) {
$result['executionOrderDefects'] = $arguments->executionOrderDefects();
}

if ($arguments->hasExtensions()) {
$result['extensions'] = $arguments->extensions();
}

if ($arguments->hasUnavailableExtensions()) {
$result['unavailableExtensions'] = $arguments->unavailableExtensions();
}

if ($arguments->hasResolveDependencies()) {
$result['resolveDependencies'] = $arguments->resolveDependencies();
}

if ($arguments->hasProcessIsolation()) {
$result['processIsolation'] = $arguments->processIsolation();
}

if ($arguments->hasRepeat()) {
$result['repeat'] = $arguments->repeat();
}

if ($arguments->hasStderr()) {
$result['stderr'] = $arguments->stderr();
}

if ($arguments->hasStopOnDefect()) {
$result['stopOnDefect'] = $arguments->stopOnDefect();
}

if ($arguments->hasStopOnError()) {
$result['stopOnError'] = $arguments->stopOnError();
}

if ($arguments->hasStopOnFailure()) {
$result['stopOnFailure'] = $arguments->stopOnFailure();
}

if ($arguments->hasStopOnWarning()) {
$result['stopOnWarning'] = $arguments->stopOnWarning();
}

if ($arguments->hasStopOnIncomplete()) {
$result['stopOnIncomplete'] = $arguments->stopOnIncomplete();
}

if ($arguments->hasStopOnRisky()) {
$result['stopOnRisky'] = $arguments->stopOnRisky();
}

if ($arguments->hasStopOnSkipped()) {
$result['stopOnSkipped'] = $arguments->stopOnSkipped();
}

if ($arguments->hasFailOnEmptyTestSuite()) {
$result['failOnEmptyTestSuite'] = $arguments->failOnEmptyTestSuite();
}

if ($arguments->hasFailOnIncomplete()) {
$result['failOnIncomplete'] = $arguments->failOnIncomplete();
}

if ($arguments->hasFailOnRisky()) {
$result['failOnRisky'] = $arguments->failOnRisky();
}

if ($arguments->hasFailOnSkipped()) {
$result['failOnSkipped'] = $arguments->failOnSkipped();
}

if ($arguments->hasFailOnWarning()) {
$result['failOnWarning'] = $arguments->failOnWarning();
}

if ($arguments->hasTestdoxGroups()) {
$result['testdoxGroups'] = $arguments->testdoxGroups();
}

if ($arguments->hasTestdoxExcludeGroups()) {
$result['testdoxExcludeGroups'] = $arguments->testdoxExcludeGroups();
}

if ($arguments->hasTestdoxHtmlFile()) {
$result['testdoxHTMLFile'] = $arguments->testdoxHtmlFile();
}

if ($arguments->hasTestdoxTextFile()) {
$result['testdoxTextFile'] = $arguments->testdoxTextFile();
}

if ($arguments->hasTestdoxXmlFile()) {
$result['testdoxXMLFile'] = $arguments->testdoxXmlFile();
}

if ($arguments->hasUseDefaultConfiguration()) {
$result['useDefaultConfiguration'] = $arguments->useDefaultConfiguration();
}

if ($arguments->hasNoExtensions()) {
$result['noExtensions'] = $arguments->noExtensions();
}

if ($arguments->hasNoCoverage()) {
$result['noCoverage'] = $arguments->noCoverage();
}

if ($arguments->hasNoLogging()) {
$result['noLogging'] = $arguments->noLogging();
}

if ($arguments->hasNoInteraction()) {
$result['noInteraction'] = $arguments->noInteraction();
}

if ($arguments->hasBackupGlobals()) {
$result['backupGlobals'] = $arguments->backupGlobals();
}

if ($arguments->hasBackupStaticAttributes()) {
$result['backupStaticAttributes'] = $arguments->backupStaticAttributes();
}

if ($arguments->hasVerbose()) {
$result['verbose'] = $arguments->verbose();
}

if ($arguments->hasReportUselessTests()) {
$result['reportUselessTests'] = $arguments->reportUselessTests();
}

if ($arguments->hasStrictCoverage()) {
$result['strictCoverage'] = $arguments->strictCoverage();
}

if ($arguments->hasDisableCodeCoverageIgnore()) {
$result['disableCodeCoverageIgnore'] = $arguments->disableCodeCoverageIgnore();
}

if ($arguments->hasBeStrictAboutChangesToGlobalState()) {
$result['beStrictAboutChangesToGlobalState'] = $arguments->beStrictAboutChangesToGlobalState();
}

if ($arguments->hasDisallowTestOutput()) {
$result['disallowTestOutput'] = $arguments->disallowTestOutput();
}

if ($arguments->hasBeStrictAboutResourceUsageDuringSmallTests()) {
$result['beStrictAboutResourceUsageDuringSmallTests'] = $arguments->beStrictAboutResourceUsageDuringSmallTests();
}

if ($arguments->hasDefaultTimeLimit()) {
$result['defaultTimeLimit'] = $arguments->defaultTimeLimit();
}

if ($arguments->hasEnforceTimeLimit()) {
$result['enforceTimeLimit'] = $arguments->enforceTimeLimit();
}

if ($arguments->hasDisallowTodoAnnotatedTests()) {
$result['disallowTodoAnnotatedTests'] = $arguments->disallowTodoAnnotatedTests();
}

if ($arguments->hasReverseList()) {
$result['reverseList'] = $arguments->reverseList();
}

if ($arguments->hasCoverageFilter()) {
$result['coverageFilter'] = $arguments->coverageFilter();
}

if ($arguments->hasRandomOrderSeed()) {
$result['randomOrderSeed'] = $arguments->randomOrderSeed();
}

if ($arguments->hasXdebugFilterFile()) {
$result['xdebugFilterFile'] = $arguments->xdebugFilterFile();
}

return $result;
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI\CliArguments;

use function array_map;
use function array_merge;
use function class_exists;
use function explode;
use function is_numeric;
use function str_replace;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\TextUI\DefaultResultPrinter;
use PHPUnit\TextUI\XmlConfiguration\Extension;
use PHPUnit\Util\Log\TeamCity;
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
use SebastianBergmann\CliParser\Exception as CliParserException;
use SebastianBergmann\CliParser\Parser as CliParser;




final class Builder
{
private const LONG_OPTIONS = [
'atleast-version=',
'prepend=',
'bootstrap=',
'cache-result',
'do-not-cache-result',
'cache-result-file=',
'check-version',
'colors==',
'columns=',
'configuration=',
'coverage-cache=',
'warm-coverage-cache',
'coverage-filter=',
'coverage-clover=',
'coverage-cobertura=',
'coverage-crap4j=',
'coverage-html=',
'coverage-php=',
'coverage-text==',
'coverage-xml=',
'path-coverage',
'debug',
'disallow-test-output',
'disallow-resource-usage',
'disallow-todo-tests',
'default-time-limit=',
'enforce-time-limit',
'exclude-group=',
'extensions=',
'filter=',
'generate-configuration',
'globals-backup',
'group=',
'covers=',
'uses=',
'help',
'resolve-dependencies',
'ignore-dependencies',
'include-path=',
'list-groups',
'list-suites',
'list-tests',
'list-tests-xml=',
'loader=',
'log-junit=',
'log-teamcity=',
'migrate-configuration',
'no-configuration',
'no-coverage',
'no-logging',
'no-interaction',
'no-extensions',
'order-by=',
'printer=',
'process-isolation',
'repeat=',
'dont-report-useless-tests',
'random-order',
'random-order-seed=',
'reverse-order',
'reverse-list',
'static-backup',
'stderr',
'stop-on-defect',
'stop-on-error',
'stop-on-failure',
'stop-on-warning',
'stop-on-incomplete',
'stop-on-risky',
'stop-on-skipped',
'fail-on-empty-test-suite',
'fail-on-incomplete',
'fail-on-risky',
'fail-on-skipped',
'fail-on-warning',
'strict-coverage',
'disable-coverage-ignore',
'strict-global-state',
'teamcity',
'testdox',
'testdox-group=',
'testdox-exclude-group=',
'testdox-html=',
'testdox-text=',
'testdox-xml=',
'test-suffix=',
'testsuite=',
'verbose',
'version',
'whitelist=',
'dump-xdebug-filter=',
];
private const SHORT_OPTIONS = 'd:c:hv';

public function fromParameters(array $parameters, array $additionalLongOptions): Configuration
{
try {
$options = (new CliParser)->parse(
$parameters,
self::SHORT_OPTIONS,
array_merge(self::LONG_OPTIONS, $additionalLongOptions),
);
} catch (CliParserException $e) {
throw new Exception(
$e->getMessage(),
$e->getCode(),
$e,
);
}

$argument = null;
$atLeastVersion = null;
$backupGlobals = null;
$backupStaticAttributes = null;
$beStrictAboutChangesToGlobalState = null;
$beStrictAboutResourceUsageDuringSmallTests = null;
$bootstrap = null;
$cacheResult = null;
$cacheResultFile = null;
$checkVersion = null;
$colors = null;
$columns = null;
$configuration = null;
$coverageCacheDirectory = null;
$warmCoverageCache = null;
$coverageFilter = null;
$coverageClover = null;
$coverageCobertura = null;
$coverageCrap4J = null;
$coverageHtml = null;
$coveragePhp = null;
$coverageText = null;
$coverageTextShowUncoveredFiles = null;
$coverageTextShowOnlySummary = null;
$coverageXml = null;
$pathCoverage = null;
$debug = null;
$defaultTimeLimit = null;
$disableCodeCoverageIgnore = null;
$disallowTestOutput = null;
$disallowTodoAnnotatedTests = null;
$enforceTimeLimit = null;
$excludeGroups = null;
$executionOrder = null;
$executionOrderDefects = null;
$extensions = [];
$unavailableExtensions = [];
$failOnEmptyTestSuite = null;
$failOnIncomplete = null;
$failOnRisky = null;
$failOnSkipped = null;
$failOnWarning = null;
$filter = null;
$generateConfiguration = null;
$migrateConfiguration = null;
$groups = null;
$testsCovering = null;
$testsUsing = null;
$help = null;
$includePath = null;
$iniSettings = [];
$junitLogfile = null;
$listGroups = null;
$listSuites = null;
$listTests = null;
$listTestsXml = null;
$loader = null;
$noCoverage = null;
$noExtensions = null;
$noInteraction = null;
$noLogging = null;
$printer = null;
$processIsolation = null;
$randomOrderSeed = null;
$repeat = null;
$reportUselessTests = null;
$resolveDependencies = null;
$reverseList = null;
$stderr = null;
$strictCoverage = null;
$stopOnDefect = null;
$stopOnError = null;
$stopOnFailure = null;
$stopOnIncomplete = null;
$stopOnRisky = null;
$stopOnSkipped = null;
$stopOnWarning = null;
$teamcityLogfile = null;
$testdoxExcludeGroups = null;
$testdoxGroups = null;
$testdoxHtmlFile = null;
$testdoxTextFile = null;
$testdoxXmlFile = null;
$testSuffixes = null;
$testSuite = null;
$unrecognizedOptions = [];
$unrecognizedOrderBy = null;
$useDefaultConfiguration = null;
$verbose = null;
$version = null;
$xdebugFilterFile = null;

if (isset($options[1][0])) {
$argument = $options[1][0];
}

foreach ($options[0] as $option) {
switch ($option[0]) {
case '--colors':
$colors = $option[1] ?: DefaultResultPrinter::COLOR_AUTO;

break;

case '--bootstrap':
$bootstrap = $option[1];

break;

case '--cache-result':
$cacheResult = true;

break;

case '--do-not-cache-result':
$cacheResult = false;

break;

case '--cache-result-file':
$cacheResultFile = $option[1];

break;

case '--columns':
if (is_numeric($option[1])) {
$columns = (int) $option[1];
} elseif ($option[1] === 'max') {
$columns = 'max';
}

break;

case 'c':
case '--configuration':
$configuration = $option[1];

break;

case '--coverage-cache':
$coverageCacheDirectory = $option[1];

break;

case '--warm-coverage-cache':
$warmCoverageCache = true;

break;

case '--coverage-clover':
$coverageClover = $option[1];

break;

case '--coverage-cobertura':
$coverageCobertura = $option[1];

break;

case '--coverage-crap4j':
$coverageCrap4J = $option[1];

break;

case '--coverage-html':
$coverageHtml = $option[1];

break;

case '--coverage-php':
$coveragePhp = $option[1];

break;

case '--coverage-text':
if ($option[1] === null) {
$option[1] = 'php://stdout';
}

$coverageText = $option[1];
$coverageTextShowUncoveredFiles = false;
$coverageTextShowOnlySummary = false;

break;

case '--coverage-xml':
$coverageXml = $option[1];

break;

case '--path-coverage':
$pathCoverage = true;

break;

case 'd':
$tmp = explode('=', $option[1]);

if (isset($tmp[0])) {
if (isset($tmp[1])) {
$iniSettings[$tmp[0]] = $tmp[1];
} else {
$iniSettings[$tmp[0]] = '1';
}
}

break;

case '--debug':
$debug = true;

break;

case 'h':
case '--help':
$help = true;

break;

case '--filter':
$filter = $option[1];

break;

case '--testsuite':
$testSuite = $option[1];

break;

case '--generate-configuration':
$generateConfiguration = true;

break;

case '--migrate-configuration':
$migrateConfiguration = true;

break;

case '--group':
$groups = explode(',', $option[1]);

break;

case '--exclude-group':
$excludeGroups = explode(',', $option[1]);

break;

case '--covers':
$testsCovering = array_map('strtolower', explode(',', $option[1]));

break;

case '--uses':
$testsUsing = array_map('strtolower', explode(',', $option[1]));

break;

case '--test-suffix':
$testSuffixes = explode(',', $option[1]);

break;

case '--include-path':
$includePath = $option[1];

break;

case '--list-groups':
$listGroups = true;

break;

case '--list-suites':
$listSuites = true;

break;

case '--list-tests':
$listTests = true;

break;

case '--list-tests-xml':
$listTestsXml = $option[1];

break;

case '--printer':
$printer = $option[1];

break;

case '--loader':
$loader = $option[1];

break;

case '--log-junit':
$junitLogfile = $option[1];

break;

case '--log-teamcity':
$teamcityLogfile = $option[1];

break;

case '--order-by':
foreach (explode(',', $option[1]) as $order) {
switch ($order) {
case 'default':
$executionOrder = TestSuiteSorter::ORDER_DEFAULT;
$executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT;
$resolveDependencies = true;

break;

case 'defects':
$executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST;

break;

case 'depends':
$resolveDependencies = true;

break;

case 'duration':
$executionOrder = TestSuiteSorter::ORDER_DURATION;

break;

case 'no-depends':
$resolveDependencies = false;

break;

case 'random':
$executionOrder = TestSuiteSorter::ORDER_RANDOMIZED;

break;

case 'reverse':
$executionOrder = TestSuiteSorter::ORDER_REVERSED;

break;

case 'size':
$executionOrder = TestSuiteSorter::ORDER_SIZE;

break;

default:
$unrecognizedOrderBy = $order;
}
}

break;

case '--process-isolation':
$processIsolation = true;

break;

case '--repeat':
$repeat = (int) $option[1];

break;

case '--stderr':
$stderr = true;

break;

case '--stop-on-defect':
$stopOnDefect = true;

break;

case '--stop-on-error':
$stopOnError = true;

break;

case '--stop-on-failure':
$stopOnFailure = true;

break;

case '--stop-on-warning':
$stopOnWarning = true;

break;

case '--stop-on-incomplete':
$stopOnIncomplete = true;

break;

case '--stop-on-risky':
$stopOnRisky = true;

break;

case '--stop-on-skipped':
$stopOnSkipped = true;

break;

case '--fail-on-empty-test-suite':
$failOnEmptyTestSuite = true;

break;

case '--fail-on-incomplete':
$failOnIncomplete = true;

break;

case '--fail-on-risky':
$failOnRisky = true;

break;

case '--fail-on-skipped':
$failOnSkipped = true;

break;

case '--fail-on-warning':
$failOnWarning = true;

break;

case '--teamcity':
$printer = TeamCity::class;

break;

case '--testdox':
$printer = CliTestDoxPrinter::class;

break;

case '--testdox-group':
$testdoxGroups = explode(',', $option[1]);

break;

case '--testdox-exclude-group':
$testdoxExcludeGroups = explode(',', $option[1]);

break;

case '--testdox-html':
$testdoxHtmlFile = $option[1];

break;

case '--testdox-text':
$testdoxTextFile = $option[1];

break;

case '--testdox-xml':
$testdoxXmlFile = $option[1];

break;

case '--no-configuration':
$useDefaultConfiguration = false;

break;

case '--extensions':
foreach (explode(',', $option[1]) as $extensionClass) {
if (!class_exists($extensionClass)) {
$unavailableExtensions[] = $extensionClass;

continue;
}

$extensions[] = new Extension($extensionClass, '', []);
}

break;

case '--no-extensions':
$noExtensions = true;

break;

case '--no-coverage':
$noCoverage = true;

break;

case '--no-logging':
$noLogging = true;

break;

case '--no-interaction':
$noInteraction = true;

break;

case '--globals-backup':
$backupGlobals = true;

break;

case '--static-backup':
$backupStaticAttributes = true;

break;

case 'v':
case '--verbose':
$verbose = true;

break;

case '--atleast-version':
$atLeastVersion = $option[1];

break;

case '--version':
$version = true;

break;

case '--dont-report-useless-tests':
$reportUselessTests = false;

break;

case '--strict-coverage':
$strictCoverage = true;

break;

case '--disable-coverage-ignore':
$disableCodeCoverageIgnore = true;

break;

case '--strict-global-state':
$beStrictAboutChangesToGlobalState = true;

break;

case '--disallow-test-output':
$disallowTestOutput = true;

break;

case '--disallow-resource-usage':
$beStrictAboutResourceUsageDuringSmallTests = true;

break;

case '--default-time-limit':
$defaultTimeLimit = (int) $option[1];

break;

case '--enforce-time-limit':
$enforceTimeLimit = true;

break;

case '--disallow-todo-tests':
$disallowTodoAnnotatedTests = true;

break;

case '--reverse-list':
$reverseList = true;

break;

case '--check-version':
$checkVersion = true;

break;

case '--coverage-filter':
case '--whitelist':
if ($coverageFilter === null) {
$coverageFilter = [];
}

$coverageFilter[] = $option[1];

break;

case '--random-order':
$executionOrder = TestSuiteSorter::ORDER_RANDOMIZED;

break;

case '--random-order-seed':
$randomOrderSeed = (int) $option[1];

break;

case '--resolve-dependencies':
$resolveDependencies = true;

break;

case '--ignore-dependencies':
$resolveDependencies = false;

break;

case '--reverse-order':
$executionOrder = TestSuiteSorter::ORDER_REVERSED;

break;

case '--dump-xdebug-filter':
$xdebugFilterFile = $option[1];

break;

default:
$unrecognizedOptions[str_replace('--', '', $option[0])] = $option[1];
}
}

if (empty($extensions)) {
$extensions = null;
}

if (empty($unavailableExtensions)) {
$unavailableExtensions = null;
}

if (empty($iniSettings)) {
$iniSettings = null;
}

if (empty($coverageFilter)) {
$coverageFilter = null;
}

return new Configuration(
$argument,
$atLeastVersion,
$backupGlobals,
$backupStaticAttributes,
$beStrictAboutChangesToGlobalState,
$beStrictAboutResourceUsageDuringSmallTests,
$bootstrap,
$cacheResult,
$cacheResultFile,
$checkVersion,
$colors,
$columns,
$configuration,
$coverageClover,
$coverageCobertura,
$coverageCrap4J,
$coverageHtml,
$coveragePhp,
$coverageText,
$coverageTextShowUncoveredFiles,
$coverageTextShowOnlySummary,
$coverageXml,
$pathCoverage,
$coverageCacheDirectory,
$warmCoverageCache,
$debug,
$defaultTimeLimit,
$disableCodeCoverageIgnore,
$disallowTestOutput,
$disallowTodoAnnotatedTests,
$enforceTimeLimit,
$excludeGroups,
$executionOrder,
$executionOrderDefects,
$extensions,
$unavailableExtensions,
$failOnEmptyTestSuite,
$failOnIncomplete,
$failOnRisky,
$failOnSkipped,
$failOnWarning,
$filter,
$generateConfiguration,
$migrateConfiguration,
$groups,
$testsCovering,
$testsUsing,
$help,
$includePath,
$iniSettings,
$junitLogfile,
$listGroups,
$listSuites,
$listTests,
$listTestsXml,
$loader,
$noCoverage,
$noExtensions,
$noInteraction,
$noLogging,
$printer,
$processIsolation,
$randomOrderSeed,
$repeat,
$reportUselessTests,
$resolveDependencies,
$reverseList,
$stderr,
$strictCoverage,
$stopOnDefect,
$stopOnError,
$stopOnFailure,
$stopOnIncomplete,
$stopOnRisky,
$stopOnSkipped,
$stopOnWarning,
$teamcityLogfile,
$testdoxExcludeGroups,
$testdoxGroups,
$testdoxHtmlFile,
$testdoxTextFile,
$testdoxXmlFile,
$testSuffixes,
$testSuite,
$unrecognizedOptions,
$unrecognizedOrderBy,
$useDefaultConfiguration,
$verbose,
$version,
$coverageFilter,
$xdebugFilterFile,
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use const PHP_EOL;
use function array_map;
use function array_reverse;
use function count;
use function floor;
use function implode;
use function in_array;
use function is_int;
use function max;
use function preg_split;
use function sprintf;
use function str_pad;
use function str_repeat;
use function strlen;
use function trim;
use function vsprintf;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\InvalidArgumentException;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Color;
use PHPUnit\Util\Printer;
use SebastianBergmann\Environment\Console;
use SebastianBergmann\Timer\ResourceUsageFormatter;
use SebastianBergmann\Timer\Timer;
use Throwable;




class DefaultResultPrinter extends Printer implements ResultPrinter
{
public const EVENT_TEST_START = 0;
public const EVENT_TEST_END = 1;
public const EVENT_TESTSUITE_START = 2;
public const EVENT_TESTSUITE_END = 3;
public const COLOR_NEVER = 'never';
public const COLOR_AUTO = 'auto';
public const COLOR_ALWAYS = 'always';
public const COLOR_DEFAULT = self::COLOR_NEVER;
private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS];




protected $column = 0;




protected $maxColumn;




protected $lastTestFailed = false;




protected $numAssertions = 0;




protected $numTests = -1;




protected $numTestsRun = 0;




protected $numTestsWidth;




protected $colors = false;




protected $debug = false;




protected $verbose = false;




private $numberOfColumns;




private $reverse;




private $defectListPrinted = false;




private $timer;









public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
{
parent::__construct($out);

if (!in_array($colors, self::AVAILABLE_COLORS, true)) {
throw InvalidArgumentException::create(
3,
vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS),
);
}

if (!is_int($numberOfColumns) && $numberOfColumns !== 'max') {
throw InvalidArgumentException::create(5, 'integer or "max"');
}

$console = new Console;
$maxNumberOfColumns = $console->getNumberOfColumns();

if ($numberOfColumns === 'max' || ($numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns)) {
$numberOfColumns = $maxNumberOfColumns;
}

$this->numberOfColumns = $numberOfColumns;
$this->verbose = $verbose;
$this->debug = $debug;
$this->reverse = $reverse;

if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) {
$this->colors = true;
} else {
$this->colors = (self::COLOR_ALWAYS === $colors);
}

$this->timer = new Timer;

$this->timer->start();
}

public function printResult(TestResult $result): void
{
$this->printHeader($result);
$this->printErrors($result);
$this->printWarnings($result);
$this->printFailures($result);
$this->printRisky($result);

if ($this->verbose) {
$this->printIncompletes($result);
$this->printSkipped($result);
}

$this->printFooter($result);
}




public function addError(Test $test, Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-red, bold', 'E');
$this->lastTestFailed = true;
}




public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->writeProgressWithColor('bg-red, fg-white', 'F');
$this->lastTestFailed = true;
}




public function addWarning(Test $test, Warning $e, float $time): void
{
$this->writeProgressWithColor('fg-yellow, bold', 'W');
$this->lastTestFailed = true;
}




public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-yellow, bold', 'I');
$this->lastTestFailed = true;
}




public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-yellow, bold', 'R');
$this->lastTestFailed = true;
}




public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-cyan, bold', 'S');
$this->lastTestFailed = true;
}




public function startTestSuite(TestSuite $suite): void
{
if ($this->numTests == -1) {
$this->numTests = count($suite);
$this->numTestsWidth = strlen((string) $this->numTests);
$this->maxColumn = $this->numberOfColumns - strlen('  /  (XXX%)') - (2 * $this->numTestsWidth);
}
}




public function endTestSuite(TestSuite $suite): void
{
}




public function startTest(Test $test): void
{
if ($this->debug) {
$this->write(
sprintf(
"Test '%s' started\n",
\PHPUnit\Util\Test::describeAsString($test),
),
);
}
}




public function endTest(Test $test, float $time): void
{
if ($this->debug) {
$this->write(
sprintf(
"Test '%s' ended\n",
\PHPUnit\Util\Test::describeAsString($test),
),
);
}

if (!$this->lastTestFailed) {
$this->writeProgress('.');
}

if ($test instanceof TestCase) {
$this->numAssertions += $test->getNumAssertions();
} elseif ($test instanceof PhptTestCase) {
$this->numAssertions++;
}

$this->lastTestFailed = false;

if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) {
$this->write($test->getActualOutput());
}
}

protected function printDefects(array $defects, string $type): void
{
$count = count($defects);

if ($count == 0) {
return;
}

if ($this->defectListPrinted) {
$this->write("\n--\n\n");
}

$this->write(
sprintf(
"There %s %d %s%s:\n",
($count == 1) ? 'was' : 'were',
$count,
$type,
($count == 1) ? '' : 's',
),
);

$i = 1;

if ($this->reverse) {
$defects = array_reverse($defects);
}

foreach ($defects as $defect) {
$this->printDefect($defect, $i++);
}

$this->defectListPrinted = true;
}

protected function printDefect(TestFailure $defect, int $count): void
{
$this->printDefectHeader($defect, $count);
$this->printDefectTrace($defect);
}

protected function printDefectHeader(TestFailure $defect, int $count): void
{
$this->write(
sprintf(
"\n%d) %s\n",
$count,
$defect->getTestName(),
),
);
}

protected function printDefectTrace(TestFailure $defect): void
{
$e = $defect->thrownException();

$this->write((string) $e);

while ($e = $e->getPrevious()) {
$this->write("\nCaused by\n" . trim((string) $e) . "\n");
}
}

protected function printErrors(TestResult $result): void
{
$this->printDefects($result->errors(), 'error');
}

protected function printFailures(TestResult $result): void
{
$this->printDefects($result->failures(), 'failure');
}

protected function printWarnings(TestResult $result): void
{
$this->printDefects($result->warnings(), 'warning');
}

protected function printIncompletes(TestResult $result): void
{
$this->printDefects($result->notImplemented(), 'incomplete test');
}

protected function printRisky(TestResult $result): void
{
$this->printDefects($result->risky(), 'risky test');
}

protected function printSkipped(TestResult $result): void
{
$this->printDefects($result->skipped(), 'skipped test');
}

protected function printHeader(TestResult $result): void
{
if (count($result) > 0) {
$this->write(PHP_EOL . PHP_EOL . (new ResourceUsageFormatter)->resourceUsage($this->timer->stop()) . PHP_EOL . PHP_EOL);
}
}

protected function printFooter(TestResult $result): void
{
if (count($result) === 0) {
$this->writeWithColor(
'fg-black, bg-yellow',
'No tests executed!',
);

return;
}

if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) {
$this->writeWithColor(
'fg-black, bg-green',
sprintf(
'OK (%d test%s, %d assertion%s)',
count($result),
(count($result) === 1) ? '' : 's',
$this->numAssertions,
($this->numAssertions === 1) ? '' : 's',
),
);

return;
}

$color = 'fg-black, bg-yellow';

if ($result->wasSuccessful()) {
if ($this->verbose || !$result->allHarmless()) {
$this->write("\n");
}

$this->writeWithColor(
$color,
'OK, but incomplete, skipped, or risky tests!',
);
} else {
$this->write("\n");

if ($result->errorCount()) {
$color = 'fg-white, bg-red';

$this->writeWithColor(
$color,
'ERRORS!',
);
} elseif ($result->failureCount()) {
$color = 'fg-white, bg-red';

$this->writeWithColor(
$color,
'FAILURES!',
);
} elseif ($result->warningCount()) {
$color = 'fg-black, bg-yellow';

$this->writeWithColor(
$color,
'WARNINGS!',
);
}
}

$this->writeCountString(count($result), 'Tests', $color, true);
$this->writeCountString($this->numAssertions, 'Assertions', $color, true);
$this->writeCountString($result->errorCount(), 'Errors', $color);
$this->writeCountString($result->failureCount(), 'Failures', $color);
$this->writeCountString($result->warningCount(), 'Warnings', $color);
$this->writeCountString($result->skippedCount(), 'Skipped', $color);
$this->writeCountString($result->notImplementedCount(), 'Incomplete', $color);
$this->writeCountString($result->riskyCount(), 'Risky', $color);
$this->writeWithColor($color, '.');
}

protected function writeProgress(string $progress): void
{
if ($this->debug) {
return;
}

$this->write($progress);
$this->column++;
$this->numTestsRun++;

if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) {
if ($this->numTestsRun == $this->numTests) {
$this->write(str_repeat(' ', $this->maxColumn - $this->column));
}

$this->write(
sprintf(
' %' . $this->numTestsWidth . 'd / %' .
$this->numTestsWidth . 'd (%3s%%)',
$this->numTestsRun,
$this->numTests,
floor(($this->numTestsRun / $this->numTests) * 100),
),
);

if ($this->column == $this->maxColumn) {
$this->writeNewLine();
}
}
}

protected function writeNewLine(): void
{
$this->column = 0;
$this->write("\n");
}





protected function colorizeTextBox(string $color, string $buffer): string
{
if (!$this->colors) {
return $buffer;
}

$lines = preg_split('/\r\n|\r|\n/', $buffer);
$padding = max(array_map('\strlen', $lines));

$styledLines = [];

foreach ($lines as $line) {
$styledLines[] = Color::colorize($color, str_pad($line, $padding));
}

return implode(PHP_EOL, $styledLines);
}




protected function writeWithColor(string $color, string $buffer, bool $lf = true): void
{
$this->write($this->colorizeTextBox($color, $buffer));

if ($lf) {
$this->write(PHP_EOL);
}
}




protected function writeProgressWithColor(string $color, string $buffer): void
{
$buffer = $this->colorizeTextBox($color, $buffer);
$this->writeProgress($buffer);
}

private function writeCountString(int $count, string $name, string $color, bool $always = false): void
{
static $first = true;

if ($always || $count > 0) {
$this->writeWithColor(
$color,
sprintf(
'%s%s: %d',
!$first ? ', ' : '',
$name,
$count,
),
false,
);

$first = false;
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use const PATH_SEPARATOR;
use const PHP_EOL;
use const STDIN;
use function array_keys;
use function assert;
use function class_exists;
use function copy;
use function explode;
use function extension_loaded;
use function fgets;
use function file_get_contents;
use function file_put_contents;
use function get_class;
use function getcwd;
use function ini_get;
use function ini_set;
use function is_array;
use function is_callable;
use function is_dir;
use function is_file;
use function is_string;
use function printf;
use function realpath;
use function sort;
use function sprintf;
use function stream_resolve_include_path;
use function strpos;
use function trim;
use function version_compare;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\Extension\PharLoader;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Runner\TestSuiteLoader;
use PHPUnit\Runner\Version;
use PHPUnit\TextUI\CliArguments\Builder;
use PHPUnit\TextUI\CliArguments\Configuration;
use PHPUnit\TextUI\CliArguments\Exception as ArgumentsException;
use PHPUnit\TextUI\CliArguments\Mapper;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper;
use PHPUnit\TextUI\XmlConfiguration\Generator;
use PHPUnit\TextUI\XmlConfiguration\Loader;
use PHPUnit\TextUI\XmlConfiguration\Migrator;
use PHPUnit\TextUI\XmlConfiguration\PhpHandler;
use PHPUnit\Util\FileLoader;
use PHPUnit\Util\Filesystem;
use PHPUnit\Util\Printer;
use PHPUnit\Util\TextTestListRenderer;
use PHPUnit\Util\Xml\SchemaDetector;
use PHPUnit\Util\XmlTestListRenderer;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer;
use SebastianBergmann\Timer\Timer;
use Throwable;




class Command
{



protected $arguments = [];




protected $longOptions = [];




private $versionStringPrinted = false;




private $warnings = [];




public static function main(bool $exit = true): int
{
try {
return (new static)->run($_SERVER['argv'], $exit);
} catch (Throwable $t) {
throw new RuntimeException(
$t->getMessage(),
(int) $t->getCode(),
$t,
);
}
}




public function run(array $argv, bool $exit = true): int
{
$this->handleArguments($argv);

$runner = $this->createRunner();

if ($this->arguments['test'] instanceof TestSuite) {
$suite = $this->arguments['test'];
} else {
$suite = $runner->getTest(
$this->arguments['test'],
$this->arguments['testSuffixes'],
);
}

if ($this->arguments['listGroups']) {
return $this->handleListGroups($suite, $exit);
}

if ($this->arguments['listSuites']) {
return $this->handleListSuites($exit);
}

if ($this->arguments['listTests']) {
return $this->handleListTests($suite, $exit);
}

if ($this->arguments['listTestsXml']) {
return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit);
}

unset($this->arguments['test'], $this->arguments['testFile']);

try {
$result = $runner->run($suite, $this->arguments, $this->warnings, $exit);
} catch (Throwable $t) {
print $t->getMessage() . PHP_EOL;
}

$return = TestRunner::FAILURE_EXIT;

if (isset($result) && $result->wasSuccessful()) {
$return = TestRunner::SUCCESS_EXIT;
} elseif (!isset($result) || $result->errorCount() > 0) {
$return = TestRunner::EXCEPTION_EXIT;
}

if ($exit) {
exit($return);
}

return $return;
}




protected function createRunner(): TestRunner
{
return new TestRunner($this->arguments['loader']);
}














































protected function handleArguments(array $argv): void
{
try {
$arguments = (new Builder)->fromParameters($argv, array_keys($this->longOptions));
} catch (ArgumentsException $e) {
$this->exitWithErrorMessage($e->getMessage());
}

assert(isset($arguments) && $arguments instanceof Configuration);

if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) {
$this->generateConfiguration();
}

if ($arguments->hasAtLeastVersion()) {
if (version_compare(Version::id(), $arguments->atLeastVersion(), '>=')) {
exit(TestRunner::SUCCESS_EXIT);
}

exit(TestRunner::FAILURE_EXIT);
}

if ($arguments->hasVersion() && $arguments->version()) {
$this->printVersionString();

exit(TestRunner::SUCCESS_EXIT);
}

if ($arguments->hasCheckVersion() && $arguments->checkVersion()) {
$this->handleVersionCheck();
}

if ($arguments->hasHelp()) {
$this->showHelp();

exit(TestRunner::SUCCESS_EXIT);
}

if ($arguments->hasUnrecognizedOrderBy()) {
$this->exitWithErrorMessage(
sprintf(
'unrecognized --order-by option: %s',
$arguments->unrecognizedOrderBy(),
),
);
}

if ($arguments->hasIniSettings()) {
foreach ($arguments->iniSettings() as $name => $value) {
ini_set($name, $value);
}
}

if ($arguments->hasIncludePath()) {
ini_set(
'include_path',
$arguments->includePath() . PATH_SEPARATOR . ini_get('include_path'),
);
}

$this->arguments = (new Mapper)->mapToLegacyArray($arguments);

$this->handleCustomOptions($arguments->unrecognizedOptions());
$this->handleCustomTestSuite();

if (!isset($this->arguments['testSuffixes'])) {
$this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
}

if (!isset($this->arguments['test']) && $arguments->hasArgument()) {
$this->arguments['test'] = realpath($arguments->argument());

if ($this->arguments['test'] === false) {
$this->exitWithErrorMessage(
sprintf(
'Cannot open file "%s".',
$arguments->argument(),
),
);
}
}

if ($this->arguments['loader'] !== null) {
$this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
}

if (isset($this->arguments['configuration'])) {
if (is_dir($this->arguments['configuration'])) {
$candidate = $this->configurationFileInDirectory($this->arguments['configuration']);

if ($candidate !== null) {
$this->arguments['configuration'] = $candidate;
}
}
} elseif ($this->arguments['useDefaultConfiguration']) {
$candidate = $this->configurationFileInDirectory(getcwd());

if ($candidate !== null) {
$this->arguments['configuration'] = $candidate;
}
}

if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) {
if (!isset($this->arguments['configuration'])) {
print 'No configuration file found to migrate.' . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}

$this->migrateConfiguration(realpath($this->arguments['configuration']));
}

if (isset($this->arguments['configuration'])) {
try {
$this->arguments['configurationObject'] = (new Loader)->load($this->arguments['configuration']);
} catch (Throwable $e) {
print $e->getMessage() . PHP_EOL;

exit(TestRunner::FAILURE_EXIT);
}

$phpunitConfiguration = $this->arguments['configurationObject']->phpunit();

(new PhpHandler)->handle($this->arguments['configurationObject']->php());

if (isset($this->arguments['bootstrap'])) {
$this->handleBootstrap($this->arguments['bootstrap']);
} elseif ($phpunitConfiguration->hasBootstrap()) {
$this->handleBootstrap($phpunitConfiguration->bootstrap());
}

if (!isset($this->arguments['stderr'])) {
$this->arguments['stderr'] = $phpunitConfiguration->stderr();
}

if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && extension_loaded('phar')) {
$result = (new PharLoader)->loadPharExtensionsInDirectory($phpunitConfiguration->extensionsDirectory());

$this->arguments['loadedExtensions'] = $result['loadedExtensions'];
$this->arguments['notLoadedExtensions'] = $result['notLoadedExtensions'];

unset($result);
}

if (!isset($this->arguments['columns'])) {
$this->arguments['columns'] = $phpunitConfiguration->columns();
}

if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) {
$file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : '';

$this->arguments['printer'] = $this->handlePrinter(
$phpunitConfiguration->printerClass(),
$file,
);
}

if ($phpunitConfiguration->hasTestSuiteLoaderClass()) {
$file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : '';

$this->arguments['loader'] = $this->handleLoader(
$phpunitConfiguration->testSuiteLoaderClass(),
$file,
);
}

if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) {
$this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite();
}

if (!isset($this->arguments['test'])) {
try {
$this->arguments['test'] = (new TestSuiteMapper)->map(
$this->arguments['configurationObject']->testSuite(),
$this->arguments['testsuite'] ?? '',
);
} catch (Exception $e) {
$this->printVersionString();

print $e->getMessage() . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}
}
} elseif (isset($this->arguments['bootstrap'])) {
$this->handleBootstrap($this->arguments['bootstrap']);
}

if (isset($this->arguments['printer']) && is_string($this->arguments['printer'])) {
$this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']);
}

if (isset($this->arguments['configurationObject'], $this->arguments['warmCoverageCache'])) {
$this->handleWarmCoverageCache($this->arguments['configurationObject']);
}

if (!isset($this->arguments['test'])) {
$this->showHelp();

exit(TestRunner::EXCEPTION_EXIT);
}
}






protected function handleLoader(string $loaderClass, string $loaderFile = ''): ?TestSuiteLoader
{
$this->warnings[] = 'Using a custom test suite loader is deprecated';

if (!class_exists($loaderClass, false)) {
if ($loaderFile == '') {
$loaderFile = Filesystem::classNameToFilename(
$loaderClass,
);
}

$loaderFile = stream_resolve_include_path($loaderFile);

if ($loaderFile) {





require $loaderFile;
}
}

if (class_exists($loaderClass, false)) {
try {
$class = new ReflectionClass($loaderClass);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
}


if ($class->implementsInterface(TestSuiteLoader::class) && $class->isInstantiable()) {
$object = $class->newInstance();

assert($object instanceof TestSuiteLoader);

return $object;
}
}

if ($loaderClass == StandardTestSuiteLoader::class) {
return null;
}

$this->exitWithErrorMessage(
sprintf(
'Could not use "%s" as loader.',
$loaderClass,
),
);

return null;
}






protected function handlePrinter(string $printerClass, string $printerFile = '')
{
if (!class_exists($printerClass, false)) {
if ($printerFile === '') {
$printerFile = Filesystem::classNameToFilename(
$printerClass,
);
}

$printerFile = stream_resolve_include_path($printerFile);

if ($printerFile) {





require $printerFile;
}
}

if (!class_exists($printerClass)) {
$this->exitWithErrorMessage(
sprintf(
'Could not use "%s" as printer: class does not exist',
$printerClass,
),
);
}

try {
$class = new ReflectionClass($printerClass);

} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);

}

if (!$class->implementsInterface(ResultPrinter::class)) {
$this->exitWithErrorMessage(
sprintf(
'Could not use "%s" as printer: class does not implement %s',
$printerClass,
ResultPrinter::class,
),
);
}

if (!$class->isInstantiable()) {
$this->exitWithErrorMessage(
sprintf(
'Could not use "%s" as printer: class cannot be instantiated',
$printerClass,
),
);
}

if ($class->isSubclassOf(ResultPrinter::class)) {
return $printerClass;
}

$outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null;

return $class->newInstance($outputStream);
}




protected function handleBootstrap(string $filename): void
{
try {
FileLoader::checkAndLoad($filename);
} catch (Throwable $t) {
if ($t instanceof \PHPUnit\Exception) {
$this->exitWithErrorMessage($t->getMessage());
}

$message = sprintf(
'Error in bootstrap script: %s:%s%s%s%s',
get_class($t),
PHP_EOL,
$t->getMessage(),
PHP_EOL,
$t->getTraceAsString(),
);

while ($t = $t->getPrevious()) {
$message .= sprintf(
'%s%sPrevious error: %s:%s%s%s%s',
PHP_EOL,
PHP_EOL,
get_class($t),
PHP_EOL,
$t->getMessage(),
PHP_EOL,
$t->getTraceAsString(),
);
}

$this->exitWithErrorMessage($message);
}
}

protected function handleVersionCheck(): void
{
$this->printVersionString();

$latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit');
$latestCompatibleVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit-' . explode('.', Version::series())[0]);

$notLatest = version_compare($latestVersion, Version::id(), '>');
$notLatestCompatible = version_compare($latestCompatibleVersion, Version::id(), '>');

if ($notLatest || $notLatestCompatible) {
print 'You are not using the latest version of PHPUnit.' . PHP_EOL;
} else {
print 'You are using the latest version of PHPUnit.' . PHP_EOL;
}

if ($notLatestCompatible) {
printf(
'The latest version compatible with PHPUnit %s is PHPUnit %s.' . PHP_EOL,
Version::id(),
$latestCompatibleVersion,
);
}

if ($notLatest) {
printf(
'The latest version is PHPUnit %s.' . PHP_EOL,
$latestVersion,
);
}

exit(TestRunner::SUCCESS_EXIT);
}




protected function showHelp(): void
{
$this->printVersionString();
(new Help)->writeToConsole();
}




protected function handleCustomTestSuite(): void
{
}

private function printVersionString(): void
{
if ($this->versionStringPrinted) {
return;
}

print Version::getVersionString() . PHP_EOL . PHP_EOL;

$this->versionStringPrinted = true;
}

private function exitWithErrorMessage(string $message): void
{
$this->printVersionString();

print $message . PHP_EOL;

exit(TestRunner::FAILURE_EXIT);
}

private function handleListGroups(TestSuite $suite, bool $exit): int
{
$this->printVersionString();

$this->warnAboutConflictingOptions(
'listGroups',
[
'filter',
'groups',
'excludeGroups',
'testsuite',
],
);

print 'Available test group(s):' . PHP_EOL;

$groups = $suite->getGroups();
sort($groups);

foreach ($groups as $group) {
if (strpos($group, '__phpunit_') === 0) {
continue;
}

printf(
' - %s' . PHP_EOL,
$group,
);
}

if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}

return TestRunner::SUCCESS_EXIT;
}





private function handleListSuites(bool $exit): int
{
$this->printVersionString();

$this->warnAboutConflictingOptions(
'listSuites',
[
'filter',
'groups',
'excludeGroups',
'testsuite',
],
);

print 'Available test suite(s):' . PHP_EOL;

foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) {
printf(
' - %s' . PHP_EOL,
$testSuite->name(),
);
}

if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}

return TestRunner::SUCCESS_EXIT;
}




private function handleListTests(TestSuite $suite, bool $exit): int
{
$this->printVersionString();

$this->warnAboutConflictingOptions(
'listTests',
[
'filter',
'groups',
'excludeGroups',
],
);

$renderer = new TextTestListRenderer;

print $renderer->render($suite);

if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}

return TestRunner::SUCCESS_EXIT;
}




private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int
{
$this->printVersionString();

$this->warnAboutConflictingOptions(
'listTestsXml',
[
'filter',
'groups',
'excludeGroups',
],
);

$renderer = new XmlTestListRenderer;

file_put_contents($target, $renderer->render($suite));

printf(
'Wrote list of tests that would have been run to %s' . PHP_EOL,
$target,
);

if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}

return TestRunner::SUCCESS_EXIT;
}

private function generateConfiguration(): void
{
$this->printVersionString();

print 'Generating phpunit.xml in ' . getcwd() . PHP_EOL . PHP_EOL;
print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): ';

$bootstrapScript = trim(fgets(STDIN));

print 'Tests directory (relative to path shown above; default: tests): ';

$testsDirectory = trim(fgets(STDIN));

print 'Source directory (relative to path shown above; default: src): ';

$src = trim(fgets(STDIN));

print 'Cache directory (relative to path shown above; default: .phpunit.cache): ';

$cacheDirectory = trim(fgets(STDIN));

if ($bootstrapScript === '') {
$bootstrapScript = 'vendor/autoload.php';
}

if ($testsDirectory === '') {
$testsDirectory = 'tests';
}

if ($src === '') {
$src = 'src';
}

if ($cacheDirectory === '') {
$cacheDirectory = '.phpunit.cache';
}

$generator = new Generator;

file_put_contents(
'phpunit.xml',
$generator->generateDefaultConfiguration(
Version::series(),
$bootstrapScript,
$testsDirectory,
$src,
$cacheDirectory,
),
);

print PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . PHP_EOL;
print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . PHP_EOL;

exit(TestRunner::SUCCESS_EXIT);
}

private function migrateConfiguration(string $filename): void
{
$this->printVersionString();

$result = (new SchemaDetector)->detect($filename);

if (!$result->detected()) {
print $filename . ' does not validate against any known schema.' . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}


if ($result->version() === Version::series()) {
print $filename . ' does not need to be migrated.' . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}

copy($filename, $filename . '.bak');

print 'Created backup:         ' . $filename . '.bak' . PHP_EOL;

try {
file_put_contents(
$filename,
(new Migrator)->migrate($filename),
);

print 'Migrated configuration: ' . $filename . PHP_EOL;
} catch (Throwable $t) {
print 'Migration failed: ' . $t->getMessage() . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}

exit(TestRunner::SUCCESS_EXIT);
}

private function handleCustomOptions(array $unrecognizedOptions): void
{
foreach ($unrecognizedOptions as $name => $value) {
if (isset($this->longOptions[$name])) {
$handler = $this->longOptions[$name];
}

$name .= '=';

if (isset($this->longOptions[$name])) {
$handler = $this->longOptions[$name];
}

if (isset($handler) && is_callable([$this, $handler])) {
$this->{$handler}($value);

unset($handler);
}
}
}

private function handleWarmCoverageCache(XmlConfiguration\Configuration $configuration): void
{
$this->printVersionString();

if (isset($this->arguments['coverageCacheDirectory'])) {
$cacheDirectory = $this->arguments['coverageCacheDirectory'];
} elseif ($configuration->codeCoverage()->hasCacheDirectory()) {
$cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path();
} else {
print 'Cache for static analysis has not been configured' . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}

$filter = new Filter;

if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) {
(new FilterMapper)->map(
$filter,
$configuration->codeCoverage(),
);
} elseif (isset($this->arguments['coverageFilter'])) {
if (!is_array($this->arguments['coverageFilter'])) {
$coverageFilterDirectories = [$this->arguments['coverageFilter']];
} else {
$coverageFilterDirectories = $this->arguments['coverageFilter'];
}

foreach ($coverageFilterDirectories as $coverageFilterDirectory) {
$filter->includeDirectory($coverageFilterDirectory);
}
} else {
print 'Filter for code coverage has not been configured' . PHP_EOL;

exit(TestRunner::EXCEPTION_EXIT);
}

$timer = new Timer;
$timer->start();

print 'Warming cache for static analysis ... ';

(new CacheWarmer)->warmCache(
$cacheDirectory,
!$configuration->codeCoverage()->disableCodeCoverageIgnore(),
$configuration->codeCoverage()->ignoreDeprecatedCodeUnits(),
$filter,
);

print 'done [' . $timer->stop()->asString() . ']' . PHP_EOL;

exit(TestRunner::SUCCESS_EXIT);
}

private function configurationFileInDirectory(string $directory): ?string
{
$candidates = [
$directory . '/phpunit.xml',
$directory . '/phpunit.xml.dist',
];

foreach ($candidates as $candidate) {
if (is_file($candidate)) {
return realpath($candidate);
}
}

return null;
}





private function warnAboutConflictingOptions(string $key, array $keys): void
{
$warningPrinted = false;

foreach ($keys as $_key) {
if (!empty($this->arguments[$_key])) {
printf(
'The %s and %s options cannot be combined, %s is ignored' . PHP_EOL,
$this->mapKeyToOptionForWarning($_key),
$this->mapKeyToOptionForWarning($key),
$this->mapKeyToOptionForWarning($_key),
);

$warningPrinted = true;
}
}

if ($warningPrinted) {
print PHP_EOL;
}
}




private function mapKeyToOptionForWarning(string $key): string
{
switch ($key) {
case 'listGroups':
return '--list-groups';

case 'listSuites':
return '--list-suites';

case 'listTests':
return '--list-tests';

case 'listTestsXml':
return '--list-tests-xml';

case 'filter':
return '--filter';

case 'groups':
return '--group';

case 'excludeGroups':
return '--exclude-group';

case 'testsuite':
return '--testsuite';
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use const PHP_VERSION;
use function explode;
use function in_array;
use function is_dir;
use function is_file;
use function strpos;
use function version_compare;
use PHPUnit\Framework\Exception as FrameworkException;
use PHPUnit\Framework\TestSuite as TestSuiteObject;
use PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection;
use SebastianBergmann\FileIterator\Facade;




final class TestSuiteMapper
{





public function map(TestSuiteCollection $configuration, string $filter): TestSuiteObject
{
try {
$filterAsArray = $filter ? explode(',', $filter) : [];
$result = new TestSuiteObject;

foreach ($configuration as $testSuiteConfiguration) {
if (!empty($filterAsArray) && !in_array($testSuiteConfiguration->name(), $filterAsArray, true)) {
continue;
}

$testSuite = new TestSuiteObject($testSuiteConfiguration->name());
$testSuiteEmpty = true;

$exclude = [];

foreach ($testSuiteConfiguration->exclude()->asArray() as $file) {
$exclude[] = $file->path();
}

foreach ($testSuiteConfiguration->directories() as $directory) {
if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) {
continue;
}

$files = (new Facade)->getFilesAsArray(
$directory->path(),
$directory->suffix(),
$directory->prefix(),
$exclude,
);

if (!empty($files)) {
$testSuite->addTestFiles($files);

$testSuiteEmpty = false;
} elseif (strpos($directory->path(), '*') === false && !is_dir($directory->path())) {
throw new TestDirectoryNotFoundException($directory->path());
}
}

foreach ($testSuiteConfiguration->files() as $file) {
if (!is_file($file->path())) {
throw new TestFileNotFoundException($file->path());
}

if (!version_compare(PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) {
continue;
}

$testSuite->addTestFile($file->path());

$testSuiteEmpty = false;
}

if (!$testSuiteEmpty) {
$result->addTest($testSuite);
}
}

return $result;
} catch (FrameworkException $e) {
throw new RuntimeException(
$e->getMessage(),
$e->getCode(),
$e,
);
}
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use RuntimeException;




final class ReflectionException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use function sprintf;
use RuntimeException;




final class TestDirectoryNotFoundException extends RuntimeException implements Exception
{
public function __construct(string $path)
{
parent::__construct(
sprintf(
'Test directory "%s" not found',
$path,
),
);
}
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use Throwable;




interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;




final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace PHPUnit\TextUI;

use function sprintf;
use RuntimeException;




final class TestFileNotFoundException extends RuntimeException implements Exception
{
public function __construct(string $path)
{
parent::__construct(
sprintf(
'Test file "%s" not found',
$path,
),
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Template;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Template;

use InvalidArgumentException;

final class RuntimeException extends InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Template;

final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\Template;

use function array_merge;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function sprintf;
use function str_replace;

final class Template
{



private $template = '';




private $openDelimiter;




private $closeDelimiter;




private $values = [];




public function __construct(string $file = '', string $openDelimiter = '{', string $closeDelimiter = '}')
{
$this->setFile($file);

$this->openDelimiter = $openDelimiter;
$this->closeDelimiter = $closeDelimiter;
}




public function setFile(string $file): void
{
$distFile = $file . '.dist';

if (file_exists($file)) {
$this->template = file_get_contents($file);
} elseif (file_exists($distFile)) {
$this->template = file_get_contents($distFile);
} else {
throw new InvalidArgumentException(
sprintf(
'Failed to load template "%s"',
$file
)
);
}
}

public function setVar(array $values, bool $merge = true): void
{
if (!$merge || empty($this->values)) {
$this->values = $values;
} else {
$this->values = array_merge($this->values, $values);
}
}

public function render(): string
{
$keys = [];

foreach ($this->values as $key => $value) {
$keys[] = $this->openDelimiter . $key . $this->closeDelimiter;
}

return str_replace($keys, $this->values, $this->template);
}




public function renderTo(string $target): void
{
if (!file_put_contents($target, $this->render())) {
throw new RuntimeException(
sprintf(
'Writing rendered result to "%s" failed',
$target
)
);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use function array_diff;
use function array_diff_key;
use function array_flip;
use function array_keys;
use function array_merge;
use function array_unique;
use function array_values;
use function count;
use function explode;
use function get_class;
use function is_array;
use function sort;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Test;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Node\Builder;
use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingFileAnalyser;
use SebastianBergmann\CodeUnitReverseLookup\Wizard;




final class CodeCoverage
{
private const UNCOVERED_FILES = 'UNCOVERED_FILES';




private $driver;




private $filter;




private $wizard;




private $checkForUnintentionallyCoveredCode = false;




private $includeUncoveredFiles = true;




private $processUncoveredFiles = false;




private $ignoreDeprecatedCode = false;




private $currentId;






private $data;




private $useAnnotationsForIgnoringCode = true;






private $tests = [];




private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = [];




private $analyser;




private $cacheDirectory;




private $cachedReport;

public function __construct(Driver $driver, Filter $filter)
{
$this->driver = $driver;
$this->filter = $filter;
$this->data = new ProcessedCodeCoverageData;
$this->wizard = new Wizard;
}




public function getReport(): Directory
{
if ($this->cachedReport === null) {
$this->cachedReport = (new Builder($this->analyser()))->build($this);
}

return $this->cachedReport;
}




public function clear(): void
{
$this->currentId = null;
$this->data = new ProcessedCodeCoverageData;
$this->tests = [];
$this->cachedReport = null;
}




public function clearCache(): void
{
$this->cachedReport = null;
}




public function filter(): Filter
{
return $this->filter;
}




public function getData(bool $raw = false): ProcessedCodeCoverageData
{
if (!$raw) {
if ($this->processUncoveredFiles) {
$this->processUncoveredFilesFromFilter();
} elseif ($this->includeUncoveredFiles) {
$this->addUncoveredFilesFromFilter();
}
}

return $this->data;
}




public function setData(ProcessedCodeCoverageData $data): void
{
$this->data = $data;
}




public function getTests(): array
{
return $this->tests;
}




public function setTests(array $tests): void
{
$this->tests = $tests;
}






public function start($id, bool $clear = false): void
{
if ($clear) {
$this->clear();
}

$this->currentId = $id;

$this->driver->start();

$this->cachedReport = null;
}






public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = []): RawCodeCoverageData
{
if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) {
throw new InvalidArgumentException(
'$linesToBeCovered must be an array or false'
);
}

$data = $this->driver->stop();
$this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed);

$this->currentId = null;
$this->cachedReport = null;

return $data;
}











public function append(RawCodeCoverageData $rawData, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = []): void
{
if ($id === null) {
$id = $this->currentId;
}

if ($id === null) {
throw new TestIdMissingException;
}

$this->cachedReport = null;

$this->applyFilter($rawData);

$this->applyExecutableLinesFilter($rawData);

if ($this->useAnnotationsForIgnoringCode) {
$this->applyIgnoredLinesFilter($rawData);
}

$this->data->initializeUnseenData($rawData);

if (!$append) {
return;
}

if ($id !== self::UNCOVERED_FILES) {
$this->applyCoversAnnotationFilter(
$rawData,
$linesToBeCovered,
$linesToBeUsed
);

if (empty($rawData->lineCoverage())) {
return;
}

$size = 'unknown';
$status = -1;
$fromTestcase = false;

if ($id instanceof TestCase) {
$fromTestcase = true;
$_size = $id->getSize();

if ($_size === Test::SMALL) {
$size = 'small';
} elseif ($_size === Test::MEDIUM) {
$size = 'medium';
} elseif ($_size === Test::LARGE) {
$size = 'large';
}

$status = $id->getStatus();
$id = get_class($id) . '::' . $id->getName();
} elseif ($id instanceof PhptTestCase) {
$fromTestcase = true;
$size = 'large';
$id = $id->getName();
}

$this->tests[$id] = ['size' => $size, 'status' => $status, 'fromTestcase' => $fromTestcase];

$this->data->markCodeAsExecutedByTestCase($id, $rawData);
}
}




public function merge(self $that): void
{
$this->filter->includeFiles(
$that->filter()->files()
);

$this->data->merge($that->data);

$this->tests = array_merge($this->tests, $that->getTests());

$this->cachedReport = null;
}

public function enableCheckForUnintentionallyCoveredCode(): void
{
$this->checkForUnintentionallyCoveredCode = true;
}

public function disableCheckForUnintentionallyCoveredCode(): void
{
$this->checkForUnintentionallyCoveredCode = false;
}

public function includeUncoveredFiles(): void
{
$this->includeUncoveredFiles = true;
}

public function excludeUncoveredFiles(): void
{
$this->includeUncoveredFiles = false;
}

public function processUncoveredFiles(): void
{
$this->processUncoveredFiles = true;
}

public function doNotProcessUncoveredFiles(): void
{
$this->processUncoveredFiles = false;
}

public function enableAnnotationsForIgnoringCode(): void
{
$this->useAnnotationsForIgnoringCode = true;
}

public function disableAnnotationsForIgnoringCode(): void
{
$this->useAnnotationsForIgnoringCode = false;
}

public function ignoreDeprecatedCode(): void
{
$this->ignoreDeprecatedCode = true;
}

public function doNotIgnoreDeprecatedCode(): void
{
$this->ignoreDeprecatedCode = false;
}




public function cachesStaticAnalysis(): bool
{
return $this->cacheDirectory !== null;
}

public function cacheStaticAnalysis(string $directory): void
{
$this->cacheDirectory = $directory;
}

public function doNotCacheStaticAnalysis(): void
{
$this->cacheDirectory = null;
}




public function cacheDirectory(): string
{
if (!$this->cachesStaticAnalysis()) {
throw new StaticAnalysisCacheNotConfiguredException(
'The static analysis cache is not configured'
);
}

return $this->cacheDirectory;
}




public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className): void
{
$this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck[] = $className;
}

public function enableBranchAndPathCoverage(): void
{
$this->driver->enableBranchAndPathCoverage();
}

public function disableBranchAndPathCoverage(): void
{
$this->driver->disableBranchAndPathCoverage();
}

public function collectsBranchAndPathCoverage(): bool
{
return $this->driver->collectsBranchAndPathCoverage();
}

public function detectsDeadCode(): bool
{
return $this->driver->detectsDeadCode();
}









private function applyCoversAnnotationFilter(RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed): void
{
if ($linesToBeCovered === false) {
$rawData->clear();

return;
}

if (empty($linesToBeCovered)) {
return;
}

if ($this->checkForUnintentionallyCoveredCode &&
(!$this->currentId instanceof TestCase ||
(!$this->currentId->isMedium() && !$this->currentId->isLarge()))) {
$this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed);
}

$rawLineData = $rawData->lineCoverage();
$filesWithNoCoverage = array_diff_key($rawLineData, $linesToBeCovered);

foreach (array_keys($filesWithNoCoverage) as $fileWithNoCoverage) {
$rawData->removeCoverageDataForFile($fileWithNoCoverage);
}

if (is_array($linesToBeCovered)) {
foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) {
$rawData->keepLineCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
$rawData->keepFunctionCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
}
}
}

private function applyFilter(RawCodeCoverageData $data): void
{
if ($this->filter->isEmpty()) {
return;
}

foreach (array_keys($data->lineCoverage()) as $filename) {
if ($this->filter->isExcluded($filename)) {
$data->removeCoverageDataForFile($filename);
}
}
}

private function applyExecutableLinesFilter(RawCodeCoverageData $data): void
{
foreach (array_keys($data->lineCoverage()) as $filename) {
if (!$this->filter->isFile($filename)) {
continue;
}

$linesToBranchMap = $this->analyser()->executableLinesIn($filename);

$data->keepLineCoverageDataOnlyForLines(
$filename,
array_keys($linesToBranchMap)
);

$data->markExecutableLineByBranch(
$filename,
$linesToBranchMap
);
}
}

private function applyIgnoredLinesFilter(RawCodeCoverageData $data): void
{
foreach (array_keys($data->lineCoverage()) as $filename) {
if (!$this->filter->isFile($filename)) {
continue;
}

$data->removeCoverageDataForLines(
$filename,
$this->analyser()->ignoredLinesFor($filename)
);
}
}




private function addUncoveredFilesFromFilter(): void
{
$uncoveredFiles = array_diff(
$this->filter->files(),
$this->data->coveredFiles()
);

foreach ($uncoveredFiles as $uncoveredFile) {
if ($this->filter->isFile($uncoveredFile)) {
$this->append(
RawCodeCoverageData::fromUncoveredFile(
$uncoveredFile,
$this->analyser()
),
self::UNCOVERED_FILES
);
}
}
}




private function processUncoveredFilesFromFilter(): void
{
$uncoveredFiles = array_diff(
$this->filter->files(),
$this->data->coveredFiles()
);

$this->driver->start();

foreach ($uncoveredFiles as $uncoveredFile) {
if ($this->filter->isFile($uncoveredFile)) {
include_once $uncoveredFile;
}
}

$this->append($this->driver->stop(), self::UNCOVERED_FILES);
}





private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed): void
{
$allowedLines = $this->getAllowedLines(
$linesToBeCovered,
$linesToBeUsed
);

$unintentionallyCoveredUnits = [];

foreach ($data->lineCoverage() as $file => $_data) {
foreach ($_data as $line => $flag) {
if ($flag === 1 && !isset($allowedLines[$file][$line])) {
$unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line);
}
}
}

$unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits);

if (!empty($unintentionallyCoveredUnits)) {
throw new UnintentionallyCoveredCodeException(
$unintentionallyCoveredUnits
);
}
}

private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array
{
$allowedLines = [];

foreach (array_keys($linesToBeCovered) as $file) {
if (!isset($allowedLines[$file])) {
$allowedLines[$file] = [];
}

$allowedLines[$file] = array_merge(
$allowedLines[$file],
$linesToBeCovered[$file]
);
}

foreach (array_keys($linesToBeUsed) as $file) {
if (!isset($allowedLines[$file])) {
$allowedLines[$file] = [];
}

$allowedLines[$file] = array_merge(
$allowedLines[$file],
$linesToBeUsed[$file]
);
}

foreach (array_keys($allowedLines) as $file) {
$allowedLines[$file] = array_flip(
array_unique($allowedLines[$file])
);
}

return $allowedLines;
}




private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits): array
{
$unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits);
sort($unintentionallyCoveredUnits);

foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) {
$unit = explode('::', $unintentionallyCoveredUnits[$k]);

if (count($unit) !== 2) {
continue;
}

try {
$class = new ReflectionClass($unit[0]);

foreach ($this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck as $parentClass) {
if ($class->isSubclassOf($parentClass)) {
unset($unintentionallyCoveredUnits[$k]);

break;
}
}
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e
);
}
}

return array_values($unintentionallyCoveredUnits);
}

private function analyser(): FileAnalyser
{
if ($this->analyser !== null) {
return $this->analyser;
}

$this->analyser = new ParsingFileAnalyser(
$this->useAnnotationsForIgnoringCode,
$this->ignoreDeprecatedCode
);

if ($this->cachesStaticAnalysis()) {
$this->analyser = new CachingFileAnalyser(
$this->cacheDirectory,
$this->analyser,
$this->useAnnotationsForIgnoringCode,
$this->ignoreDeprecatedCode
);
}

return $this->analyser;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use function array_key_exists;
use function array_keys;
use function array_merge;
use function array_unique;
use function count;
use function is_array;
use function ksort;
use SebastianBergmann\CodeCoverage\Driver\Driver;




final class ProcessedCodeCoverageData
{






private $lineCoverage = [];








private $functionCoverage = [];

public function initializeUnseenData(RawCodeCoverageData $rawData): void
{
foreach ($rawData->lineCoverage() as $file => $lines) {
if (!isset($this->lineCoverage[$file])) {
$this->lineCoverage[$file] = [];

foreach ($lines as $k => $v) {
$this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : [];
}
}
}

foreach ($rawData->functionCoverage() as $file => $functions) {
foreach ($functions as $functionName => $functionData) {
if (isset($this->functionCoverage[$file][$functionName])) {
$this->initPreviouslySeenFunction($file, $functionName, $functionData);
} else {
$this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
}
}
}
}

public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode): void
{
foreach ($executedCode->lineCoverage() as $file => $lines) {
foreach ($lines as $k => $v) {
if ($v === Driver::LINE_EXECUTED) {
$this->lineCoverage[$file][$k][] = $testCaseId;
}
}
}

foreach ($executedCode->functionCoverage() as $file => $functions) {
foreach ($functions as $functionName => $functionData) {
foreach ($functionData['branches'] as $branchId => $branchData) {
if ($branchData['hit'] === Driver::BRANCH_HIT) {
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId;
}
}

foreach ($functionData['paths'] as $pathId => $pathData) {
if ($pathData['hit'] === Driver::BRANCH_HIT) {
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId;
}
}
}
}
}

public function setLineCoverage(array $lineCoverage): void
{
$this->lineCoverage = $lineCoverage;
}

public function lineCoverage(): array
{
ksort($this->lineCoverage);

return $this->lineCoverage;
}

public function setFunctionCoverage(array $functionCoverage): void
{
$this->functionCoverage = $functionCoverage;
}

public function functionCoverage(): array
{
ksort($this->functionCoverage);

return $this->functionCoverage;
}

public function coveredFiles(): array
{
ksort($this->lineCoverage);

return array_keys($this->lineCoverage);
}

public function renameFile(string $oldFile, string $newFile): void
{
$this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile];

if (isset($this->functionCoverage[$oldFile])) {
$this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile];
}

unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]);
}

public function merge(self $newData): void
{
foreach ($newData->lineCoverage as $file => $lines) {
if (!isset($this->lineCoverage[$file])) {
$this->lineCoverage[$file] = $lines;

continue;
}


$compareLineNumbers = array_unique(
array_merge(
array_keys($this->lineCoverage[$file]),
array_keys($newData->lineCoverage[$file])
)
);

foreach ($compareLineNumbers as $line) {
$thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line);
$thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line);

if ($thatPriority > $thisPriority) {
$this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line];
} elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) {
$this->lineCoverage[$file][$line] = array_unique(
array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])
);
}
}
}

foreach ($newData->functionCoverage as $file => $functions) {
if (!isset($this->functionCoverage[$file])) {
$this->functionCoverage[$file] = $functions;

continue;
}

foreach ($functions as $functionName => $functionData) {
if (isset($this->functionCoverage[$file][$functionName])) {
$this->initPreviouslySeenFunction($file, $functionName, $functionData);
} else {
$this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
}

foreach ($functionData['branches'] as $branchId => $branchData) {
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit']));
}

foreach ($functionData['paths'] as $pathId => $pathData) {
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit']));
}
}
}
}











private function priorityForLine(array $data, int $line): int
{
if (!array_key_exists($line, $data)) {
return 1;
}

if (is_array($data[$line]) && count($data[$line]) === 0) {
return 2;
}

if ($data[$line] === null) {
return 3;
}

return 4;
}




private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void
{
$this->functionCoverage[$file][$functionName] = $functionData;

foreach (array_keys($functionData['branches']) as $branchId) {
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
}

foreach (array_keys($functionData['paths']) as $pathId) {
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
}
}






private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void
{
foreach ($functionData['branches'] as $branchId => $branchData) {
if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) {
$this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData;
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
}
}

foreach ($functionData['paths'] as $pathId => $pathData) {
if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) {
$this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData;
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
}
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use function dirname;
use SebastianBergmann\Version as VersionId;

final class Version
{



private static $version;

public static function id(): string
{
if (self::$version === null) {
self::$version = (new VersionId('9.2.31', dirname(__DIR__)))->getVersion();
}

return self::$version;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use function array_diff;
use function array_diff_key;
use function array_flip;
use function array_intersect;
use function array_intersect_key;
use function count;
use function explode;
use function file_get_contents;
use function in_array;
use function is_file;
use function range;
use function trim;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;




final class RawCodeCoverageData
{



private static $emptyLineCache = [];






private $lineCoverage;






private $functionCoverage;

public static function fromXdebugWithoutPathCoverage(array $rawCoverage): self
{
return new self($rawCoverage, []);
}

public static function fromXdebugWithPathCoverage(array $rawCoverage): self
{
$lineCoverage = [];
$functionCoverage = [];

foreach ($rawCoverage as $file => $fileCoverageData) {
$lineCoverage[$file] = $fileCoverageData['lines'];
$functionCoverage[$file] = $fileCoverageData['functions'];
}

return new self($lineCoverage, $functionCoverage);
}

public static function fromXdebugWithMixedCoverage(array $rawCoverage): self
{
$lineCoverage = [];
$functionCoverage = [];

foreach ($rawCoverage as $file => $fileCoverageData) {
if (!isset($fileCoverageData['functions'])) {


$lineCoverage[$file] = $fileCoverageData;

continue;
}

$lineCoverage[$file] = $fileCoverageData['lines'];
$functionCoverage[$file] = $fileCoverageData['functions'];
}

return new self($lineCoverage, $functionCoverage);
}

public static function fromUncoveredFile(string $filename, FileAnalyser $analyser): self
{
$lineCoverage = [];

foreach ($analyser->executableLinesIn($filename) as $line => $branch) {
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTED;
}

return new self([$filename => $lineCoverage], []);
}

private function __construct(array $lineCoverage, array $functionCoverage)
{
$this->lineCoverage = $lineCoverage;
$this->functionCoverage = $functionCoverage;

$this->skipEmptyLines();
}

public function clear(): void
{
$this->lineCoverage = $this->functionCoverage = [];
}

public function lineCoverage(): array
{
return $this->lineCoverage;
}

public function functionCoverage(): array
{
return $this->functionCoverage;
}

public function removeCoverageDataForFile(string $filename): void
{
unset($this->lineCoverage[$filename], $this->functionCoverage[$filename]);
}




public function keepLineCoverageDataOnlyForLines(string $filename, array $lines): void
{
if (!isset($this->lineCoverage[$filename])) {
return;
}

$this->lineCoverage[$filename] = array_intersect_key(
$this->lineCoverage[$filename],
array_flip($lines)
);
}




public function markExecutableLineByBranch(string $filename, array $linesToBranchMap): void
{
if (!isset($this->lineCoverage[$filename])) {
return;
}

$linesByBranch = [];

foreach ($linesToBranchMap as $line => $branch) {
$linesByBranch[$branch][] = $line;
}

foreach ($this->lineCoverage[$filename] as $line => $lineStatus) {
if (!isset($linesToBranchMap[$line])) {
continue;
}

$branch = $linesToBranchMap[$line];

if (!isset($linesByBranch[$branch])) {
continue;
}

foreach ($linesByBranch[$branch] as $lineInBranch) {
$this->lineCoverage[$filename][$lineInBranch] = $lineStatus;
}

if (Driver::LINE_EXECUTED === $lineStatus) {
unset($linesByBranch[$branch]);
}
}
}




public function keepFunctionCoverageDataOnlyForLines(string $filename, array $lines): void
{
if (!isset($this->functionCoverage[$filename])) {
return;
}

foreach ($this->functionCoverage[$filename] as $functionName => $functionData) {
foreach ($functionData['branches'] as $branchId => $branch) {
if (count(array_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) {
unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]);

foreach ($functionData['paths'] as $pathId => $path) {
if (in_array($branchId, $path['path'], true)) {
unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]);
}
}
}
}
}
}




public function removeCoverageDataForLines(string $filename, array $lines): void
{
if (empty($lines)) {
return;
}

if (!isset($this->lineCoverage[$filename])) {
return;
}

$this->lineCoverage[$filename] = array_diff_key(
$this->lineCoverage[$filename],
array_flip($lines)
);

if (isset($this->functionCoverage[$filename])) {
foreach ($this->functionCoverage[$filename] as $functionName => $functionData) {
foreach ($functionData['branches'] as $branchId => $branch) {
if (count(array_intersect($lines, range($branch['line_start'], $branch['line_end']))) > 0) {
unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]);

foreach ($functionData['paths'] as $pathId => $path) {
if (in_array($branchId, $path['path'], true)) {
unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]);
}
}
}
}
}
}
}









private function skipEmptyLines(): void
{
foreach ($this->lineCoverage as $filename => $coverage) {
foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) {
unset($this->lineCoverage[$filename][$emptyLine]);
}
}
}

private function getEmptyLinesForFile(string $filename): array
{
if (!isset(self::$emptyLineCache[$filename])) {
self::$emptyLineCache[$filename] = [];

if (is_file($filename)) {
$sourceLines = explode("\n", file_get_contents($filename));

foreach ($sourceLines as $line => $source) {
if (trim($source) === '') {
self::$emptyLineCache[$filename][] = ($line + 1);
}
}
}
}

return self::$emptyLineCache[$filename];
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use const XDEBUG_CC_BRANCH_CHECK;
use const XDEBUG_CC_DEAD_CODE;
use const XDEBUG_CC_UNUSED;
use const XDEBUG_FILTER_CODE_COVERAGE;
use const XDEBUG_PATH_INCLUDE;
use function explode;
use function extension_loaded;
use function getenv;
use function in_array;
use function ini_get;
use function phpversion;
use function sprintf;
use function version_compare;
use function xdebug_get_code_coverage;
use function xdebug_set_filter;
use function xdebug_start_code_coverage;
use function xdebug_stop_code_coverage;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;




final class Xdebug3Driver extends Driver
{





public function __construct(Filter $filter)
{
if (!extension_loaded('xdebug')) {
throw new XdebugNotAvailableException;
}

if (version_compare(phpversion('xdebug'), '3', '<')) {
throw new WrongXdebugVersionException(
sprintf(
'This driver requires Xdebug 3 but version %s is loaded',
phpversion('xdebug')
)
);
}

$mode = getenv('XDEBUG_MODE');

if ($mode === false || $mode === '') {
$mode = ini_get('xdebug.mode');
}

if ($mode === false ||
!in_array('coverage', explode(',', $mode), true)) {
throw new Xdebug3NotEnabledException;
}

if (!$filter->isEmpty()) {
xdebug_set_filter(
XDEBUG_FILTER_CODE_COVERAGE,
XDEBUG_PATH_INCLUDE,
$filter->files()
);
}
}

public function canCollectBranchAndPathCoverage(): bool
{
return true;
}

public function canDetectDeadCode(): bool
{
return true;
}

public function start(): void
{
$flags = XDEBUG_CC_UNUSED;

if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) {
$flags |= XDEBUG_CC_DEAD_CODE;
}

if ($this->collectsBranchAndPathCoverage()) {
$flags |= XDEBUG_CC_BRANCH_CHECK;
}

xdebug_start_code_coverage($flags);
}

public function stop(): RawCodeCoverageData
{
$data = xdebug_get_code_coverage();

xdebug_stop_code_coverage();

if ($this->collectsBranchAndPathCoverage()) {
return RawCodeCoverageData::fromXdebugWithPathCoverage($data);
}

return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data);
}

public function nameAndVersion(): string
{
return 'Xdebug ' . phpversion('xdebug');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use const pcov\inclusive;
use function array_intersect;
use function extension_loaded;
use function pcov\clear;
use function pcov\collect;
use function pcov\start;
use function pcov\stop;
use function pcov\waiting;
use function phpversion;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;




final class PcovDriver extends Driver
{



private $filter;




public function __construct(Filter $filter)
{
if (!extension_loaded('pcov')) {
throw new PcovNotAvailableException;
}

$this->filter = $filter;
}

public function start(): void
{
start();
}

public function stop(): RawCodeCoverageData
{
stop();

$filesToCollectCoverageFor = waiting();
$collected = [];

if ($filesToCollectCoverageFor) {
if (!$this->filter->isEmpty()) {
$filesToCollectCoverageFor = array_intersect($filesToCollectCoverageFor, $this->filter->files());
}

$collected = collect(inclusive, $filesToCollectCoverageFor);

clear();
}

return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collected);
}

public function nameAndVersion(): string
{
return 'PCOV ' . phpversion('pcov');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use const XDEBUG_CC_BRANCH_CHECK;
use const XDEBUG_CC_DEAD_CODE;
use const XDEBUG_CC_UNUSED;
use const XDEBUG_FILTER_CODE_COVERAGE;
use const XDEBUG_PATH_INCLUDE;
use const XDEBUG_PATH_WHITELIST;
use function defined;
use function extension_loaded;
use function ini_get;
use function phpversion;
use function sprintf;
use function version_compare;
use function xdebug_get_code_coverage;
use function xdebug_set_filter;
use function xdebug_start_code_coverage;
use function xdebug_stop_code_coverage;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;




final class Xdebug2Driver extends Driver
{



private $pathCoverageIsMixedCoverage;






public function __construct(Filter $filter)
{
if (!extension_loaded('xdebug')) {
throw new XdebugNotAvailableException;
}

if (version_compare(phpversion('xdebug'), '3', '>=')) {
throw new WrongXdebugVersionException(
sprintf(
'This driver requires Xdebug 2 but version %s is loaded',
phpversion('xdebug')
)
);
}

if (!ini_get('xdebug.coverage_enable')) {
throw new Xdebug2NotEnabledException;
}

if (!$filter->isEmpty()) {
if (defined('XDEBUG_PATH_WHITELIST')) {
$listType = XDEBUG_PATH_WHITELIST;
} else {
$listType = XDEBUG_PATH_INCLUDE;
}

xdebug_set_filter(
XDEBUG_FILTER_CODE_COVERAGE,
$listType,
$filter->files()
);
}

$this->pathCoverageIsMixedCoverage = version_compare(phpversion('xdebug'), '2.9.6', '<');
}

public function canCollectBranchAndPathCoverage(): bool
{
return true;
}

public function canDetectDeadCode(): bool
{
return true;
}

public function start(): void
{
$flags = XDEBUG_CC_UNUSED;

if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) {
$flags |= XDEBUG_CC_DEAD_CODE;
}

if ($this->collectsBranchAndPathCoverage()) {
$flags |= XDEBUG_CC_BRANCH_CHECK;
}

xdebug_start_code_coverage($flags);
}

public function stop(): RawCodeCoverageData
{
$data = xdebug_get_code_coverage();

xdebug_stop_code_coverage();

if ($this->collectsBranchAndPathCoverage()) {
if ($this->pathCoverageIsMixedCoverage) {
return RawCodeCoverageData::fromXdebugWithMixedCoverage($data);
}

return RawCodeCoverageData::fromXdebugWithPathCoverage($data);
}

return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data);
}

public function nameAndVersion(): string
{
return 'Xdebug ' . phpversion('xdebug');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use function phpversion;
use function version_compare;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
use SebastianBergmann\Environment\Runtime;

final class Selector
{








public function forLineCoverage(Filter $filter): Driver
{
$runtime = new Runtime;

if ($runtime->hasPHPDBGCodeCoverage()) {
return new PhpdbgDriver;
}

if ($runtime->hasPCOV()) {
return new PcovDriver($filter);
}

if ($runtime->hasXdebug()) {
if (version_compare(phpversion('xdebug'), '3', '>=')) {
$driver = new Xdebug3Driver($filter);
} else {
$driver = new Xdebug2Driver($filter);
}

$driver->enableDeadCodeDetection();

return $driver;
}

throw new NoCodeCoverageDriverAvailableException;
}







public function forLineAndPathCoverage(Filter $filter): Driver
{
if ((new Runtime)->hasXdebug()) {
if (version_compare(phpversion('xdebug'), '3', '>=')) {
$driver = new Xdebug3Driver($filter);
} else {
$driver = new Xdebug2Driver($filter);
}

$driver->enableDeadCodeDetection();
$driver->enableBranchAndPathCoverage();

return $driver;
}

throw new NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use const PHP_SAPI;
use const PHP_VERSION;
use function array_diff;
use function array_keys;
use function array_merge;
use function get_included_files;
use function phpdbg_end_oplog;
use function phpdbg_get_executable;
use function phpdbg_start_oplog;
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;




final class PhpdbgDriver extends Driver
{



public function __construct()
{
if (PHP_SAPI !== 'phpdbg') {
throw new PhpdbgNotAvailableException;
}
}

public function start(): void
{
phpdbg_start_oplog();
}

public function stop(): RawCodeCoverageData
{
static $fetchedLines = [];

$dbgData = phpdbg_end_oplog();

if ($fetchedLines === []) {
$sourceLines = phpdbg_get_executable();
} else {
$newFiles = array_diff(get_included_files(), array_keys($fetchedLines));

$sourceLines = [];

if ($newFiles) {
$sourceLines = phpdbg_get_executable(['files' => $newFiles]);
}
}

foreach ($sourceLines as $file => $lines) {
foreach ($lines as $lineNo => $numExecuted) {
$sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED;
}
}

$fetchedLines = array_merge($fetchedLines, $sourceLines);

return RawCodeCoverageData::fromXdebugWithoutPathCoverage(
$this->detectExecutedLines($fetchedLines, $dbgData)
);
}

public function nameAndVersion(): string
{
return 'PHPDBG ' . PHP_VERSION;
}

private function detectExecutedLines(array $sourceLines, array $dbgData): array
{
foreach ($dbgData as $file => $coveredLines) {
foreach ($coveredLines as $lineNo => $numExecuted) {


if (isset($sourceLines[$file][$lineNo])) {
$sourceLines[$file][$lineNo] = self::LINE_EXECUTED;
}
}
}

return $sourceLines;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use function sprintf;
use SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException;
use SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;




abstract class Driver
{





public const LINE_NOT_EXECUTABLE = -2;






public const LINE_NOT_EXECUTED = -1;






public const LINE_EXECUTED = 1;






public const BRANCH_NOT_HIT = 0;






public const BRANCH_HIT = 1;




private $collectBranchAndPathCoverage = false;




private $detectDeadCode = false;











public static function forLineCoverage(Filter $filter): self
{
return (new Selector)->forLineCoverage($filter);
}









public static function forLineAndPathCoverage(Filter $filter): self
{
return (new Selector)->forLineAndPathCoverage($filter);
}

public function canCollectBranchAndPathCoverage(): bool
{
return false;
}

public function collectsBranchAndPathCoverage(): bool
{
return $this->collectBranchAndPathCoverage;
}




public function enableBranchAndPathCoverage(): void
{
if (!$this->canCollectBranchAndPathCoverage()) {
throw new BranchAndPathCoverageNotSupportedException(
sprintf(
'%s does not support branch and path coverage',
$this->nameAndVersion()
)
);
}

$this->collectBranchAndPathCoverage = true;
}

public function disableBranchAndPathCoverage(): void
{
$this->collectBranchAndPathCoverage = false;
}

public function canDetectDeadCode(): bool
{
return false;
}

public function detectsDeadCode(): bool
{
return $this->detectDeadCode;
}




public function enableDeadCodeDetection(): void
{
if (!$this->canDetectDeadCode()) {
throw new DeadCodeDetectionNotSupportedException(
sprintf(
'%s does not support dead code detection',
$this->nameAndVersion()
)
);
}

$this->detectDeadCode = true;
}

public function disableDeadCodeDetection(): void
{
$this->detectDeadCode = false;
}

abstract public function nameAndVersion(): string;

abstract public function start(): void;

abstract public function stop(): RawCodeCoverageData;
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Html;

use const DIRECTORY_SEPARATOR;
use function copy;
use function date;
use function dirname;
use function substr;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\InvalidArgumentException;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Util\Filesystem;

final class Facade
{



private $templatePath;




private $generator;




private $lowUpperBound;




private $highLowerBound;

public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, string $generator = '')
{
if ($lowUpperBound > $highLowerBound) {
throw new InvalidArgumentException(
'$lowUpperBound must not be larger than $highLowerBound'
);
}

$this->generator = $generator;
$this->highLowerBound = $highLowerBound;
$this->lowUpperBound = $lowUpperBound;
$this->templatePath = __DIR__ . '/Renderer/Template/';
}

public function process(CodeCoverage $coverage, string $target): void
{
$target = $this->directory($target);
$report = $coverage->getReport();
$date = date('D M j G:i:s T Y');

$dashboard = new Dashboard(
$this->templatePath,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound,
$coverage->collectsBranchAndPathCoverage()
);

$directory = new Directory(
$this->templatePath,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound,
$coverage->collectsBranchAndPathCoverage()
);

$file = new File(
$this->templatePath,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound,
$coverage->collectsBranchAndPathCoverage()
);

$directory->render($report, $target . 'index.html');
$dashboard->render($report, $target . 'dashboard.html');

foreach ($report as $node) {
$id = $node->id();

if ($node instanceof DirectoryNode) {
Filesystem::createDirectory($target . $id);

$directory->render($node, $target . $id . '/index.html');
$dashboard->render($node, $target . $id . '/dashboard.html');
} else {
$dir = dirname($target . $id);

Filesystem::createDirectory($dir);

$file->render($node, $target . $id);
}
}

$this->copyFiles($target);
}

private function copyFiles(string $target): void
{
$dir = $this->directory($target . '_css');

copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
copy($this->templatePath . 'css/style.css', $dir . 'style.css');
copy($this->templatePath . 'css/custom.css', $dir . 'custom.css');
copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css');

$dir = $this->directory($target . '_icons');
copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg');
copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg');

$dir = $this->directory($target . '_js');
copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js');
copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
copy($this->templatePath . 'js/file.js', $dir . 'file.js');
}

private function directory(string $directory): string
{
if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) {
$directory .= DIRECTORY_SEPARATOR;
}

Filesystem::createDirectory($directory);

return $directory;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Html;

use function array_pop;
use function count;
use function sprintf;
use function str_repeat;
use function substr_count;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\Environment\Runtime;
use SebastianBergmann\Template\Template;




abstract class Renderer
{



protected $templatePath;




protected $generator;




protected $date;




protected $lowUpperBound;




protected $highLowerBound;




protected $hasBranchCoverage;




protected $version;

public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound, bool $hasBranchCoverage)
{
$this->templatePath = $templatePath;
$this->generator = $generator;
$this->date = $date;
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
$this->version = Version::id();
$this->hasBranchCoverage = $hasBranchCoverage;
}

protected function renderItemTemplate(Template $template, array $data): string
{
$numSeparator = '&nbsp;/&nbsp;';

if (isset($data['numClasses']) && $data['numClasses'] > 0) {
$classesLevel = $this->colorLevel($data['testedClassesPercent']);

$classesNumber = $data['numTestedClasses'] . $numSeparator .
$data['numClasses'];

$classesBar = $this->coverageBar(
$data['testedClassesPercent']
);
} else {
$classesLevel = '';
$classesNumber = '0' . $numSeparator . '0';
$classesBar = '';
$data['testedClassesPercentAsString'] = 'n/a';
}

if ($data['numMethods'] > 0) {
$methodsLevel = $this->colorLevel($data['testedMethodsPercent']);

$methodsNumber = $data['numTestedMethods'] . $numSeparator .
$data['numMethods'];

$methodsBar = $this->coverageBar(
$data['testedMethodsPercent']
);
} else {
$methodsLevel = '';
$methodsNumber = '0' . $numSeparator . '0';
$methodsBar = '';
$data['testedMethodsPercentAsString'] = 'n/a';
}

if ($data['numExecutableLines'] > 0) {
$linesLevel = $this->colorLevel($data['linesExecutedPercent']);

$linesNumber = $data['numExecutedLines'] . $numSeparator .
$data['numExecutableLines'];

$linesBar = $this->coverageBar(
$data['linesExecutedPercent']
);
} else {
$linesLevel = '';
$linesNumber = '0' . $numSeparator . '0';
$linesBar = '';
$data['linesExecutedPercentAsString'] = 'n/a';
}

if ($data['numExecutablePaths'] > 0) {
$pathsLevel = $this->colorLevel($data['pathsExecutedPercent']);

$pathsNumber = $data['numExecutedPaths'] . $numSeparator .
$data['numExecutablePaths'];

$pathsBar = $this->coverageBar(
$data['pathsExecutedPercent']
);
} else {
$pathsLevel = '';
$pathsNumber = '0' . $numSeparator . '0';
$pathsBar = '';
$data['pathsExecutedPercentAsString'] = 'n/a';
}

if ($data['numExecutableBranches'] > 0) {
$branchesLevel = $this->colorLevel($data['branchesExecutedPercent']);

$branchesNumber = $data['numExecutedBranches'] . $numSeparator .
$data['numExecutableBranches'];

$branchesBar = $this->coverageBar(
$data['branchesExecutedPercent']
);
} else {
$branchesLevel = '';
$branchesNumber = '0' . $numSeparator . '0';
$branchesBar = '';
$data['branchesExecutedPercentAsString'] = 'n/a';
}

$template->setVar(
[
'icon' => $data['icon'] ?? '',
'crap' => $data['crap'] ?? '',
'name' => $data['name'],
'lines_bar' => $linesBar,
'lines_executed_percent' => $data['linesExecutedPercentAsString'],
'lines_level' => $linesLevel,
'lines_number' => $linesNumber,
'paths_bar' => $pathsBar,
'paths_executed_percent' => $data['pathsExecutedPercentAsString'],
'paths_level' => $pathsLevel,
'paths_number' => $pathsNumber,
'branches_bar' => $branchesBar,
'branches_executed_percent' => $data['branchesExecutedPercentAsString'],
'branches_level' => $branchesLevel,
'branches_number' => $branchesNumber,
'methods_bar' => $methodsBar,
'methods_tested_percent' => $data['testedMethodsPercentAsString'],
'methods_level' => $methodsLevel,
'methods_number' => $methodsNumber,
'classes_bar' => $classesBar,
'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '',
'classes_level' => $classesLevel,
'classes_number' => $classesNumber,
]
);

return $template->render();
}

protected function setCommonTemplateVariables(Template $template, AbstractNode $node): void
{
$template->setVar(
[
'id' => $node->id(),
'full_path' => $node->pathAsString(),
'path_to_root' => $this->pathToRoot($node),
'breadcrumbs' => $this->breadcrumbs($node),
'date' => $this->date,
'version' => $this->version,
'runtime' => $this->runtimeString(),
'generator' => $this->generator,
'low_upper_bound' => $this->lowUpperBound,
'high_lower_bound' => $this->highLowerBound,
]
);
}

protected function breadcrumbs(AbstractNode $node): string
{
$breadcrumbs = '';
$path = $node->pathAsArray();
$pathToRoot = [];
$max = count($path);

if ($node instanceof FileNode) {
$max--;
}

for ($i = 0; $i < $max; $i++) {
$pathToRoot[] = str_repeat('../', $i);
}

foreach ($path as $step) {
if ($step !== $node) {
$breadcrumbs .= $this->inactiveBreadcrumb(
$step,
array_pop($pathToRoot)
);
} else {
$breadcrumbs .= $this->activeBreadcrumb($step);
}
}

return $breadcrumbs;
}

protected function activeBreadcrumb(AbstractNode $node): string
{
$buffer = sprintf(
'         <li class="breadcrumb-item active">%s</li>' . "\n",
$node->name()
);

if ($node instanceof DirectoryNode) {
$buffer .= '         <li class="breadcrumb-item">(<a href="dashboard.html">Dashboard</a>)</li>' . "\n";
}

return $buffer;
}

protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string
{
return sprintf(
'         <li class="breadcrumb-item"><a href="%sindex.html">%s</a></li>' . "\n",
$pathToRoot,
$node->name()
);
}

protected function pathToRoot(AbstractNode $node): string
{
$id = $node->id();
$depth = substr_count($id, '/');

if ($id !== 'index' &&
$node instanceof DirectoryNode) {
$depth++;
}

return str_repeat('../', $depth);
}

protected function coverageBar(float $percent): string
{
$level = $this->colorLevel($percent);

$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html');
$template = new Template(
$templateName,
'{{',
'}}'
);

$template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]);

return $template->render();
}

protected function colorLevel(float $percent): string
{
if ($percent <= $this->lowUpperBound) {
return 'danger';
}

if ($percent > $this->lowUpperBound &&
$percent < $this->highLowerBound) {
return 'warning';
}

return 'success';
}

private function runtimeString(): string
{
$runtime = new Runtime;

return sprintf(
'<a href="%s" target="_top">%s %s</a>',
$runtime->getVendorUrl(),
$runtime->getName(),
$runtime->getVersion()
);
}
}
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Dashboard for {{full_path}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/nv.d3.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <header>
   <div class="container-fluid">
    <div class="row">
     <div class="col-md-12">
      <nav aria-label="breadcrumb">
       <ol class="breadcrumb">
{{breadcrumbs}}
       </ol>
      </nav>
     </div>
    </div>
   </div>
  </header>
  <div class="container-fluid">
   <div class="row">
    <div class="col-md-12">
     <h2>Classes</h2>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Coverage Distribution</h3>
     <div id="classCoverageDistribution" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Complexity</h3>
     <div id="classComplexity" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Insufficient Coverage</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Class</th>
         <th class="text-right">Coverage</th>
        </tr>
       </thead>
       <tbody>
{{insufficient_coverage_classes}}
       </tbody>
      </table>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Project Risks</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Class</th>
         <th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
        </tr>
       </thead>
       <tbody>
{{project_risks_classes}}
       </tbody>
      </table>
     </div>
    </div>
   </div>
   <div class="row">
    <div class="col-md-12">
     <h2>Methods</h2>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Coverage Distribution</h3>
     <div id="methodCoverageDistribution" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Complexity</h3>
     <div id="methodComplexity" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Insufficient Coverage</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Method</th>
         <th class="text-right">Coverage</th>
        </tr>
       </thead>
       <tbody>
{{insufficient_coverage_methods}}
       </tbody>
      </table>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Project Risks</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Method</th>
         <th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
        </tr>
       </thead>
       <tbody>
{{project_risks_methods}}
       </tbody>
      </table>
     </div>
    </div>
   </div>
   <footer>
    <hr/>
    <p>
     <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
    </p>
   </footer>
  </div>
  <script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/d3.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/nv.d3.min.js?v={{version}}" type="text/javascript"></script>
  <script type="text/javascript">
$(document).ready(function() {
  nv.addGraph(function() {
    var chart = nv.models.multiBarChart();
    chart.tooltips(false)
      .showControls(false)
      .showLegend(false)
      .reduceXTicks(false)
      .staggerLabels(true)
      .yAxis.tickFormat(d3.format('d'));

    d3.select('#classCoverageDistribution svg')
      .datum(getCoverageDistributionData({{class_coverage_distribution}}, "Class Coverage"))
      .transition().duration(500).call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  nv.addGraph(function() {
    var chart = nv.models.multiBarChart();
    chart.tooltips(false)
      .showControls(false)
      .showLegend(false)
      .reduceXTicks(false)
      .staggerLabels(true)
      .yAxis.tickFormat(d3.format('d'));

    d3.select('#methodCoverageDistribution svg')
      .datum(getCoverageDistributionData({{method_coverage_distribution}}, "Method Coverage"))
      .transition().duration(500).call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  function getCoverageDistributionData(data, label) {
    var labels = [
      '0%',
      '0-10%',
      '10-20%',
      '20-30%',
      '30-40%',
      '40-50%',
      '50-60%',
      '60-70%',
      '70-80%',
      '80-90%',
      '90-100%',
      '100%'
    ];
    var values = [];
    $.each(labels, function(key) {
      values.push({x: labels[key], y: data[key]});
    });

    return [
      {
        key: label,
        values: values,
        color: "#4572A7"
      }
    ];
  }
  nv.addGraph(function() {
    var chart = nv.models.scatterChart()
      .showDistX(true)
      .showDistY(true)
      .showLegend(false)
      .forceX([0, 100]);
    chart.tooltipContent(function(graph) {
      return '<p>' + graph.point.class + '</p>';
    });

    chart.xAxis.axisLabel('Code Coverage (in percent)');
    chart.yAxis.axisLabel('Cyclomatic Complexity');

    d3.select('#classComplexity svg')
      .datum(getComplexityData({{complexity_class}}, 'Class Complexity'))
      .transition()
      .duration(500)
      .call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  nv.addGraph(function() {
    var chart = nv.models.scatterChart()
      .showDistX(true)
      .showDistY(true)
      .showLegend(false)
      .forceX([0, 100]);
    chart.tooltipContent(function(graph) {
      return '<p>' + graph.point.class + '</p>';
    });

    chart.xAxis.axisLabel('Code Coverage (in percent)');
    chart.yAxis.axisLabel('Method Complexity');

    d3.select('#methodComplexity svg')
      .datum(getComplexityData({{complexity_method}}, 'Method Complexity'))
      .transition()
      .duration(500)
      .call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  function getComplexityData(data, label) {
    var values = [];
    $.each(data, function(key) {
      var value = Math.round(data[key][0]*100) / 100;
      values.push({
        x: value,
        y: data[key][1],
        class: data[key][2],
        size: 0.05,
        shape: 'diamond'
      });
    });

    return [
      {
        key: label,
        values: values,
        color: "#4572A7"
      }
    ];
  }
});
  </script>
 </body>
</html>
      <tr>
       <td class="{{lines_level}}">{{name}}</td>
       <td class="{{lines_level}} big">{{lines_bar}}</td>
       <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
       <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
       <td class="{{branches_level}} big">{{branches_bar}}</td>
       <td class="{{branches_level}} small"><div align="right">{{branches_executed_percent}}</div></td>
       <td class="{{branches_level}} small"><div align="right">{{branches_number}}</div></td>
       <td class="{{paths_level}} big">{{paths_bar}}</td>
       <td class="{{paths_level}} small"><div align="right">{{paths_executed_percent}}</div></td>
       <td class="{{paths_level}} small"><div align="right">{{paths_number}}</div></td>
       <td class="{{methods_level}} big">{{methods_bar}}</td>
       <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
       <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
       <td class="{{methods_level}} small">{{crap}}</td>
       <td class="{{classes_level}} big">{{classes_bar}}</td>
       <td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
       <td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
      </tr>

/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */
!function(){var a={};a.dev=!1,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.logs={},a.dom={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(a,b){console&&console.warn&&console.warn("nvd3 warning: `"+a+"` has been deprecated. ",b||"")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start();var c=function(){for(var d,e,f=0;b>f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+="<div class='footer'>"+a.footer+"</div>"),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;e<c.length;e+=1){var f=c[e]&&c[e].values?c[e].values.length:0;d=f>d?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)<d.range()[1]+10||d(a)>d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)<E[0]||d(a)>E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g");
x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f);
var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&C<B.length;)E[C]=B[C],D+=B[C++];for(0===C&&(C=1);D>p&&C>1;){E=[],C--;for(var F=0;F<B.length;F++)B[F]>(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,d<c.left+c.right+J+a&&(L=J=5,K+=A),L+=a,L>M&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open<c.close?"2ca02c":"d62728";return'<h3 style="color: #'+d+'">'+a.value+"</h3><table><tr><td>open:</td><td>"+b.yAxis.tickFormat()(c.open)+"</td></tr><tr><td>close:</td><td>"+b.yAxis.tickFormat()(c.close)+"</td></tr><tr><td>high</td><td>"+b.yAxis.tickFormat()(c.high)+"</td></tr><tr><td>low:</td><td>"+b.yAxis.tickFormat()(c.low)+"</td></tr></table>"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open<c.close?"2ca02c":"d62728";return'<h3 style="color: #'+d+'">'+a.value+"</h3><table><tr><td>open:</td><td>"+b.yAxis.tickFormat()(c.open)+"</td></tr><tr><td>close:</td><td>"+b.yAxis.tickFormat()(c.close)+"</td></tr><tr><td>high</td><td>"+b.yAxis.tickFormat()(c.high)+"</td></tr><tr><td>low:</td><td>"+b.yAxis.tickFormat()(c.low)+"</td></tr></table>"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&E<D.length;)F[E]=D[E],C+=D[E++];for(0===E&&(E=1);C>g&&E>1;){F=[],E--;for(var G=0;G<D.length;G++)D[G]>(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,d<c.left+c.right+K+a&&(M=K=5,L+=x),M+=a,M>N&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D]
}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();
var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left
}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;M<b[0].length;M++)I.push(K),J.push(L);else I=z.map(function(a){return(a.outer-a.outer/5)*H}),J=z.map(function(a){return(a.inner-a.inner/5)*H}),y=d3.min(z.map(function(a){return a.inner-a.inner/5}));a.utils.initSVG(i);var N=i.selectAll(".nv-wrap.nv-pie").data(b),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-pie nv-chart-"+h),P=O.append("g"),Q=N.select("g"),R=P.append("g").attr("class","nv-pie");P.append("g").attr("class","nv-pieLabels"),N.attr("transform","translate("+c.left+","+c.top+")"),Q.select(".nv-pie").attr("transform","translate("+F/2+","+G/2+")"),Q.select(".nv-pieLabels").attr("transform","translate("+F/2+","+G/2+")"),i.on("click",function(a,b){A.chartClick({data:a,index:b,pos:d3.event,id:h})}),B=[],C=[];for(var M=0;M<b[0].length;M++){var S=d3.svg.arc().outerRadius(I[M]),T=d3.svg.arc().outerRadius(I[M]+5);u!==!1&&(S.startAngle(u),T.startAngle(u)),w!==!1&&(S.endAngle(w),T.endAngle(w)),p&&(S.innerRadius(J[M]),T.innerRadius(J[M])),S.cornerRadius&&x&&(S.cornerRadius(x),T.cornerRadius(x)),B.push(S),C.push(T)}var U=d3.layout.pie().sort(null).value(function(a){return a.disabled?0:g(a)});U.padAngle&&v&&U.padAngle(v),p&&q&&(R.append("text").attr("class","nv-pie-title"),N.select(".nv-pie-title").style("text-anchor","middle").text(function(){return q}).style("font-size",Math.min(F,G)*y*2/(q.length+2)+"px").attr("dy","0.35em").attr("transform",function(){return"translate(0, "+s+")"}));var V=N.select(".nv-pie").selectAll(".nv-slice").data(U),W=N.select(".nv-pieLabels").selectAll(".nv-label").data(U);V.exit().remove(),W.exit().remove();var X=V.enter().append("g");X.attr("class","nv-slice"),X.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),r&&d3.select(this).select("path").transition().duration(70).attr("d",C[b]),A.elementMouseover({data:a.data,index:b,color:d3.select(this).style("fill")})}),X.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),r&&d3.select(this).select("path").transition().duration(50).attr("d",B[b]),A.elementMouseout({data:a.data,index:b})}),X.on("mousemove",function(a,b){A.elementMousemove({data:a.data,index:b})}),X.on("click",function(a,b){A.elementClick({data:a.data,index:b,color:d3.select(this).style("fill")})}),X.on("dblclick",function(a,b){A.elementDblClick({data:a.data,index:b,color:d3.select(this).style("fill")})}),V.attr("fill",function(a,b){return j(a.data,b)}),V.attr("stroke",function(a,b){return j(a.data,b)});X.append("path").each(function(a){this._current=a});if(V.select("path").transition().attr("d",function(a,b){return B[b](a)}).attrTween("d",E),l){for(var Y=[],M=0;M<b[0].length;M++)Y.push(B[M]),m?p&&(Y[M]=d3.svg.arc().outerRadius(B[M].outerRadius()),u!==!1&&Y[M].startAngle(u),w!==!1&&Y[M].endAngle(w)):p||Y[M].innerRadius(0);W.enter().append("g").classed("nv-label",!0).each(function(a){var b=d3.select(this);b.attr("transform",function(a,b){if(t){a.outerRadius=I[b]+10,a.innerRadius=I[b]+15;var c=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2<Math.PI?c-=90:c+=90,"translate("+Y[b].centroid(a)+") rotate("+c+")"}return a.outerRadius=H+10,a.innerRadius=H+15,"translate("+Y[b].centroid(a)+")"}),b.append("rect").style("stroke","#fff").style("fill","#fff").attr("rx",3).attr("ry",3),b.append("text").style("text-anchor",t?(a.startAngle+a.endAngle)/2<Math.PI?"start":"end":"middle").style("fill","#000")});var Z={},$=14,_=140,ab=function(a){return Math.floor(a[0]/_)*_+","+Math.floor(a[1]/$)*$};W.watchTransition(D,"pie labels").attr("transform",function(a,b){if(t){a.outerRadius=I[b]+10,a.innerRadius=I[b]+15;var c=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2<Math.PI?c-=90:c+=90,"translate("+Y[b].centroid(a)+") rotate("+c+")"}a.outerRadius=H+10,a.innerRadius=H+15;var d=Y[b].centroid(a);if(a.value){var e=ab(d);Z[e]&&(d[1]-=$),Z[ab(d)]=!0}return"translate("+d+")"}),W.select(".nv-label text").style("text-anchor",function(a){return t?(a.startAngle+a.endAngle)/2<Math.PI?"start":"end":"middle"}).text(function(a,b){var c=(a.endAngle-a.startAngle)/(2*Math.PI),d="";if(!a.value||o>c)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b)
}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;f<a.length;f++)Math.abs(e.x()(a[f],f)-b)<c&&(c=Math.abs(e.x()(a[f],f)-b),d=f);return d}if(!j){var b=d3.mouse(this)[0]-f.left;i=[a(p,Math.round(c.invert(b)))],q()}}var s=d3.select(this);a.utils.initSVG(s);var t=a.utils.availableWidth(g,s,f),u=a.utils.availableHeight(h,s,f);if(b.update=function(){s.call(b)},b.container=this,!p||!p.length)return a.utils.noData(b,s),b;s.selectAll(".nv-noData").remove();var v=e.y()(p[p.length-1],p.length-1);c=e.xScale(),d=e.yScale();var w=s.selectAll("g.nv-wrap.nv-sparklineplus").data([p]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-sparklineplus"),y=x.append("g"),z=w.select("g");y.append("g").attr("class","nv-sparklineWrap"),y.append("g").attr("class","nv-valueWrap"),y.append("g").attr("class","nv-hoverArea"),w.attr("transform","translate("+f.left+","+f.top+")");var A=z.select(".nv-sparklineWrap");if(e.width(t).height(u),A.call(e),m){var B=z.select(".nv-valueWrap"),C=B.selectAll(".nv-currentValue").data([v]);C.enter().append("text").attr("class","nv-currentValue").attr("dx",o?-8:8).attr("dy",".9em").style("text-anchor",o?"end":"start"),C.attr("x",t+(o?f.right:0)).attr("y",n?function(a){return d(a)}:0).style("fill",e.color()(p[p.length-1],p.length-1)).text(l(v))}y.select(".nv-hoverArea").append("rect").on("mousemove",r).on("click",function(){j=!j}).on("mouseout",function(){i=[],q()}),z.select(".nv-hoverArea rect").attr("transform",function(){return"translate("+-f.left+","+-f.top+")"}).attr("width",t+f.left+f.right).attr("height",u+f.top)}),b}var c,d,e=a.models.sparkline(),f={top:15,right:100,bottom:10,left:50},g=null,h=null,i=[],j=!1,k=d3.format(",r"),l=d3.format(",.2f"),m=!0,n=!0,o=!1,p=null;return b.sparkline=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},xTickFormat:{get:function(){return k},set:function(a){k=a}},yTickFormat:{get:function(){return l},set:function(a){l=a}},showLastValue:{get:function(){return m},set:function(a){m=a}},alignValue:{get:function(){return n},set:function(a){n=a}},rightAlignValue:{get:function(){return o},set:function(a){o=a}},noData:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.stackedArea=function(){"use strict";function b(m){return u.reset(),u.models(r),m.each(function(m){var s=f-e.left-e.right,v=g-e.top-e.bottom;j=d3.select(this),a.utils.initSVG(j),c=r.xScale(),d=r.yScale();var w=m;m.forEach(function(a,b){a.seriesIndex=b,a.values=a.values.map(function(a,c){return a.index=c,a.seriesIndex=b,a})});var x=m.filter(function(a){return!a.disabled});m=d3.layout.stack().order(o).offset(n).values(function(a){return a.values}).x(k).y(l).out(function(a,b,c){a.display={y:c,y0:b}})(x);var y=j.selectAll("g.nv-wrap.nv-stackedarea").data([m]),z=y.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedarea"),A=z.append("defs"),B=z.append("g"),C=y.select("g");B.append("g").attr("class","nv-areaWrap"),B.append("g").attr("class","nv-scatterWrap"),y.attr("transform","translate("+e.left+","+e.top+")"),0==r.forceY().length&&r.forceY().push(0),r.width(s).height(v).x(k).y(function(a){return a.display.y+a.display.y0}).forceY([0]).color(m.map(function(a){return a.color||h(a,a.seriesIndex)}));var D=C.select(".nv-scatterWrap").datum(m);D.call(r),A.append("clipPath").attr("id","nv-edge-clip-"+i).append("rect"),y.select("#nv-edge-clip-"+i+" rect").attr("width",s).attr("height",v),C.attr("clip-path",q?"url(#nv-edge-clip-"+i+")":"");var E=d3.svg.area().x(function(a,b){return c(k(a,b))}).y0(function(a){return d(a.display.y0)}).y1(function(a){return d(a.display.y+a.display.y0)}).interpolate(p),F=d3.svg.area().x(function(a,b){return c(k(a,b))}).y0(function(a){return d(a.display.y0)}).y1(function(a){return d(a.display.y0)}),G=C.select(".nv-areaWrap").selectAll("path.nv-area").data(function(a){return a});G.enter().append("path").attr("class",function(a,b){return"nv-area nv-area-"+b}).attr("d",function(a){return F(a.values,a.seriesIndex)}).on("mouseover",function(a){d3.select(this).classed("hover",!0),t.areaMouseover({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}).on("mouseout",function(a){d3.select(this).classed("hover",!1),t.areaMouseout({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}).on("click",function(a){d3.select(this).classed("hover",!1),t.areaClick({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}),G.exit().remove(),G.style("fill",function(a){return a.color||h(a,a.seriesIndex)}).style("stroke",function(a){return a.color||h(a,a.seriesIndex)}),G.watchTransition(u,"stackedArea path").attr("d",function(a,b){return E(a.values,b)}),r.dispatch.on("elementMouseover.area",function(a){C.select(".nv-chart-"+i+" .nv-area-"+a.seriesIndex).classed("hover",!0)}),r.dispatch.on("elementMouseout.area",function(a){C.select(".nv-chart-"+i+" .nv-area-"+a.seriesIndex).classed("hover",!1)}),b.d3_stackedOffset_stackPercent=function(a){var b,c,d,e=a.length,f=a[0].length,g=[];for(c=0;f>c;++c){for(b=0,d=0;b<w.length;b++)d+=l(w[b].values[c]);if(d)for(b=0;e>b;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();/*!
  * Bootstrap v4.6.2 (https://getbootstrap.com/)
  * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var o=i(e),a=i(n);function s(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function l(t,e,n){return e&&s(t.prototype,e),n&&s(t,n),Object.defineProperty(t,"prototype",{writable:!1}),t}function r(){return r=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t},r.apply(this,arguments)}function u(t,e){return u=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},u(t,e)}var f="transitionend";var d={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=o.default(t).css("transition-duration"),n=o.default(t).css("transition-delay"),i=parseFloat(e),a=parseFloat(n);return i||a?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){o.default(t).trigger(f)},supportsTransitionEnd:function(){return Boolean(f)},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],a=e[i],s=a&&d.isElement(a)?"element":null===(l=a)||"undefined"==typeof l?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(s))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+s+'" but expected type "'+o+'".')}var l},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?d.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof o.default)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=o.default.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};d.jQueryDetection(),o.default.fn.emulateTransitionEnd=function(t){var e=this,n=!1;return o.default(this).one(d.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||d.triggerTransitionEnd(e)}),t),this},o.default.event.special[d.TRANSITION_END]={bindType:f,delegateType:f,handle:function(t){if(o.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var c="bs.alert",h=o.default.fn.alert,g=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){o.default.removeData(this._element,c),this._element=null},e._getRootElement=function(t){var e=d.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=o.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=o.default.Event("close.bs.alert");return o.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(o.default(t).removeClass("show"),o.default(t).hasClass("fade")){var n=d.getTransitionDurationFromElement(t);o.default(t).one(d.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){o.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(c);i||(i=new t(this),n.data(c,i)),"close"===e&&i[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();o.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',g._handleDismiss(new g)),o.default.fn.alert=g._jQueryInterface,o.default.fn.alert.Constructor=g,o.default.fn.alert.noConflict=function(){return o.default.fn.alert=h,g._jQueryInterface};var m="bs.button",p=o.default.fn.button,_="active",v='[data-toggle^="button"]',y='input:not([type="hidden"])',b=".btn",E=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=o.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var i=this._element.querySelector(y);if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains(_))t=!1;else{var a=n.querySelector(".active");a&&o.default(a).removeClass(_)}t&&("checkbox"!==i.type&&"radio"!==i.type||(i.checked=!this._element.classList.contains(_)),this.shouldAvoidTriggerChange||o.default(i).trigger("change")),i.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(_)),t&&o.default(this._element).toggleClass(_))},e.dispose=function(){o.default.removeData(this._element,m),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this),a=i.data(m);a||(a=new t(this),i.data(m,a)),a.shouldAvoidTriggerChange=n,"toggle"===e&&a[e]()}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();o.default(document).on("click.bs.button.data-api",v,(function(t){var e=t.target,n=e;if(o.default(e).hasClass("btn")||(e=o.default(e).closest(b)[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var i=e.querySelector(y);if(i&&(i.hasAttribute("disabled")||i.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||E._jQueryInterface.call(o.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",v,(function(t){var e=o.default(t.target).closest(b)[0];o.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),o.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector(y);o.checked||o.hasAttribute("checked")?i.classList.add(_):i.classList.remove(_)}for(var a=0,s=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;a<s;a++){var l=t[a];"true"===l.getAttribute("aria-pressed")?l.classList.add(_):l.classList.remove(_)}})),o.default.fn.button=E._jQueryInterface,o.default.fn.button.Constructor=E,o.default.fn.button.noConflict=function(){return o.default.fn.button=p,E._jQueryInterface};var T="carousel",w="bs.carousel",C=o.default.fn[T],S="active",N="next",D="prev",A="slid.bs.carousel",I=".active.carousel-item",k={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},O={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},j={TOUCH:"touch",PEN:"pen"},P=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide(N)},e.nextWhenVisible=function(){var t=o.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide(D)},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(d.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(I);var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)o.default(this._element).one(A,(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?N:D;this._slide(i,this._items[t])}},e.dispose=function(){o.default(this._element).off(".bs.carousel"),o.default.removeData(this._element,w),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=r({},k,t),d.typeCheckConfig(T,t,O),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&o.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&o.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&j[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&j[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};o.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(o.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(o.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){t.touchDeltaX=e.originalEvent.touches&&e.originalEvent.touches.length>1?0:e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),o.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n=t===N,i=t===D,o=this._getItemIndex(e),a=this._items.length-1;if((i&&0===o||n&&o===a)&&!this._config.wrap)return e;var s=(o+(t===D?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(this._element.querySelector(I)),a=o.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n});return o.default(this._element).trigger(a),a},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));o.default(e).removeClass(S);var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&o.default(n).addClass(S)}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(I);if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var n,i,a,s=this,l=this._element.querySelector(I),r=this._getItemIndex(l),u=e||l&&this._getItemByDirection(t,l),f=this._getItemIndex(u),c=Boolean(this._interval);if(t===N?(n="carousel-item-left",i="carousel-item-next",a="left"):(n="carousel-item-right",i="carousel-item-prev",a="right"),u&&o.default(u).hasClass(S))this._isSliding=!1;else if(!this._triggerSlideEvent(u,a).isDefaultPrevented()&&l&&u){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(u),this._activeElement=u;var h=o.default.Event(A,{relatedTarget:u,direction:a,from:r,to:f});if(o.default(this._element).hasClass("slide")){o.default(u).addClass(i),d.reflow(u),o.default(l).addClass(n),o.default(u).addClass(n);var g=d.getTransitionDurationFromElement(l);o.default(l).one(d.TRANSITION_END,(function(){o.default(u).removeClass(n+" "+i).addClass(S),o.default(l).removeClass("active "+i+" "+n),s._isSliding=!1,setTimeout((function(){return o.default(s._element).trigger(h)}),0)})).emulateTransitionEnd(g)}else o.default(l).removeClass(S),o.default(u).addClass(S),this._isSliding=!1,o.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data(w),i=r({},k,o.default(this).data());"object"==typeof e&&(i=r({},i,e));var a="string"==typeof e?e:i.slide;if(n||(n=new t(this,i),o.default(this).data(w,n)),"number"==typeof e)n.to(e);else if("string"==typeof a){if("undefined"==typeof n[a])throw new TypeError('No method named "'+a+'"');n[a]()}else i.interval&&i.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=d.getSelectorFromElement(this);if(n){var i=o.default(n)[0];if(i&&o.default(i).hasClass("carousel")){var a=r({},o.default(i).data(),o.default(this).data()),s=this.getAttribute("data-slide-to");s&&(a.interval=!1),t._jQueryInterface.call(o.default(i),a),s&&o.default(i).data(w).to(s),e.preventDefault()}}},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return k}}]),t}();o.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",P._dataApiClickHandler),o.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e<n;e++){var i=o.default(t[e]);P._jQueryInterface.call(i,i.data())}})),o.default.fn[T]=P._jQueryInterface,o.default.fn[T].Constructor=P,o.default.fn[T].noConflict=function(){return o.default.fn[T]=C,P._jQueryInterface};var L="collapse",R="bs.collapse",x=o.default.fn[L],q="show",F="collapse",Q="collapsing",B="collapsed",H="width",U='[data-toggle="collapse"]',M={toggle:!0,parent:""},W={toggle:"boolean",parent:"(string|element)"},V=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll(U)),i=0,o=n.length;i<o;i++){var a=n[i],s=d.getSelectorFromElement(a),l=[].slice.call(document.querySelectorAll(s)).filter((function(e){return e===t}));null!==s&&l.length>0&&(this._selector=s,this._triggerArray.push(a))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){o.default(this._element).hasClass(q)?this.hide():this.show()},e.show=function(){var e,n,i=this;if(!(this._isTransitioning||o.default(this._element).hasClass(q)||(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof i._config.parent?t.getAttribute("data-parent")===i._config.parent:t.classList.contains(F)}))).length&&(e=null),e&&(n=o.default(e).not(this._selector).data(R))&&n._isTransitioning))){var a=o.default.Event("show.bs.collapse");if(o.default(this._element).trigger(a),!a.isDefaultPrevented()){e&&(t._jQueryInterface.call(o.default(e).not(this._selector),"hide"),n||o.default(e).data(R,null));var s=this._getDimension();o.default(this._element).removeClass(F).addClass(Q),this._element.style[s]=0,this._triggerArray.length&&o.default(this._triggerArray).removeClass(B).attr("aria-expanded",!0),this.setTransitioning(!0);var l="scroll"+(s[0].toUpperCase()+s.slice(1)),r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){o.default(i._element).removeClass(Q).addClass("collapse show"),i._element.style[s]="",i.setTransitioning(!1),o.default(i._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(r),this._element.style[s]=this._element[l]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&o.default(this._element).hasClass(q)){var e=o.default.Event("hide.bs.collapse");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",d.reflow(this._element),o.default(this._element).addClass(Q).removeClass("collapse show");var i=this._triggerArray.length;if(i>0)for(var a=0;a<i;a++){var s=this._triggerArray[a],l=d.getSelectorFromElement(s);null!==l&&(o.default([].slice.call(document.querySelectorAll(l))).hasClass(q)||o.default(s).addClass(B).attr("aria-expanded",!1))}this.setTransitioning(!0),this._element.style[n]="";var r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){t.setTransitioning(!1),o.default(t._element).removeClass(Q).addClass(F).trigger("hidden.bs.collapse")})).emulateTransitionEnd(r)}}},e.setTransitioning=function(t){this._isTransitioning=t},e.dispose=function(){o.default.removeData(this._element,R),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(t){return(t=r({},M,t)).toggle=Boolean(t.toggle),d.typeCheckConfig(L,t,W),t},e._getDimension=function(){return o.default(this._element).hasClass(H)?H:"height"},e._getParent=function(){var e,n=this;d.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var i='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',a=[].slice.call(e.querySelectorAll(i));return o.default(a).each((function(e,i){n._addAriaAndCollapsedClass(t._getTargetFromElement(i),[i])})),e},e._addAriaAndCollapsedClass=function(t,e){var n=o.default(t).hasClass(q);e.length&&o.default(e).toggleClass(B,!n).attr("aria-expanded",n)},t._getTargetFromElement=function(t){var e=d.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(R),a=r({},M,n.data(),"object"==typeof e&&e?e:{});if(!i&&a.toggle&&"string"==typeof e&&/show|hide/.test(e)&&(a.toggle=!1),i||(i=new t(this,a),n.data(R,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return M}}]),t}();o.default(document).on("click.bs.collapse.data-api",U,(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var e=o.default(this),n=d.getSelectorFromElement(this),i=[].slice.call(document.querySelectorAll(n));o.default(i).each((function(){var t=o.default(this),n=t.data(R)?"toggle":e.data();V._jQueryInterface.call(t,n)}))})),o.default.fn[L]=V._jQueryInterface,o.default.fn[L].Constructor=V,o.default.fn[L].noConflict=function(){return o.default.fn[L]=x,V._jQueryInterface};var z="dropdown",K="bs.dropdown",X=o.default.fn[z],Y=new RegExp("38|40|27"),$="disabled",J="show",G="dropdown-menu-right",Z="hide.bs.dropdown",tt="hidden.bs.dropdown",et="click.bs.dropdown.data-api",nt="keydown.bs.dropdown.data-api",it='[data-toggle="dropdown"]',ot=".dropdown-menu",at={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},st={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},lt=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!o.default(this._element).hasClass($)){var e=o.default(this._menu).hasClass(J);t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||o.default(this._element).hasClass($)||o.default(this._menu).hasClass(J))){var n={relatedTarget:this._element},i=o.default.Event("show.bs.dropdown",n),s=t._getParentFromElement(this._element);if(o.default(s).trigger(i),!i.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");var l=this._element;"parent"===this._config.reference?l=s:d.isElement(this._config.reference)&&(l=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(l=this._config.reference[0])),"scrollParent"!==this._config.boundary&&o.default(s).addClass("position-static"),this._popper=new a.default(l,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===o.default(s).closest(".navbar-nav").length&&o.default(document.body).children().on("mouseover",null,o.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),o.default(this._menu).toggleClass(J),o.default(s).toggleClass(J).trigger(o.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!o.default(this._element).hasClass($)&&o.default(this._menu).hasClass(J)){var e={relatedTarget:this._element},n=o.default.Event(Z,e),i=t._getParentFromElement(this._element);o.default(i).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),o.default(this._menu).toggleClass(J),o.default(i).toggleClass(J).trigger(o.default.Event(tt,e)))}},e.dispose=function(){o.default.removeData(this._element,K),o.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;o.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=r({},this.constructor.Default,o.default(this._element).data(),t),d.typeCheckConfig(z,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(ot))}return this._menu},e._getPlacement=function(){var t=o.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=o.default(this._menu).hasClass(G)?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":o.default(this._menu).hasClass(G)&&(e="bottom-end"),e},e._detectNavbar=function(){return o.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t._config.offset(e.offsets,t._element)),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),r({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data(K);if(n||(n=new t(this,"object"==typeof e?e:null),o.default(this).data(K,n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll(it)),i=0,a=n.length;i<a;i++){var s=t._getParentFromElement(n[i]),l=o.default(n[i]).data(K),r={relatedTarget:n[i]};if(e&&"click"===e.type&&(r.clickEvent=e),l){var u=l._menu;if(o.default(s).hasClass(J)&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&o.default.contains(s,e.target))){var f=o.default.Event(Z,r);o.default(s).trigger(f),f.isDefaultPrevented()||("ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),n[i].setAttribute("aria-expanded","false"),l._popper&&l._popper.destroy(),o.default(u).removeClass(J),o.default(s).removeClass(J).trigger(o.default.Event(tt,r)))}}}},t._getParentFromElement=function(t){var e,n=d.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(e){if(!(/input|textarea/i.test(e.target.tagName)?32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||o.default(e.target).closest(ot).length):!Y.test(e.which))&&!this.disabled&&!o.default(this).hasClass($)){var n=t._getParentFromElement(this),i=o.default(n).hasClass(J);if(i||27!==e.which){if(e.preventDefault(),e.stopPropagation(),!i||27===e.which||32===e.which)return 27===e.which&&o.default(n.querySelector(it)).trigger("focus"),void o.default(this).trigger("click");var a=[].slice.call(n.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return o.default(t).is(":visible")}));if(0!==a.length){var s=a.indexOf(e.target);38===e.which&&s>0&&s--,40===e.which&&s<a.length-1&&s++,s<0&&(s=0),a[s].focus()}}}},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return at}},{key:"DefaultType",get:function(){return st}}]),t}();o.default(document).on(nt,it,lt._dataApiKeydownHandler).on(nt,ot,lt._dataApiKeydownHandler).on(et+" keyup.bs.dropdown.data-api",lt._clearMenus).on(et,it,(function(t){t.preventDefault(),t.stopPropagation(),lt._jQueryInterface.call(o.default(this),"toggle")})).on(et,".dropdown form",(function(t){t.stopPropagation()})),o.default.fn[z]=lt._jQueryInterface,o.default.fn[z].Constructor=lt,o.default.fn[z].noConflict=function(){return o.default.fn[z]=X,lt._jQueryInterface};var rt="bs.modal",ut=o.default.fn.modal,ft="modal-open",dt="fade",ct="show",ht="modal-static",gt="hidden.bs.modal",mt="show.bs.modal",pt="focusin.bs.modal",_t="resize.bs.modal",vt="click.dismiss.bs.modal",yt="keydown.dismiss.bs.modal",bt="mousedown.dismiss.bs.modal",Et=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Tt={backdrop:!0,keyboard:!0,focus:!0,show:!0},wt={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},Ct=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var e=t.prototype;return e.toggle=function(t){return this._isShown?this.hide():this.show(t)},e.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){var n=o.default.Event(mt,{relatedTarget:t});o.default(this._element).trigger(n),n.isDefaultPrevented()||(this._isShown=!0,o.default(this._element).hasClass(dt)&&(this._isTransitioning=!0),this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),o.default(this._element).on(vt,'[data-dismiss="modal"]',(function(t){return e.hide(t)})),o.default(this._dialog).on(bt,(function(){o.default(e._element).one("mouseup.dismiss.bs.modal",(function(t){o.default(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return e._showElement(t)})))}},e.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=o.default.Event("hide.bs.modal");if(o.default(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var i=o.default(this._element).hasClass(dt);if(i&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),o.default(document).off(pt),o.default(this._element).removeClass(ct),o.default(this._element).off(vt),o.default(this._dialog).off(bt),i){var a=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(t){return e._hideModal(t)})).emulateTransitionEnd(a)}else this._hideModal()}}},e.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return o.default(t).off(".bs.modal")})),o.default(document).off(pt),o.default.removeData(this._element,rt),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(t){return t=r({},Tt,t),d.typeCheckConfig("modal",t,wt),t},e._triggerBackdropTransition=function(){var t=this,e=o.default.Event("hidePrevented.bs.modal");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._element.scrollHeight>document.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add(ht);var i=d.getTransitionDurationFromElement(this._dialog);o.default(this._element).off(d.TRANSITION_END),o.default(this._element).one(d.TRANSITION_END,(function(){t._element.classList.remove(ht),n||o.default(t._element).one(d.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,i)})).emulateTransitionEnd(i),this._element.focus()}},e._showElement=function(t){var e=this,n=o.default(this._element).hasClass(dt),i=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),o.default(this._dialog).hasClass("modal-dialog-scrollable")&&i?i.scrollTop=0:this._element.scrollTop=0,n&&d.reflow(this._element),o.default(this._element).addClass(ct),this._config.focus&&this._enforceFocus();var a=o.default.Event("shown.bs.modal",{relatedTarget:t}),s=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,o.default(e._element).trigger(a)};if(n){var l=d.getTransitionDurationFromElement(this._dialog);o.default(this._dialog).one(d.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._enforceFocus=function(){var t=this;o.default(document).off(pt).on(pt,(function(e){document!==e.target&&t._element!==e.target&&0===o.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?o.default(this._element).on(yt,(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||o.default(this._element).off(yt)},e._setResizeEvent=function(){var t=this;this._isShown?o.default(window).on(_t,(function(e){return t.handleUpdate(e)})):o.default(window).off(_t)},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){o.default(document.body).removeClass(ft),t._resetAdjustments(),t._resetScrollbar(),o.default(t._element).trigger(gt)}))},e._removeBackdrop=function(){this._backdrop&&(o.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=o.default(this._element).hasClass(dt)?dt:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),o.default(this._backdrop).appendTo(document.body),o.default(this._element).on(vt,(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),n&&d.reflow(this._backdrop),o.default(this._backdrop).addClass(ct),!t)return;if(!n)return void t();var i=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,t).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){o.default(this._backdrop).removeClass(ct);var a=function(){e._removeBackdrop(),t&&t()};if(o.default(this._element).hasClass(dt)){var s=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(Et)),n=[].slice.call(document.querySelectorAll(".sticky-top"));o.default(e).each((function(e,n){var i=n.style.paddingRight,a=o.default(n).css("padding-right");o.default(n).data("padding-right",i).css("padding-right",parseFloat(a)+t._scrollbarWidth+"px")})),o.default(n).each((function(e,n){var i=n.style.marginRight,a=o.default(n).css("margin-right");o.default(n).data("margin-right",i).css("margin-right",parseFloat(a)-t._scrollbarWidth+"px")}));var i=document.body.style.paddingRight,a=o.default(document.body).css("padding-right");o.default(document.body).data("padding-right",i).css("padding-right",parseFloat(a)+this._scrollbarWidth+"px")}o.default(document.body).addClass(ft)},e._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(Et));o.default(t).each((function(t,e){var n=o.default(e).data("padding-right");o.default(e).removeData("padding-right"),e.style.paddingRight=n||""}));var e=[].slice.call(document.querySelectorAll(".sticky-top"));o.default(e).each((function(t,e){var n=o.default(e).data("margin-right");"undefined"!=typeof n&&o.default(e).css("margin-right",n).removeData("margin-right")}));var n=o.default(document.body).data("padding-right");o.default(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this).data(rt),a=r({},Tt,o.default(this).data(),"object"==typeof e&&e?e:{});if(i||(i=new t(this,a),o.default(this).data(rt,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](n)}else a.show&&i.show(n)}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Tt}}]),t}();o.default(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var e,n=this,i=d.getSelectorFromElement(this);i&&(e=document.querySelector(i));var a=o.default(e).data(rt)?"toggle":r({},o.default(e).data(),o.default(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var s=o.default(e).one(mt,(function(t){t.isDefaultPrevented()||s.one(gt,(function(){o.default(n).is(":visible")&&n.focus()}))}));Ct._jQueryInterface.call(o.default(e),a,this)})),o.default.fn.modal=Ct._jQueryInterface,o.default.fn.modal.Constructor=Ct,o.default.fn.modal.noConflict=function(){return o.default.fn.modal=ut,Ct._jQueryInterface};var St=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],Nt=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,Dt=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function At(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),a=[].slice.call(i.body.querySelectorAll("*")),s=function(t,n){var i=a[t],s=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var l=[].slice.call(i.attributes),r=[].concat(e["*"]||[],e[s]||[]);l.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===St.indexOf(n)||Boolean(Nt.test(t.nodeValue)||Dt.test(t.nodeValue));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,a=i.length;o<a;o++)if(i[o].test(n))return!0;return!1})(t,r)||i.removeAttribute(t.nodeName)}))},l=0,r=a.length;l<r;l++)s(l);return i.body.innerHTML}var It="tooltip",kt="bs.tooltip",Ot=o.default.fn.tooltip,jt=new RegExp("(^|\\s)bs-tooltip\\S+","g"),Pt=["sanitize","whiteList","sanitizeFn"],Lt="fade",Rt="show",xt="show",qt="out",Ft="hover",Qt="focus",Bt={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Ht={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},Ut={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},Mt={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},Wt=function(){function t(t,e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=o.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(o.default(this.getTipElement()).hasClass(Rt))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),o.default.removeData(this.element,this.constructor.DATA_KEY),o.default(this.element).off(this.constructor.EVENT_KEY),o.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&o.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===o.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=o.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){o.default(this.element).trigger(e);var n=d.findShadowRoot(this.element),i=o.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var s=this.getTipElement(),l=d.getUID(this.constructor.NAME);s.setAttribute("id",l),this.element.setAttribute("aria-describedby",l),this.setContent(),this.config.animation&&o.default(s).addClass(Lt);var r="function"==typeof this.config.placement?this.config.placement.call(this,s,this.element):this.config.placement,u=this._getAttachment(r);this.addAttachmentClass(u);var f=this._getContainer();o.default(s).data(this.constructor.DATA_KEY,this),o.default.contains(this.element.ownerDocument.documentElement,this.tip)||o.default(s).appendTo(f),o.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new a.default(this.element,s,this._getPopperConfig(u)),o.default(s).addClass(Rt),o.default(s).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&o.default(document.body).children().on("mouseover",null,o.default.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,o.default(t.element).trigger(t.constructor.Event.SHOWN),e===qt&&t._leave(null,t)};if(o.default(this.tip).hasClass(Lt)){var h=d.getTransitionDurationFromElement(this.tip);o.default(this.tip).one(d.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(t){var e=this,n=this.getTipElement(),i=o.default.Event(this.constructor.Event.HIDE),a=function(){e._hoverState!==xt&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),o.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(o.default(this.element).trigger(i),!i.isDefaultPrevented()){if(o.default(n).removeClass(Rt),"ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,o.default(this.tip).hasClass(Lt)){var s=d.getTransitionDurationFromElement(n);o.default(n).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(o.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),o.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=At(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?o.default(e).parent().is(t)||t.empty().append(e):t.text(o.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return r({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t.config.offset(e.offsets,t.element)),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:d.isElement(this.config.container)?o.default(this.config.container):o.default(document).find(this.config.container)},e._getAttachment=function(t){return Bt[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)o.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n=e===Ft?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i=e===Ft?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;o.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},o.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=r({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Qt:Ft]=!0),o.default(e.getTipElement()).hasClass(Rt)||e._hoverState===xt?e._hoverState=xt:(clearTimeout(e._timeout),e._hoverState=xt,e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){e._hoverState===xt&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Qt:Ft]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=qt,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){e._hoverState===qt&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=o.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Pt.indexOf(t)&&delete e[t]})),"number"==typeof(t=r({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),d.typeCheckConfig(It,t,this.constructor.DefaultType),t.sanitize&&(t.template=At(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(jt);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(o.default(t).removeClass(Lt),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(kt),a="object"==typeof e&&e;if((i||!/dispose|hide/.test(e))&&(i||(i=new t(this,a),n.data(kt,i)),"string"==typeof e)){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Ht}},{key:"NAME",get:function(){return It}},{key:"DATA_KEY",get:function(){return kt}},{key:"Event",get:function(){return Mt}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Ut}}]),t}();o.default.fn.tooltip=Wt._jQueryInterface,o.default.fn.tooltip.Constructor=Wt,o.default.fn.tooltip.noConflict=function(){return o.default.fn.tooltip=Ot,Wt._jQueryInterface};var Vt="bs.popover",zt=o.default.fn.popover,Kt=new RegExp("(^|\\s)bs-popover\\S+","g"),Xt=r({},Wt.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),Yt=r({},Wt.DefaultType,{content:"(string|element|function)"}),$t={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},Jt=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),e.prototype.constructor=e,u(e,n);var a=i.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},a.setContent=function(){var t=o.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(Kt);null!==e&&e.length>0&&t.removeClass(e.join(""))},i._jQueryInterface=function(t){return this.each((function(){var e=o.default(this).data(Vt),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new i(this,n),o.default(this).data(Vt,e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},l(i,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Xt}},{key:"NAME",get:function(){return"popover"}},{key:"DATA_KEY",get:function(){return Vt}},{key:"Event",get:function(){return $t}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Yt}}]),i}(Wt);o.default.fn.popover=Jt._jQueryInterface,o.default.fn.popover.Constructor=Jt,o.default.fn.popover.noConflict=function(){return o.default.fn.popover=zt,Jt._jQueryInterface};var Gt="scrollspy",Zt="bs.scrollspy",te=o.default.fn[Gt],ee="active",ne="position",ie=".nav, .list-group",oe={offset:10,method:"auto",target:""},ae={offset:"number",method:"string",target:"(string|element)"},se=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,o.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":ne,n="auto"===this._config.method?e:this._config.method,i=n===ne?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,a=d.getSelectorFromElement(t);if(a&&(e=document.querySelector(a)),e){var s=e.getBoundingClientRect();if(s.width||s.height)return[o.default(e)[n]().top+i,a]}return null})).filter(Boolean).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){o.default.removeData(this._element,Zt),o.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=r({},oe,"object"==typeof t&&t?t:{})).target&&d.isElement(t.target)){var e=o.default(t.target).attr("id");e||(e=d.getUID(Gt),o.default(t.target).attr("id",e)),t.target="#"+e}return d.typeCheckConfig(Gt,t,ae),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),n=o.default([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass("dropdown-item")?(n.closest(".dropdown").find(".dropdown-toggle").addClass(ee),n.addClass(ee)):(n.addClass(ee),n.parents(ie).prev(".nav-link, .list-group-item").addClass(ee),n.parents(ie).prev(".nav-item").children(".nav-link").addClass(ee)),o.default(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},e._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains(ee)})).forEach((function(t){return t.classList.remove(ee)}))},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data(Zt);if(n||(n=new t(this,"object"==typeof e&&e),o.default(this).data(Zt,n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return oe}}]),t}();o.default(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),e=t.length;e--;){var n=o.default(t[e]);se._jQueryInterface.call(n,n.data())}})),o.default.fn[Gt]=se._jQueryInterface,o.default.fn[Gt].Constructor=se,o.default.fn[Gt].noConflict=function(){return o.default.fn[Gt]=te,se._jQueryInterface};var le="bs.tab",re=o.default.fn.tab,ue="active",fe="fade",de="show",ce=".active",he="> li > .active",ge=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&o.default(this._element).hasClass(ue)||o.default(this._element).hasClass("disabled")||this._element.hasAttribute("disabled"))){var e,n,i=o.default(this._element).closest(".nav, .list-group")[0],a=d.getSelectorFromElement(this._element);if(i){var s="UL"===i.nodeName||"OL"===i.nodeName?he:ce;n=(n=o.default.makeArray(o.default(i).find(s)))[n.length-1]}var l=o.default.Event("hide.bs.tab",{relatedTarget:this._element}),r=o.default.Event("show.bs.tab",{relatedTarget:n});if(n&&o.default(n).trigger(l),o.default(this._element).trigger(r),!r.isDefaultPrevented()&&!l.isDefaultPrevented()){a&&(e=document.querySelector(a)),this._activate(this._element,i);var u=function(){var e=o.default.Event("hidden.bs.tab",{relatedTarget:t._element}),i=o.default.Event("shown.bs.tab",{relatedTarget:n});o.default(n).trigger(e),o.default(t._element).trigger(i)};e?this._activate(e,e.parentNode,u):u()}}},e.dispose=function(){o.default.removeData(this._element,le),this._element=null},e._activate=function(t,e,n){var i=this,a=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?o.default(e).children(ce):o.default(e).find(he))[0],s=n&&a&&o.default(a).hasClass(fe),l=function(){return i._transitionComplete(t,a,n)};if(a&&s){var r=d.getTransitionDurationFromElement(a);o.default(a).removeClass(de).one(d.TRANSITION_END,l).emulateTransitionEnd(r)}else l()},e._transitionComplete=function(t,e,n){if(e){o.default(e).removeClass(ue);var i=o.default(e.parentNode).find("> .dropdown-menu .active")[0];i&&o.default(i).removeClass(ue),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}o.default(t).addClass(ue),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),d.reflow(t),t.classList.contains(fe)&&t.classList.add(de);var a=t.parentNode;if(a&&"LI"===a.nodeName&&(a=a.parentNode),a&&o.default(a).hasClass("dropdown-menu")){var s=o.default(t).closest(".dropdown")[0];if(s){var l=[].slice.call(s.querySelectorAll(".dropdown-toggle"));o.default(l).addClass(ue)}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(le);if(i||(i=new t(this),n.data(le,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();o.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ge._jQueryInterface.call(o.default(this),"show")})),o.default.fn.tab=ge._jQueryInterface,o.default.fn.tab.Constructor=ge,o.default.fn.tab.noConflict=function(){return o.default.fn.tab=re,ge._jQueryInterface};var me="bs.toast",pe=o.default.fn.toast,_e="hide",ve="show",ye="showing",be="click.dismiss.bs.toast",Ee={animation:!0,autohide:!0,delay:500},Te={animation:"boolean",autohide:"boolean",delay:"number"},we=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=o.default.Event("show.bs.toast");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove(ye),t._element.classList.add(ve),o.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove(_e),d.reflow(this._element),this._element.classList.add(ye),this._config.animation){var i=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,n).emulateTransitionEnd(i)}else n()}},e.hide=function(){if(this._element.classList.contains(ve)){var t=o.default.Event("hide.bs.toast");o.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains(ve)&&this._element.classList.remove(ve),o.default(this._element).off(be),o.default.removeData(this._element,me),this._element=null,this._config=null},e._getConfig=function(t){return t=r({},Ee,o.default(this._element).data(),"object"==typeof t&&t?t:{}),d.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;o.default(this._element).on(be,'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add(_e),o.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove(ve),this._config.animation){var n=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(me);if(i||(i=new t(this,"object"==typeof e&&e),n.data(me,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](this)}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"DefaultType",get:function(){return Te}},{key:"Default",get:function(){return Ee}}]),t}();o.default.fn.toast=we._jQueryInterface,o.default.fn.toast.Constructor=we,o.default.fn.toast.noConflict=function(){return o.default.fn.toast=pe,we._jQueryInterface},t.Alert=g,t.Button=E,t.Carousel=P,t.Collapse=V,t.Dropdown=lt,t.Modal=Ct,t.Popover=Jt,t.Scrollspy=se,t.Tab=ge,t.Toast=we,t.Tooltip=Wt,t.Util=d,Object.defineProperty(t,"__esModule",{value:!0})}));
//# sourceMappingURL=bootstrap.min.js.map/*
 Copyright (C) Federico Zivolo 2020
 Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
 */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',n=e.nodeName;if('BODY'===n||'HTML'===n){var i=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||i;return r[o]}return e[o]}function f(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],n=l(t,'top'),i=l(t,'left'),r=o?-1:1;return e.top+=n*r,e.bottom+=n*r,e.left+=i*r,e.right+=i*r,e}function m(e,t){var o='x'===t?'Left':'Top',n='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'])+parseFloat(e['border'+n+'Width'])}function h(e,t,o,n){return ee(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],r(10)?parseInt(o['offset'+e])+parseInt(n['margin'+('Height'===e?'Top':'Left')])+parseInt(n['margin'+('Height'===e?'Bottom':'Right')]):0)}function c(e){var t=e.body,o=e.documentElement,n=r(10)&&getComputedStyle(o);return{height:h('Height',t,o,n),width:h('Width',t,o,n)}}function g(e){return le({},e,{right:e.left+e.width,bottom:e.top+e.height})}function u(e){var o={};try{if(r(10)){o=e.getBoundingClientRect();var n=l(e,'top'),i=l(e,'left');o.top+=n,o.left+=i,o.bottom+=n,o.right+=i}else o=e.getBoundingClientRect()}catch(t){}var p={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},s='HTML'===e.nodeName?c(e.ownerDocument):{},d=s.width||e.clientWidth||p.width,a=s.height||e.clientHeight||p.height,f=e.offsetWidth-d,h=e.offsetHeight-a;if(f||h){var u=t(e);f-=m(u,'x'),h-=m(u,'y'),p.width-=f,p.height-=h}return g(p)}function b(e,o){var i=2<arguments.length&&void 0!==arguments[2]&&arguments[2],p=r(10),s='HTML'===o.nodeName,d=u(e),a=u(o),l=n(e),m=t(o),h=parseFloat(m.borderTopWidth),c=parseFloat(m.borderLeftWidth);i&&s&&(a.top=ee(a.top,0),a.left=ee(a.left,0));var b=g({top:d.top-a.top-h,left:d.left-a.left-c,width:d.width,height:d.height});if(b.marginTop=0,b.marginLeft=0,!p&&s){var w=parseFloat(m.marginTop),y=parseFloat(m.marginLeft);b.top-=h-w,b.bottom-=h-w,b.left-=c-y,b.right-=c-y,b.marginTop=w,b.marginLeft=y}return(p&&!i?o.contains(l):o===l&&'BODY'!==l.nodeName)&&(b=f(b,o)),b}function w(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=e.ownerDocument.documentElement,n=b(e,o),i=ee(o.clientWidth,window.innerWidth||0),r=ee(o.clientHeight,window.innerHeight||0),p=t?0:l(o),s=t?0:l(o,'left'),d={top:p-n.top+n.marginTop,left:s-n.left+n.marginLeft,width:i,height:r};return g(d)}function y(e){var n=e.nodeName;if('BODY'===n||'HTML'===n)return!1;if('fixed'===t(e,'position'))return!0;var i=o(e);return!!i&&y(i)}function E(e){if(!e||!e.parentElement||r())return document.documentElement;for(var o=e.parentElement;o&&'none'===t(o,'transform');)o=o.parentElement;return o||document.documentElement}function v(e,t,r,p){var s=4<arguments.length&&void 0!==arguments[4]&&arguments[4],d={top:0,left:0},l=s?E(e):a(e,i(t));if('viewport'===p)d=w(l,s);else{var f;'scrollParent'===p?(f=n(o(t)),'BODY'===f.nodeName&&(f=e.ownerDocument.documentElement)):'window'===p?f=e.ownerDocument.documentElement:f=p;var m=b(f,l,s);if('HTML'===f.nodeName&&!y(l)){var h=c(e.ownerDocument),g=h.height,u=h.width;d.top+=m.top-m.marginTop,d.bottom=g+m.top,d.left+=m.left-m.marginLeft,d.right=u+m.left}else d=m}r=r||0;var v='number'==typeof r;return d.left+=v?r:r.left||0,d.top+=v?r:r.top||0,d.right-=v?r:r.right||0,d.bottom-=v?r:r.bottom||0,d}function x(e){var t=e.width,o=e.height;return t*o}function O(e,t,o,n,i){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=v(o,n,r,i),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return le({key:e},s[e],{area:x(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,n=e.height;return t>=o.clientWidth&&n>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function L(e,t,o){var n=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,r=n?E(t):a(t,i(o));return b(o,r,n)}function S(e){var t=e.ownerDocument.defaultView,o=t.getComputedStyle(e),n=parseFloat(o.marginTop||0)+parseFloat(o.marginBottom||0),i=parseFloat(o.marginLeft||0)+parseFloat(o.marginRight||0),r={width:e.offsetWidth+i,height:e.offsetHeight+n};return r}function T(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function C(e,t,o){o=o.split('-')[0];var n=S(e),i={width:n.width,height:n.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return i[p]=t[p]+t[d]/2-n[d]/2,i[s]=o===s?t[s]-n[a]:t[T(s)],i}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function N(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var n=D(e,function(e){return e[t]===o});return e.indexOf(n)}function P(t,o,n){var i=void 0===n?t:t.slice(0,N(t,'name',n));return i.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var n=t['function']||t.fn;t.enabled&&e(n)&&(o.offsets.popper=g(o.offsets.popper),o.offsets.reference=g(o.offsets.reference),o=n(o,t))}),o}function k(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=O(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=C(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?'fixed':'absolute',e=P(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var o=e.name,n=e.enabled;return n&&o===t})}function B(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function H(){return this.state.isDestroyed=!0,W(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.position='',this.popper.style.top='',this.popper.style.left='',this.popper.style.right='',this.popper.style.bottom='',this.popper.style.willChange='',this.popper.style[B('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function A(e){var t=e.ownerDocument;return t?t.defaultView:window}function M(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||M(n(p.parentNode),t,o,i),i.push(p)}function F(e,t,o,i){o.updateBound=i,A(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return M(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function I(){this.state.eventsEnabled||(this.state=F(this.reference,this.options,this.state,this.scheduleUpdate))}function R(e,t){return A(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function U(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=R(this.reference,this.state))}function Y(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function V(e,t){Object.keys(t).forEach(function(o){var n='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&Y(t[o])&&(n='px'),e.style[o]=t[o]+n})}function j(e,t){Object.keys(t).forEach(function(o){var n=t[o];!1===n?e.removeAttribute(o):e.setAttribute(o,t[o])})}function q(e,t){var o=e.offsets,n=o.popper,i=o.reference,r=$,p=function(e){return e},s=r(i.width),d=r(n.width),a=-1!==['left','right'].indexOf(e.placement),l=-1!==e.placement.indexOf('-'),f=t?a||l||s%2==d%2?r:Z:p,m=t?r:p;return{left:f(1==s%2&&1==d%2&&!l&&t?n.left-1:n.left),top:m(n.top),bottom:m(n.bottom),right:f(n.right)}}function K(e,t,o){var n=D(e,function(e){var o=e.name;return o===t}),i=!!n&&e.some(function(e){return e.name===o&&e.enabled&&e.order<n.order});if(!i){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return i}function z(e){return'end'===e?'start':'start'===e?'end':e}function G(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=he.indexOf(e),n=he.slice(o+1).concat(he.slice(0,o));return t?n.reverse():n}function _(e,t,o,n){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+i[1],p=i[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=n;}var d=g(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?ee(document.documentElement.clientHeight,window.innerHeight||0):ee(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function X(e,t,o,n){var i=[0,0],r=-1!==['right','left'].indexOf(n),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(D(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,n){var i=(1===n?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return _(e,i,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,n){Y(o)&&(i[t]+=o*('-'===e[n-1]?-1:1))})}),i}function J(e,t){var o,n=t.offset,i=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=i.split('-')[0];return o=Y(+n)?[+n,0]:X(n,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}var Q=Math.min,Z=Math.floor,$=Math.round,ee=Math.max,te='undefined'!=typeof window&&'undefined'!=typeof document&&'undefined'!=typeof navigator,oe=function(){for(var e=['Edge','Trident','Firefox'],t=0;t<e.length;t+=1)if(te&&0<=navigator.userAgent.indexOf(e[t]))return 1;return 0}(),ne=te&&window.Promise,ie=ne?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},oe))}},re=te&&!!(window.MSInputMethodContext&&document.documentMode),pe=te&&/MSIE 10/.test(navigator.userAgent),se=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},de=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,n){return o&&e(t.prototype,o),n&&e(t,n),t}}(),ae=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},le=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var n in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},fe=te&&/Firefox/i.test(navigator.userAgent),me=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],he=me.slice(3),ce={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},ge=function(){function t(o,n){var i=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};se(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=ie(this.update.bind(this)),this.options=le({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(le({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=le({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return le({name:e},i.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return de(t,[{key:'update',value:function(){return k.call(this)}},{key:'destroy',value:function(){return H.call(this)}},{key:'enableEventListeners',value:function(){return I.call(this)}},{key:'disableEventListeners',value:function(){return U.call(this)}}]),t}();return ge.Utils=('undefined'==typeof window?global:window).PopperUtils,ge.placements=me,ge.Defaults={placement:'bottom',positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],n=t.split('-')[1];if(n){var i=e.offsets,r=i.reference,p=i.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:ae({},d,r[d]),end:ae({},d,r[d]+r[a]-p[a])};e.offsets.popper=le({},p,l[n])}return e}},offset:{order:200,enabled:!0,fn:J,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||p(e.instance.popper);e.instance.reference===o&&(o=p(o));var n=B('transform'),i=e.instance.popper.style,r=i.top,s=i.left,d=i[n];i.top='',i.left='',i[n]='';var a=v(e.instance.popper,e.instance.reference,t.padding,o,e.positionFixed);i.top=r,i.left=s,i[n]=d,t.boundaries=a;var l=t.priority,f=e.offsets.popper,m={primary:function(e){var o=f[e];return f[e]<a[e]&&!t.escapeWithReference&&(o=ee(f[e],a[e])),ae({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=f[o];return f[e]>a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(n[d])&&(e.offsets.popper[d]=r(n[d])-o[a]),o[d]>r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[c]-u)),d[m]+u>s[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)<f(l.right)||'top'===n&&f(a.bottom)>f(l.top)||'bottom'===n&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,n=t.y,i=e.offsets.popper,r=D(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==r&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===r?t.gpuAcceleration:r,l=p(e.instance.popper),f=u(l),m={position:i.position},h=q(e,2>window.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge});
//# sourceMappingURL=popper.min.js.map
!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){
r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(var t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){
return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;else for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))}):(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.select=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.slice(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv("	","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)ht(e[r].geometry,t)}},wa={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)pt(e[r],t,0)},Polygon:function(n,t){gt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)gt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)ht(e[r],t)}};ao.geo.area=function(n){return Sa=0,ao.geo.stream(n,Na),Sa};var Sa,ka=new ft,Na={sphere:function(){Sa+=4*Fo},point:b,lineStart:b,lineEnd:b,polygonStart:function(){ka.reset(),Na.lineStart=vt},polygonEnd:function(){var n=2*ka;Sa+=0>n?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,g,v,d,y,m,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=o,m=0,Na.polygonStart()},polygonEnd:function(){Na.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>ka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var e=Ce,r=ze;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ao.geom.polygon=function(n){return ko(n,rl),n};var rl=ao.geom.polygon.prototype=[];rl.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++t<e;)n=r,r=this[t],i+=n[1]*r[0]-n[0]*r[1];return.5*i},rl.centroid=function(n){var t,e,r=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++r<i;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[u*n,o*n]},rl.clip=function(n){for(var t,e,r,i,u,o,a=De(n),l=-1,c=this.length-De(this),f=this[c-1];++l<c;){for(t=n.slice(),n.length=0,i=this[l],u=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Te(o,f,i)?(Te(u,f,i)||n.push(Re(u,o,f,i)),n.push(o)):Te(u,f,i)&&n.push(Re(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var il,ul,ol,al,ll,cl=[],fl=[];Ye.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ve),t.length},tr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},er.prototype={insert:function(n,t){var e,r,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=or(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.R&&(ir(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ur(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.L&&(ur(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ir(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,i=n.U,u=n.L,o=n.R;if(e=u?o?or(o):u:o,i?i.L===n?i.L=e:i.R=e:this._=e,u&&o?(r=e.C,e.C=n.C,e.L=u,u.U=e,e!==o?(i=e.U,e.U=n.U,n=e.R,i.L=n,e.R=o,o.U=e):(e.U=i,i=e,n=e.R)):(r=n.C,n=e),n&&(n.U=i),!r){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ir(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ur(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ir(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ur(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ir(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ur(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ar(e(n),a).cells.forEach(function(e,a){var l=e.edges,c=e.site,f=t[a]=l.length?l.map(function(n){var t=n.start();return[t.x,t.y]}):c.x>=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l<c;)i=f,u=s,f=a[l].edge,s=f.l===o?f.r:f.l,r<u.i&&r<s.i&&cr(o,u,s)<0&&t.push([n[r],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=En(r=n),t):r},t.y=function(n){return arguments.length?(o=En(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?sl:n,t):a===sl?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===sl?null:a&&a[1]},t)};var sl=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var l=n.x,f=n.y;if(null!=l)if(xo(l-e)+xo(f-r)<.01)c(n,t,e,r,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,c(n,s,l,f,i,u,o,a),c(n,t,e,r,i,u,o,a)}else n.x=e,n.y=r,n.point=t}else c(n,t,e,r,i,u,o,a)}function c(n,t,e,r,i,o,a,l){var c=.5*(i+a),f=.5*(o+l),s=e>=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.x<v&&(v=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p<g;)u(k,n[p],s[p],h[p],v,d,y,m);--p}else n.forEach(k.add);return s=h=n=f=null,k}var o,a=Ce,l=ze;return(o=arguments.length)?(a=fr,l=sr,3===o&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(l=n,u):l},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},ao.interpolateRgb=vr,ao.interpolateObject=dr,ao.interpolateNumber=yr,ao.interpolateString=mr;var hl=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pl=new RegExp(hl.source,"g");ao.interpolate=Mr,ao.interpolators=[function(n,t){var e=typeof t;return("string"===e?ua.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?vr:mr:t instanceof an?vr:Array.isArray(t)?xr:"object"===e&&isNaN(t)?dr:yr)(n,t)}],ao.interpolateArray=xr;var gl=function(){return m},vl=ao.map({linear:gl,poly:Er,quad:function(){return Sr},cubic:function(){return kr},sin:function(){return Ar},exp:function(){return Cr},circle:function(){return zr},elastic:Lr,back:qr,bounce:function(){return Tr}}),dl=ao.map({"in":m,out:_r,"in-out":wr,"out-in":function(n){return wr(_r(n))}});ao.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Jr(n[e]));return t}},ao.layout.chord=function(){function n(){var n,c,s,h,p,g={},v=[],d=ao.range(u),y=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(c=0,p=-1;++p<u;)c+=i[h][p];v.push(c),y.push(ao.range(u)),n+=c}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&y.forEach(function(n,t){n.sort(function(n,e){return a(i[t][n],i[t][e])})}),n=(Ho-f*u)/n,c=0,h=-1;++h<u;){for(s=c,p=-1;++p<u;){var m=d[h],M=y[m][p],x=i[m][M],b=c,_=c+=x*n;g[m+"-"+M]={index:m,subindex:M,startAngle:b,endAngle:_,value:x}}r[m]={index:m,startAngle:s,endAngle:c,value:v[m]},c+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=g[h+"-"+p],S=g[p+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}l&&t()}function t(){e.sort(function(n,t){return l((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,o,a,l,c={},f=0;return c.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,c):i},c.padding=function(n){return arguments.length?(f=n,e=r=null,c):f},c.sortGroups=function(n){return arguments.length?(o=n,e=r=null,c):o},c.sortSubgroups=function(n){return arguments.length?(a=n,e=null,c):a},c.sortChords=function(n){return arguments.length?(l=n,e&&t(),c):l},c.chords=function(){return e||n(),e},c.groups=function(){return r||n(),r},c},ao.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-e,l=u*u+o*o;if(l>a*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++l<f;)if(!isNaN(o=a[l][n]))return o;return Math.random()*r}var t,e,r,i=M.length,c=x.length,s=f[0],v=f[1];for(t=0;i>t;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++c<o;)n(a=u[c],e,l=a.value*r,i),e+=l}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var o=r.call(this,e,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var r=ao.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},ii(e,r)},ao.layout.pie=function(){function n(o){var a,l=o.length,c=o.map(function(e,r){return+t.call(n,e,r)}),f=+("function"==typeof r?r.apply(this,arguments):r),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/l,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u<p;)o=l[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=c[u],a>=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>g.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0;
if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<o;)i=n[u],i.x=a,i.y=c,i.dy=f,a+=i.dx=Math.min(e.x+e.dx-a,f?l(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-a,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<o;)i=n[u],i.x=a,i.y=c,i.dx=f,c+=i.dy=Math.min(e.y+e.dy-c,f?l(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-c,e.x+=f,e.dx-=f}}function u(r){var i=o||a(r),u=i[0];return u.x=u.y=0,u.value?(u.dx=c[0],u.dy=c[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?e:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),l=Math.round,c=[1,1],f=null,s=Oi,h=!1,p="squarify",g=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(c=n,u):c},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ii(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ii(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(l=n?Math.round:Number,u):l!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(g=n,u):g},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++a<l;){u.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(e=c[f])&&Qu(e,f,i,r,o),t.push(e)}return Wu(u,i,r)},Co.interrupt=function(n){return this.each(null==n?Il:Bu(Ku(n)))};var Hl,Ol,Il=Bu(Ku()),Yl=[],Zl=0;Yl.call=Co.call,Yl.empty=Co.empty,Yl.node=Co.node,Yl.size=Co.size,ao.transition=function(n,t){return n&&n.transition?Hl?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Yl,Yl.select=function(n){var t,e,r,i=this.id,u=this.namespace,o=[];n=A(n);for(var a=-1,l=this.length;++a<l;){o.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(r=c[f])&&(e=n.call(r,r.__data__,f,a))?("__data__"in r&&(e.__data__=r.__data__),Qu(e,f,u,i,r[u][i]),t.push(e)):t.push(null)}return Wu(o,u,i)},Yl.selectAll=function(n){var t,e,r,i,u,o=this.id,a=this.namespace,l=[];n=C(n);for(var c=-1,f=this.length;++c<f;)for(var s=this[c],h=-1,p=s.length;++h<p;)if(r=s[h]){u=r[a][o],e=n.call(r,r.__data__,h,c),l.push(t=[]);for(var g=-1,v=e.length;++g<v;)(i=e[g])&&Qu(i,g,a,o,u),t.push(i)}return Wu(l,a,o)},Yl.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]<M[0])],L[1]=h[+(n[1]<M[1])]):M=null),E&&y(n,c,0)&&(r(k),t=!0),A&&y(n,f,1)&&(i(k),t=!0),t&&(e(k),w({type:"brush",mode:C?"move":"resize"}))}function y(n,t,e){var r,i,u=Zi(t),l=u[0],c=u[1],f=L[e],v=e?h:s,d=v[1]-v[0];return C&&(l-=f,c-=d+f),r=(e?g:p)?Math.max(l,Math.min(c,n[e])):n[e],C?i=(r+=f)+d:(M&&(f=Math.max(l,Math.min(c,2*M[e]-r))),r>f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=y.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:v}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,y,s,c,v,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",$=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),v(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&D.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(j),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace($," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,y){var v="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===y?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=v!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(v){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=y)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace(B,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ye(function(){return[0]}),last:ye(function(e,t){return[t-1]}),eq:ye(function(e,t,n){return[n<0?n+t:n]}),even:ye(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ye(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ye(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ye(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,y,v,e){return y&&!y[S]&&(y=Ce(y)),v&&!v[S]&&(v=Ce(v,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?v||(e?d:l||y)?[]:t:f;if(g&&g(f,p,n,r),y){i=Te(p,u),y(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(v||d){if(v){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);v(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=v?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),v?v(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(B,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace(B," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,y,v,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(y=o,m=0<(v=i).length,x=0<y.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=y[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=v[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+v.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ve(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ve(t.parentNode)||t),n},d.sortStable=S.split("").sort(j).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var D,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function $(){E.removeEventListener("DOMContentLoaded",$),C.removeEventListener("load",$),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",$),C.addEventListener("load",$));var B=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)B(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):B(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",v.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,v.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^([^.]*)(?:\.(.+)|)/;function we(){return!0}function Te(){return!1}function Ce(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ee(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ee(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Te;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n&&n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,we)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=y.events)||(u=y.events=Object.create(null)),(a=y.handle)||(a=y.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=be.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=Y.hasData(e)&&Y.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=be.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||S.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click",we),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?we:Te,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Te,isPropagationStopped:Te,isImmediatePropagationStopped:Te,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=we,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=we,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=we,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(t,e){S.event.special[t]={setup:function(){return Se(this,t,Ce),!1},trigger:function(){return Se(this,t),!0},_default:function(e){return Y.get(e.target,t)},delegateType:e}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return Ee(this,e,t,n,r)},one:function(e,t,n,r){return Ee(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Te),this.each(function(){S.event.remove(this,e,n,t)})}});var ke=/<script|<style|<link/i,Ae=/checked\s*(?:[^=]|=\s*.checked.)/i,Ne=/^\s*<!\[CDATA\[|\]\]>\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function He(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!v.checkClone&&Ae.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),He(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ye(e,"script"),De)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ye(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,qe),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(Ne,""),u,l))}return n}function Oe(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ye(r)),r.parentNode&&(n&&ie(r)&&ve(ye(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(v.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ye(c),r=0,i=(o=ye(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ye(e),a=a||ye(c),r=0,i=o.length;r<i;r++)Le(o[r],a[r]);else Le(e,c);return 0<(a=ye(c,"script")).length&&ve(a,!f&&ye(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Oe(this,e,!0)},remove:function(e){return Oe(this,e)},text:function(e){return B(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return He(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||je(this,e).appendChild(e)})},prepend:function(){return He(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=je(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return B(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ke.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ye(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return He(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ye(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Pe=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Re=/^--/,Me=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Ie=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},We=new RegExp(ne.join("|"),"i"),Fe="[\\x20\\t\\r\\n\\f]",$e=new RegExp("^"+Fe+"+|((?:^|[^\\\\])(?:\\\\.)*)"+Fe+"+$","g");function Be(e,t,n){var r,i,o,a,s=Re.test(t),u=e.style;return(n=n||Me(e))&&(a=n.getPropertyValue(t)||n[t],s&&(a=a.replace($e,"$1")),""!==a||ie(e)||(a=S.style(e,t)),!v.pixelBoxStyles()&&Pe.test(a)&&We.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=n.width,u.width=r,u.minWidth=i,u.maxWidth=o)),void 0!==a?a+"":a}function _e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",v.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(v,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,re.removeChild(e)),a}}))}();var ze=["Webkit","Moz","ms"],Ue=E.createElement("div").style,Xe={};function Ve(e){var t=S.cssProps[e]||Xe[e];return t||(e in Ue?e:Xe[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=ze.length;while(n--)if((e=ze[n]+t)in Ue)return e}(e)||e)}var Ge=/^(none|table(?!-c[ea]).+)/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Me(e),i=(!v.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Pe.test(a)){if(!n)return a;a="auto"}return(!v.boxSizingReliable()&&i||!v.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Re.test(t),l=e.style;if(u||(t=Ve(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),v.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Re.test(t)||(t=Ve(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ge.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):Ie(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Me(e),o=!v.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=_e(v.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-Ie(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return B(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Me(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Ve(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),y=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!y||void 0===y[r])continue;g=!0}d[r]=y&&y[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=y&&y.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(y?"hidden"in y&&(g=y.hidden):y=Y.access(e,"fxshow",{display:l}),o&&(y.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?y[r]:0,r,p),r in y||(y[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",v.checkOn=""!==rt.value,v.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",v.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return B(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!v.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function yt(e){return(e.match(P)||[]).join(" ")}function vt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return B(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),v.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a;return m(t)?this.each(function(e){S(this).addClass(t.call(this,e,vt(this)))}):(e=mt(t)).length?this.each(function(){if(r=vt(this),n=1===this.nodeType&&" "+yt(r)+" "){for(o=0;o<e.length;o++)i=e[o],n.indexOf(" "+i+" ")<0&&(n+=i+" ");a=yt(n),r!==a&&this.setAttribute("class",a)}}):this},removeClass:function(t){var e,n,r,i,o,a;return m(t)?this.each(function(e){S(this).removeClass(t.call(this,e,vt(this)))}):arguments.length?(e=mt(t)).length?this.each(function(){if(r=vt(this),n=1===this.nodeType&&" "+yt(r)+" "){for(o=0;o<e.length;o++){i=e[o];while(-1<n.indexOf(" "+i+" "))n=n.replace(" "+i+" "," ")}a=yt(n),r!==a&&this.setAttribute("class",a)}}):this:this.attr("class","")},toggleClass:function(t,n){var e,r,i,o,a=typeof t,s="string"===a||Array.isArray(t);return m(t)?this.each(function(e){S(this).toggleClass(t.call(this,e,vt(this),n),n)}):"boolean"==typeof n&&s?n?this.addClass(t):this.removeClass(t):(e=mt(t),this.each(function(){if(s)for(o=S(this),i=0;i<e.length;i++)r=e[i],o.hasClass(r)?o.removeClass(r):o.addClass(r);else void 0!==t&&"boolean"!==a||((r=vt(this))&&Y.set(this,"__className__",r),this.setAttribute&&this.setAttribute("class",r||!1===t?"":Y.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+yt(vt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:yt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},v.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),v.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=y.call(e,"type")?e.type:e,h=y.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),v.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||S.error("Invalid XML: "+(n?S.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function jt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):jt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)jt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var Dt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function $t(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function Bt(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Bt(Bt(e,S.ajaxSettings),t):Bt(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,y=S.ajaxSetup({},t),v=y.context||y,m=y.context&&(v.nodeType||v.jquery)?S(v):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=y.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(y.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),y.url=((e||y.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),y.type=t.method||t.type||y.method||y.type,y.dataTypes=(y.dataType||"*").toLowerCase().match(P)||[""],null==y.crossDomain){r=E.createElement("a");try{r.href=y.url,r.href=r.href,y.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){y.crossDomain=!0}}if(y.data&&y.processData&&"string"!=typeof y.data&&(y.data=S.param(y.data,y.traditional)),$t(Rt,y,t,T),h)return T;for(i in(g=S.event&&y.global)&&0==S.active++&&S.event.trigger("ajaxStart"),y.type=y.type.toUpperCase(),y.hasContent=!Ot.test(y.type),f=y.url.replace(qt,""),y.hasContent?y.data&&y.processData&&0===(y.contentType||"").indexOf("application/x-www-form-urlencoded")&&(y.data=y.data.replace(Dt,"+")):(o=y.url.slice(f.length),y.data&&(y.processData||"string"==typeof y.data)&&(f+=(Et.test(f)?"&":"?")+y.data,delete y.data),!1===y.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),y.url=f+o),y.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(y.data&&y.hasContent&&!1!==y.contentType||t.contentType)&&T.setRequestHeader("Content-Type",y.contentType),T.setRequestHeader("Accept",y.dataTypes[0]&&y.accepts[y.dataTypes[0]]?y.accepts[y.dataTypes[0]]+("*"!==y.dataTypes[0]?", "+It+"; q=0.01":""):y.accepts["*"]),y.headers)T.setRequestHeader(i,y.headers[i]);if(y.beforeSend&&(!1===y.beforeSend.call(v,T,y)||h))return T.abort();if(u="abort",b.add(y.complete),T.done(y.success),T.fail(y.error),c=$t(Mt,y,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,y]),h)return T;y.async&&0<y.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},y.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(y,T,n)),!i&&-1<S.inArray("script",y.dataTypes)&&S.inArray("json",y.dataTypes)<0&&(y.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(y,s,T,i),i?(y.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===y.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(v,[o,l,T]):x.rejectWith(v,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,y,i?o:a]),b.fireWith(v,[T,l]),g&&(m.trigger("ajaxComplete",[T,y]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();v.cors=!!zt&&"withCredentials"in zt,v.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(v.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=yt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"$1")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
  $(function() {
   var $window     = $(window)
     , $top_link   = $('#toplink')
     , $body       = $('body, html')
     , offset      = $('#code').offset().top
     , hidePopover = function ($target) {
        $target.data('popover-hover', false);

        setTimeout(function () {
         if (!$target.data('popover-hover')) {
          $target.popover('hide');
         }
        }, 300);
     };

   $top_link.hide().click(function(event) {
    event.preventDefault();
    $body.animate({scrollTop:0}, 800);
   });

   $window.scroll(function() {
    if($window.scrollTop() > offset) {
     $top_link.fadeIn();
    } else {
     $top_link.fadeOut();
    }
   }).scroll();

   $('.popin')
    .popover({trigger: 'manual'})
    .on({
     'mouseenter.popover': function () {
      var $target = $(this);
      var $container = $target.children().first();

      $target.data('popover-hover', true);

      // popover already displayed
      if ($target.next('.popover').length) {
       return;
      }

      // show the popover
      $container.popover('show');

      // register mouse events on the popover
      $target.next('.popover:not(.popover-initialized)')
       .on({
        'mouseenter': function () {
         $target.data('popover-hover', true);
        },
        'mouseleave': function () {
         hidePopover($container);
        }
       })
       .addClass('popover-initialized');
     },
     'mouseleave.popover': function () {
      hidePopover($(this).children().first());
     }
    });
  });
    <tr class="{{class}} d-flex"><td {{popover}} class="col-1 text-right"><a id="{{lineNumber}}" href="#{{lineNumber}}">{{lineNumber}}</a></td><td class="col-11 codeLine">{{lineContent}}</td></tr>
<hr/>
<h4>Paths</h4>
<p>
    Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
    necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
    Please also be aware that some paths may include implicit rather than explicit branches, e.g. an <code>if</code> statement
    <i>always</i> has an <code>else</code> as part of its logical flow even if you didn't write one.
</p>
{{paths}}
<hr/>
<h4>Branches</h4>
<p>
    Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
    necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
    Please also be aware that some branches may be implicit rather than explicit, e.g. an <code>if</code> statement
    <i>always</i> has an <code>else</code> as part of its logical flow even if you didn't write one.
</p>
{{branches}}
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Code Coverage for {{full_path}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <header>
   <div class="container-fluid">
    <div class="row">
     <div class="col-md-12">
      <nav aria-label="breadcrumb">
       <ol class="breadcrumb">
{{breadcrumbs}}
       </ol>
      </nav>
     </div>
    </div>
   </div>
  </header>
  <div class="container-fluid">
   <div class="table-responsive">
    <table class="table table-bordered">
     <thead>
      <tr>
       <td>&nbsp;</td>
       <td colspan="10"><div align="center"><strong>Code Coverage</strong></div></td>
      </tr>
      <tr>
       <td>&nbsp;</td>
       <td colspan="3"><div align="center"><strong>Lines</strong></div></td>
       <td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
       <td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
      </tr>
     </thead>
     <tbody>
{{items}}
     </tbody>
    </table>
   </div>
{{lines}}
{{structure}}
   <footer>
    <hr/>
    <h4>Legend</h4>
    {{legend}}
    <p>
     <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
    </p>
    <a title="Back to the top" id="toplink" href="#">
        <svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M12 11L6 5l-6 6h12z"/></svg>
    </a>
   </footer>
  </div>
  <script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/popper.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/bootstrap.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/file.js?v={{version}}" type="text/javascript"></script>
 </body>
</html>
.octicon {
  display: inline-block;
  vertical-align: text-top;
  fill: currentColor;
}
.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc}/*!
 * Bootstrap v4.6.2 (https://getbootstrap.com/)
 * Copyright 2011-2022 The Bootstrap Authors
 * Copyright 2011-2022 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:.875em;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated select.form-control:valid,select.form-control.is-valid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated select.form-control:invalid,select.form-control.is-invalid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.width{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.width{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label,.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label::after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label,.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label::after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:1px solid #adb5bd}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;overflow:hidden;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentcolor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentcolor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
/*# sourceMappingURL=bootstrap.min.css.map */body {
 font-family: sans-serif;
 font-size: 1em;
 font-kerning: normal;
 font-variant-ligatures: common-ligatures;
 text-rendering: optimizeLegibility;
 padding-top: 10px;
}

.popover {
 max-width: none;
}

.octicon {
 margin-right:.25em;
 vertical-align: baseline;
 width: 0.75em;
}

.table-bordered>thead>tr>td {
 border-bottom-width: 1px;
}

.table tbody>tr>td, .table thead>tr>td {
 padding-top: 3px;
 padding-bottom: 3px;
}

.table-condensed tbody>tr>td {
 padding-top: 0;
 padding-bottom: 0;
}

.table .progress {
 margin-bottom: inherit;
}

.table-borderless th, .table-borderless td {
 border: 0 !important;
}

.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success {
 background-color: #dff0d8;
}

.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests {
 background-color: #c3e3b5;
}

.table tbody tr.covered-by-small-tests, li.covered-by-small-tests {
 background-color: #99cb84;
}

.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger {
 background-color: #f2dede;
}

.table tbody tr.warning, .table tbody td.warning, li.warning, span.warning {
 background-color: #fcf8e3;
}

.table tbody td.info {
 background-color: #d9edf7;
}

td.big {
 vertical-align: middle;
 width: 117px;
}

td.small {
}

td.codeLine {
 font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
 white-space: pre-wrap;
}

td span.comment {
 color: #888a85;
}

td span.default {
 color: #2e3436;
}

td span.html {
 color: #888a85;
}

td span.keyword {
 color: #2e3436;
 font-weight: bold;
}

pre span.string {
 color: #2e3436;
}

span.success, span.warning, span.danger {
 margin-right: 2px;
 padding-left: 10px;
 padding-right: 10px;
 text-align: center;
}

#toplink {
 position: fixed;
 left: 5px;
 bottom: 5px;
 outline: 0;
}

svg text {
 font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
 font-size: 11px;
 color: #666;
 fill: #666;
}

.scrollbox {
 height:245px;
 overflow-x:scroll;
 overflow-y:scroll;
}

table + .structure-heading {
 border-top: 1px solid lightgrey;
 padding-top: 0.5em;
}

.legend {
 font-weight: bold;
 margin-right: 2px;
 padding-left: 10px;
 padding-right: 10px;
 text-align: center;
}

.covered-by-small-tests {
 background-color: #99cb84;
}

.covered-by-medium-tests {
 background-color: #c3e3b5;
}

.covered-by-large-tests {
 background-color: #dff0d8;
}

.not-covered {
 background-color: #f2dede;
}

.not-coverable {
 background-color: #fcf8e3;
}
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Code Coverage for {{full_path}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <header>
   <div class="container-fluid">
    <div class="row">
     <div class="col-md-12">
      <nav aria-label="breadcrumb">
       <ol class="breadcrumb">
{{breadcrumbs}}
       </ol>
      </nav>
     </div>
    </div>
   </div>
  </header>
  <div class="container-fluid">
   <div class="table-responsive">
    <table class="table table-bordered">
     <thead>
      <tr>
       <td>&nbsp;</td>
       <td colspan="16"><div align="center"><strong>Code Coverage</strong></div></td>
      </tr>
      <tr>
       <td>&nbsp;</td>
       <td colspan="3"><div align="center"><strong>Lines</strong></div></td>
       <td colspan="3"><div align="center"><strong>Branches</strong></div></td>
       <td colspan="3"><div align="center"><strong>Paths</strong></div></td>
       <td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
       <td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
      </tr>
     </thead>
     <tbody>
{{items}}
     </tbody>
    </table>
   </div>
{{lines}}
{{structure}}
   <footer>
    <hr/>
    <h4>Legend</h4>
    {{legend}}
    <p>
     <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
    </p>
    <a title="Back to the top" id="toplink" href="#">
        <svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M12 11L6 5l-6 6h12z"/></svg>
    </a>
   </footer>
  </div>
  <script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/popper.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/bootstrap.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/file.js?v={{version}}" type="text/javascript"></script>
 </body>
</html>
      <tr>
       <td class="{{lines_level}}">{{name}}</td>
       <td class="{{lines_level}} big">{{lines_bar}}</td>
       <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
       <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
       <td class="{{methods_level}} big">{{methods_bar}}</td>
       <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
       <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
       <td class="{{methods_level}} small">{{crap}}</td>
       <td class="{{classes_level}} big">{{classes_bar}}</td>
       <td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
       <td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
      </tr>

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Code Coverage for {{full_path}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <header>
   <div class="container-fluid">
    <div class="row">
     <div class="col-md-12">
      <nav aria-label="breadcrumb">
       <ol class="breadcrumb">
{{breadcrumbs}}
       </ol>
      </nav>
     </div>
    </div>
   </div>
  </header>
  <div class="container-fluid">
   <div class="table-responsive">
    <table class="table table-bordered">
     <thead>
      <tr>
       <td>&nbsp;</td>
       <td colspan="15"><div align="center"><strong>Code Coverage</strong></div></td>
      </tr>
      <tr>
       <td>&nbsp;</td>
       <td colspan="3"><div align="center"><strong>Lines</strong></div></td>
       <td colspan="3"><div align="center"><strong>Branches</strong></div></td>
       <td colspan="3"><div align="center"><strong>Paths</strong></div></td>
       <td colspan="3"><div align="center"><strong>Functions and Methods</strong></div></td>
       <td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
      </tr>
     </thead>
     <tbody>
{{items}}
     </tbody>
    </table>
   </div>
   <footer>
    <hr/>
    <h4>Legend</h4>
    <p>
     <span class="danger"><strong>Low</strong>: 0% to {{low_upper_bound}}%</span>
     <span class="warning"><strong>Medium</strong>: {{low_upper_bound}}% to {{high_lower_bound}}%</span>
     <span class="success"><strong>High</strong>: {{high_lower_bound}}% to 100%</span>
    </p>
    <p>
     <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
    </p>
   </footer>
  </div>
 </body>
</html>
      <tr>
       <td class="{{lines_level}}">{{name}}</td>
       <td class="{{lines_level}} big">{{lines_bar}}</td>
       <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
       <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
       <td class="{{methods_level}} big">{{methods_bar}}</td>
       <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
       <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
       <td class="{{methods_level}} small">{{crap}}</td>
       <td class="{{methods_level}}" colspan="3"></td>
      </tr>

       <div class="progress">
         <div class="progress-bar bg-{{level}}" role="progressbar" aria-valuenow="{{percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{percent}}%">
           <span class="sr-only">{{percent}}% covered ({{level}})</span>
         </div>
       </div>
      <tr>
       <td class="{{lines_level}}">{{name}}</td>
       <td class="{{lines_level}} big">{{lines_bar}}</td>
       <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
       <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
       <td class="{{branches_level}} big">{{branches_bar}}</td>
       <td class="{{branches_level}} small"><div align="right">{{branches_executed_percent}}</div></td>
       <td class="{{branches_level}} small"><div align="right">{{branches_number}}</div></td>
       <td class="{{paths_level}} big">{{paths_bar}}</td>
       <td class="{{paths_level}} small"><div align="right">{{paths_executed_percent}}</div></td>
       <td class="{{paths_level}} small"><div align="right">{{paths_number}}</div></td>
       <td class="{{methods_level}} big">{{methods_bar}}</td>
       <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
       <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
       <td class="{{methods_level}} small">{{crap}}</td>
       <td class="{{methods_level}}" colspan="3"></td>
      </tr>

<table id="code" class="table table-borderless table-condensed">
<tbody>
{{lines}}
</tbody>
</table>
      <tr>
       <td class="{{lines_level}}">{{icon}}{{name}}</td>
       <td class="{{lines_level}} big">{{lines_bar}}</td>
       <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
       <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
       <td class="{{methods_level}} big">{{methods_bar}}</td>
       <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
       <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
       <td class="{{classes_level}} big">{{classes_bar}}</td>
       <td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
       <td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
      </tr>

       <div class="progress">
         <div class="progress-bar bg-{{level}}" role="progressbar" aria-valuenow="{{percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{percent}}%">
           <span class="sr-only">{{percent}}% covered ({{level}})</span>
         </div>
       </div>
      <tr>
       <td class="{{lines_level}}">{{icon}}{{name}}</td>
       <td class="{{lines_level}} big">{{lines_bar}}</td>
       <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
       <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
       <td class="{{branches_level}} big">{{branches_bar}}</td>
       <td class="{{branches_level}} small"><div align="right">{{branches_executed_percent}}</div></td>
       <td class="{{branches_level}} small"><div align="right">{{branches_number}}</div></td>
       <td class="{{paths_level}} big">{{paths_bar}}</td>
       <td class="{{paths_level}} small"><div align="right">{{paths_executed_percent}}</div></td>
       <td class="{{paths_level}} small"><div align="right">{{paths_number}}</div></td>
       <td class="{{methods_level}} big">{{methods_bar}}</td>
       <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
       <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
       <td class="{{classes_level}} big">{{classes_bar}}</td>
       <td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
       <td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
      </tr>

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Dashboard for {{full_path}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/nv.d3.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <header>
   <div class="container-fluid">
    <div class="row">
     <div class="col-md-12">
      <nav aria-label="breadcrumb">
       <ol class="breadcrumb">
{{breadcrumbs}}
       </ol>
      </nav>
     </div>
    </div>
   </div>
  </header>
  <div class="container-fluid">
   <div class="row">
    <div class="col-md-12">
     <h2>Classes</h2>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Coverage Distribution</h3>
     <div id="classCoverageDistribution" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Complexity</h3>
     <div id="classComplexity" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Insufficient Coverage</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Class</th>
         <th class="text-right">Coverage</th>
        </tr>
       </thead>
       <tbody>
{{insufficient_coverage_classes}}
       </tbody>
      </table>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Project Risks</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Class</th>
         <th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
        </tr>
       </thead>
       <tbody>
{{project_risks_classes}}
       </tbody>
      </table>
     </div>
    </div>
   </div>
   <div class="row">
    <div class="col-md-12">
     <h2>Methods</h2>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Coverage Distribution</h3>
     <div id="methodCoverageDistribution" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Complexity</h3>
     <div id="methodComplexity" style="height: 300px;">
       <svg></svg>
     </div>
    </div>
   </div>
   <div class="row">
    <div class="col-md-6">
     <h3>Insufficient Coverage</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Method</th>
         <th class="text-right">Coverage</th>
        </tr>
       </thead>
       <tbody>
{{insufficient_coverage_methods}}
       </tbody>
      </table>
     </div>
    </div>
    <div class="col-md-6">
     <h3>Project Risks</h3>
     <div class="scrollbox">
      <table class="table">
       <thead>
        <tr>
         <th>Method</th>
         <th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
        </tr>
       </thead>
       <tbody>
{{project_risks_methods}}
       </tbody>
      </table>
     </div>
    </div>
   </div>
   <footer>
    <hr/>
    <p>
     <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
    </p>
   </footer>
  </div>
  <script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/d3.min.js?v={{version}}" type="text/javascript"></script>
  <script src="{{path_to_root}}_js/nv.d3.min.js?v={{version}}" type="text/javascript"></script>
  <script type="text/javascript">
$(document).ready(function() {
  nv.addGraph(function() {
    var chart = nv.models.multiBarChart();
    chart.tooltips(false)
      .showControls(false)
      .showLegend(false)
      .reduceXTicks(false)
      .staggerLabels(true)
      .yAxis.tickFormat(d3.format('d'));

    d3.select('#classCoverageDistribution svg')
      .datum(getCoverageDistributionData({{class_coverage_distribution}}, "Class Coverage"))
      .transition().duration(500).call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  nv.addGraph(function() {
    var chart = nv.models.multiBarChart();
    chart.tooltips(false)
      .showControls(false)
      .showLegend(false)
      .reduceXTicks(false)
      .staggerLabels(true)
      .yAxis.tickFormat(d3.format('d'));

    d3.select('#methodCoverageDistribution svg')
      .datum(getCoverageDistributionData({{method_coverage_distribution}}, "Method Coverage"))
      .transition().duration(500).call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  function getCoverageDistributionData(data, label) {
    var labels = [
      '0%',
      '0-10%',
      '10-20%',
      '20-30%',
      '30-40%',
      '40-50%',
      '50-60%',
      '60-70%',
      '70-80%',
      '80-90%',
      '90-100%',
      '100%'
    ];
    var values = [];
    $.each(labels, function(key) {
      values.push({x: labels[key], y: data[key]});
    });

    return [
      {
        key: label,
        values: values,
        color: "#4572A7"
      }
    ];
  }
  nv.addGraph(function() {
    var chart = nv.models.scatterChart()
      .showDistX(true)
      .showDistY(true)
      .showLegend(false)
      .forceX([0, 100]);
    chart.tooltipContent(function(graph) {
      return '<p>' + graph.point.class + '</p>';
    });

    chart.xAxis.axisLabel('Code Coverage (in percent)');
    chart.yAxis.axisLabel('Cyclomatic Complexity');

    d3.select('#classComplexity svg')
      .datum(getComplexityData({{complexity_class}}, 'Class Complexity'))
      .transition()
      .duration(500)
      .call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  nv.addGraph(function() {
    var chart = nv.models.scatterChart()
      .showDistX(true)
      .showDistY(true)
      .showLegend(false)
      .forceX([0, 100]);
    chart.tooltipContent(function(graph) {
      return '<p>' + graph.point.class + '</p>';
    });

    chart.xAxis.axisLabel('Code Coverage (in percent)');
    chart.yAxis.axisLabel('Method Complexity');

    d3.select('#methodComplexity svg')
      .datum(getComplexityData({{complexity_method}}, 'Method Complexity'))
      .transition()
      .duration(500)
      .call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
  });

  function getComplexityData(data, label) {
    var values = [];
    $.each(data, function(key) {
      var value = Math.round(data[key][0]*100) / 100;
      values.push({
        x: value,
        y: data[key][1],
        class: data[key][2],
        size: 0.05,
        shape: 'diamond'
      });
    });

    return [
      {
        key: label,
        values: values,
        color: "#4572A7"
      }
    ];
  }
});
  </script>
 </body>
</html>
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Code Coverage for {{full_path}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
  <link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <header>
   <div class="container-fluid">
    <div class="row">
     <div class="col-md-12">
      <nav aria-label="breadcrumb">
       <ol class="breadcrumb">
{{breadcrumbs}}
       </ol>
      </nav>
     </div>
    </div>
   </div>
  </header>
  <div class="container-fluid">
   <div class="table-responsive">
    <table class="table table-bordered">
     <thead>
      <tr>
       <td>&nbsp;</td>
       <td colspan="9"><div align="center"><strong>Code Coverage</strong></div></td>
      </tr>
      <tr>
       <td>&nbsp;</td>
       <td colspan="3"><div align="center"><strong>Lines</strong></div></td>
       <td colspan="3"><div align="center"><strong>Functions and Methods</strong></div></td>
       <td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
      </tr>
     </thead>
     <tbody>
{{items}}
     </tbody>
    </table>
   </div>
   <footer>
    <hr/>
    <h4>Legend</h4>
    <p>
     <span class="danger"><strong>Low</strong>: 0% to {{low_upper_bound}}%</span>
     <span class="warning"><strong>Medium</strong>: {{low_upper_bound}}% to {{high_lower_bound}}%</span>
     <span class="success"><strong>High</strong>: {{high_lower_bound}}% to 100%</span>
    </p>
    <p>
     <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
    </p>
   </footer>
  </div>
 </body>
</html>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM6 4H1V3h5v1z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M8.5 1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V4.5L8.5 1zM11 14H1V2h7l3 3v9zM5 6.98L3.5 8.5 5 10l-.5 1L2 8.5 4.5 6l.5.98zM7.5 6L10 8.5 7.5 11l-.5-.98L8.5 8.5 7 7l.5-1z"/></svg><?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Html;

use function count;
use function sprintf;
use function str_repeat;
use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\Template\Template;




final class Directory extends Renderer
{
public function render(DirectoryNode $node, string $file): void
{
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_branch.html' : 'directory.html');
$template = new Template($templateName, '{{', '}}');

$this->setCommonTemplateVariables($template, $node);

$items = $this->renderItem($node, true);

foreach ($node->directories() as $item) {
$items .= $this->renderItem($item);
}

foreach ($node->files() as $item) {
$items .= $this->renderItem($item);
}

$template->setVar(
[
'id' => $node->id(),
'items' => $items,
]
);

$template->renderTo($file);
}

private function renderItem(Node $node, bool $total = false): string
{
$data = [
'numClasses' => $node->numberOfClassesAndTraits(),
'numTestedClasses' => $node->numberOfTestedClassesAndTraits(),
'numMethods' => $node->numberOfFunctionsAndMethods(),
'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(),
'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(),
'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(),
'numExecutedLines' => $node->numberOfExecutedLines(),
'numExecutableLines' => $node->numberOfExecutableLines(),
'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(),
'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(),
'numExecutedBranches' => $node->numberOfExecutedBranches(),
'numExecutableBranches' => $node->numberOfExecutableBranches(),
'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(),
'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(),
'numExecutedPaths' => $node->numberOfExecutedPaths(),
'numExecutablePaths' => $node->numberOfExecutablePaths(),
'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(),
'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(),
'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(),
'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(),
];

if ($total) {
$data['name'] = 'Total';
} else {
$up = str_repeat('../', count($node->pathAsArray()) - 2);
$data['icon'] = sprintf('<img src="%s_icons/file-code.svg" class="octicon" />', $up);

if ($node instanceof DirectoryNode) {
$data['name'] = sprintf(
'<a href="%s/index.html">%s</a>',
$node->name(),
$node->name()
);
$data['icon'] = sprintf('<img src="%s_icons/file-directory.svg" class="octicon" />', $up);
} elseif ($this->hasBranchCoverage) {
$data['name'] = sprintf(
'%s <a class="small" href="%s.html">[line]</a> <a class="small" href="%s_branch.html">[branch]</a> <a class="small" href="%s_path.html">[path]</a>',
$node->name(),
$node->name(),
$node->name(),
$node->name()
);
} else {
$data['name'] = sprintf(
'<a href="%s.html">%s</a>',
$node->name(),
$node->name()
);
}
}

$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_item_branch.html' : 'directory_item.html');

return $this->renderItemTemplate(
new Template($templateName, '{{', '}}'),
$data
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Html;

use function array_values;
use function arsort;
use function asort;
use function count;
use function explode;
use function floor;
use function json_encode;
use function sprintf;
use function str_replace;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\Template\Template;




final class Dashboard extends Renderer
{
public function render(DirectoryNode $node, string $file): void
{
$classes = $node->classesAndTraits();
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'dashboard_branch.html' : 'dashboard.html');
$template = new Template(
$templateName,
'{{',
'}}'
);

$this->setCommonTemplateVariables($template, $node);

$baseLink = $node->id() . '/';
$complexity = $this->complexity($classes, $baseLink);
$coverageDistribution = $this->coverageDistribution($classes);
$insufficientCoverage = $this->insufficientCoverage($classes, $baseLink);
$projectRisks = $this->projectRisks($classes, $baseLink);

$template->setVar(
[
'insufficient_coverage_classes' => $insufficientCoverage['class'],
'insufficient_coverage_methods' => $insufficientCoverage['method'],
'project_risks_classes' => $projectRisks['class'],
'project_risks_methods' => $projectRisks['method'],
'complexity_class' => $complexity['class'],
'complexity_method' => $complexity['method'],
'class_coverage_distribution' => $coverageDistribution['class'],
'method_coverage_distribution' => $coverageDistribution['method'],
]
);

$template->renderTo($file);
}

protected function activeBreadcrumb(AbstractNode $node): string
{
return sprintf(
'         <li class="breadcrumb-item"><a href="index.html">%s</a></li>' . "\n" .
'         <li class="breadcrumb-item active">(Dashboard)</li>' . "\n",
$node->name()
);
}




private function complexity(array $classes, string $baseLink): array
{
$result = ['class' => [], 'method' => []];

foreach ($classes as $className => $class) {
foreach ($class['methods'] as $methodName => $method) {
if ($className !== '*') {
$methodName = $className . '::' . $methodName;
}

$result['method'][] = [
$method['coverage'],
$method['ccn'],
sprintf(
'<a href="%s">%s</a>',
str_replace($baseLink, '', $method['link']),
$methodName
),
];
}

$result['class'][] = [
$class['coverage'],
$class['ccn'],
sprintf(
'<a href="%s">%s</a>',
str_replace($baseLink, '', $class['link']),
$className
),
];
}

return [
'class' => json_encode($result['class']),
'method' => json_encode($result['method']),
];
}




private function coverageDistribution(array $classes): array
{
$result = [
'class' => [
'0%' => 0,
'0-10%' => 0,
'10-20%' => 0,
'20-30%' => 0,
'30-40%' => 0,
'40-50%' => 0,
'50-60%' => 0,
'60-70%' => 0,
'70-80%' => 0,
'80-90%' => 0,
'90-100%' => 0,
'100%' => 0,
],
'method' => [
'0%' => 0,
'0-10%' => 0,
'10-20%' => 0,
'20-30%' => 0,
'30-40%' => 0,
'40-50%' => 0,
'50-60%' => 0,
'60-70%' => 0,
'70-80%' => 0,
'80-90%' => 0,
'90-100%' => 0,
'100%' => 0,
],
];

foreach ($classes as $class) {
foreach ($class['methods'] as $methodName => $method) {
if ($method['coverage'] === 0) {
$result['method']['0%']++;
} elseif ($method['coverage'] === 100) {
$result['method']['100%']++;
} else {
$key = floor($method['coverage'] / 10) * 10;
$key = $key . '-' . ($key + 10) . '%';
$result['method'][$key]++;
}
}

if ($class['coverage'] === 0) {
$result['class']['0%']++;
} elseif ($class['coverage'] === 100) {
$result['class']['100%']++;
} else {
$key = floor($class['coverage'] / 10) * 10;
$key = $key . '-' . ($key + 10) . '%';
$result['class'][$key]++;
}
}

return [
'class' => json_encode(array_values($result['class'])),
'method' => json_encode(array_values($result['method'])),
];
}




private function insufficientCoverage(array $classes, string $baseLink): array
{
$leastTestedClasses = [];
$leastTestedMethods = [];
$result = ['class' => '', 'method' => ''];

foreach ($classes as $className => $class) {
foreach ($class['methods'] as $methodName => $method) {
if ($method['coverage'] < $this->highLowerBound) {
$key = $methodName;

if ($className !== '*') {
$key = $className . '::' . $methodName;
}

$leastTestedMethods[$key] = $method['coverage'];
}
}

if ($class['coverage'] < $this->highLowerBound) {
$leastTestedClasses[$className] = $class['coverage'];
}
}

asort($leastTestedClasses);
asort($leastTestedMethods);

foreach ($leastTestedClasses as $className => $coverage) {
$result['class'] .= sprintf(
'       <tr><td><a href="%s">%s</a></td><td class="text-right">%d%%</td></tr>' . "\n",
str_replace($baseLink, '', $classes[$className]['link']),
$className,
$coverage
);
}

foreach ($leastTestedMethods as $methodName => $coverage) {
[$class, $method] = explode('::', $methodName);

$result['method'] .= sprintf(
'       <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d%%</td></tr>' . "\n",
str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
$methodName,
$method,
$coverage
);
}

return $result;
}




private function projectRisks(array $classes, string $baseLink): array
{
$classRisks = [];
$methodRisks = [];
$result = ['class' => '', 'method' => ''];

foreach ($classes as $className => $class) {
foreach ($class['methods'] as $methodName => $method) {
if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) {
$key = $methodName;

if ($className !== '*') {
$key = $className . '::' . $methodName;
}

$methodRisks[$key] = $method['crap'];
}
}

if ($class['coverage'] < $this->highLowerBound &&
$class['ccn'] > count($class['methods'])) {
$classRisks[$className] = $class['crap'];
}
}

arsort($classRisks);
arsort($methodRisks);

foreach ($classRisks as $className => $crap) {
$result['class'] .= sprintf(
'       <tr><td><a href="%s">%s</a></td><td class="text-right">%d</td></tr>' . "\n",
str_replace($baseLink, '', $classes[$className]['link']),
$className,
$crap
);
}

foreach ($methodRisks as $methodName => $crap) {
[$class, $method] = explode('::', $methodName);

$result['method'] .= sprintf(
'       <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d</td></tr>' . "\n",
str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
$methodName,
$method,
$crap
);
}

return $result;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Html;

use const ENT_COMPAT;
use const ENT_HTML401;
use const ENT_SUBSTITUTE;
use const T_ABSTRACT;
use const T_ARRAY;
use const T_AS;
use const T_BREAK;
use const T_CALLABLE;
use const T_CASE;
use const T_CATCH;
use const T_CLASS;
use const T_CLONE;
use const T_COMMENT;
use const T_CONST;
use const T_CONTINUE;
use const T_DECLARE;
use const T_DEFAULT;
use const T_DO;
use const T_DOC_COMMENT;
use const T_ECHO;
use const T_ELSE;
use const T_ELSEIF;
use const T_EMPTY;
use const T_ENDDECLARE;
use const T_ENDFOR;
use const T_ENDFOREACH;
use const T_ENDIF;
use const T_ENDSWITCH;
use const T_ENDWHILE;
use const T_EVAL;
use const T_EXIT;
use const T_EXTENDS;
use const T_FINAL;
use const T_FINALLY;
use const T_FOR;
use const T_FOREACH;
use const T_FUNCTION;
use const T_GLOBAL;
use const T_GOTO;
use const T_HALT_COMPILER;
use const T_IF;
use const T_IMPLEMENTS;
use const T_INCLUDE;
use const T_INCLUDE_ONCE;
use const T_INLINE_HTML;
use const T_INSTANCEOF;
use const T_INSTEADOF;
use const T_INTERFACE;
use const T_ISSET;
use const T_LIST;
use const T_NAMESPACE;
use const T_NEW;
use const T_PRINT;
use const T_PRIVATE;
use const T_PROTECTED;
use const T_PUBLIC;
use const T_REQUIRE;
use const T_REQUIRE_ONCE;
use const T_RETURN;
use const T_STATIC;
use const T_SWITCH;
use const T_THROW;
use const T_TRAIT;
use const T_TRY;
use const T_UNSET;
use const T_USE;
use const T_VAR;
use const T_WHILE;
use const T_YIELD;
use const T_YIELD_FROM;
use function array_key_exists;
use function array_keys;
use function array_merge;
use function array_pop;
use function array_unique;
use function constant;
use function count;
use function defined;
use function explode;
use function file_get_contents;
use function htmlspecialchars;
use function is_string;
use function ksort;
use function range;
use function sort;
use function sprintf;
use function str_replace;
use function substr;
use function token_get_all;
use function trim;
use PHPUnit\Runner\BaseTestRunner;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Util\Percentage;
use SebastianBergmann\Template\Template;




final class File extends Renderer
{



private static $keywordTokens = [];




private static $formattedSourceCache = [];




private $htmlSpecialCharsFlags = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE;

public function render(FileNode $node, string $file): void
{
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_branch.html' : 'file.html');
$template = new Template($templateName, '{{', '}}');
$this->setCommonTemplateVariables($template, $node);

$template->setVar(
[
'items' => $this->renderItems($node),
'lines' => $this->renderSourceWithLineCoverage($node),
'legend' => '<p><span class="legend covered-by-small-tests">Covered by small (and larger) tests</span><span class="legend covered-by-medium-tests">Covered by medium (and large) tests</span><span class="legend covered-by-large-tests">Covered by large tests (and tests of unknown size)</span><span class="legend not-covered">Not covered</span><span class="legend not-coverable">Not coverable</span></p>',
'structure' => '',
]
);

$template->renderTo($file . '.html');

if ($this->hasBranchCoverage) {
$template->setVar(
[
'items' => $this->renderItems($node),
'lines' => $this->renderSourceWithBranchCoverage($node),
'legend' => '<p><span class="success"><strong>Fully covered</strong></span><span class="warning"><strong>Partially covered</strong></span><span class="danger"><strong>Not covered</strong></span></p>',
'structure' => $this->renderBranchStructure($node),
]
);

$template->renderTo($file . '_branch.html');

$template->setVar(
[
'items' => $this->renderItems($node),
'lines' => $this->renderSourceWithPathCoverage($node),
'legend' => '<p><span class="success"><strong>Fully covered</strong></span><span class="warning"><strong>Partially covered</strong></span><span class="danger"><strong>Not covered</strong></span></p>',
'structure' => $this->renderPathStructure($node),
]
);

$template->renderTo($file . '_path.html');
}
}

private function renderItems(FileNode $node): string
{
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_item_branch.html' : 'file_item.html');
$template = new Template($templateName, '{{', '}}');

$methodTemplateName = $this->templatePath . ($this->hasBranchCoverage ? 'method_item_branch.html' : 'method_item.html');
$methodItemTemplate = new Template(
$methodTemplateName,
'{{',
'}}'
);

$items = $this->renderItemTemplate(
$template,
[
'name' => 'Total',
'numClasses' => $node->numberOfClassesAndTraits(),
'numTestedClasses' => $node->numberOfTestedClassesAndTraits(),
'numMethods' => $node->numberOfFunctionsAndMethods(),
'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(),
'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(),
'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(),
'numExecutedLines' => $node->numberOfExecutedLines(),
'numExecutableLines' => $node->numberOfExecutableLines(),
'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(),
'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(),
'numExecutedBranches' => $node->numberOfExecutedBranches(),
'numExecutableBranches' => $node->numberOfExecutableBranches(),
'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(),
'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(),
'numExecutedPaths' => $node->numberOfExecutedPaths(),
'numExecutablePaths' => $node->numberOfExecutablePaths(),
'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(),
'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(),
'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(),
'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(),
'crap' => '<abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr>',
]
);

$items .= $this->renderFunctionItems(
$node->functions(),
$methodItemTemplate
);

$items .= $this->renderTraitOrClassItems(
$node->traits(),
$template,
$methodItemTemplate
);

$items .= $this->renderTraitOrClassItems(
$node->classes(),
$template,
$methodItemTemplate
);

return $items;
}

private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate): string
{
$buffer = '';

if (empty($items)) {
return $buffer;
}

foreach ($items as $name => $item) {
$numMethods = 0;
$numTestedMethods = 0;

foreach ($item['methods'] as $method) {
if ($method['executableLines'] > 0) {
$numMethods++;

if ($method['executedLines'] === $method['executableLines']) {
$numTestedMethods++;
}
}
}

if ($item['executableLines'] > 0) {
$numClasses = 1;
$numTestedClasses = $numTestedMethods === $numMethods ? 1 : 0;
$linesExecutedPercentAsString = Percentage::fromFractionAndTotal(
$item['executedLines'],
$item['executableLines']
)->asString();
$branchesExecutedPercentAsString = Percentage::fromFractionAndTotal(
$item['executedBranches'],
$item['executableBranches']
)->asString();
$pathsExecutedPercentAsString = Percentage::fromFractionAndTotal(
$item['executedPaths'],
$item['executablePaths']
)->asString();
} else {
$numClasses = 0;
$numTestedClasses = 0;
$linesExecutedPercentAsString = 'n/a';
$branchesExecutedPercentAsString = 'n/a';
$pathsExecutedPercentAsString = 'n/a';
}

$testedMethodsPercentage = Percentage::fromFractionAndTotal(
$numTestedMethods,
$numMethods
);

$testedClassesPercentage = Percentage::fromFractionAndTotal(
$numTestedMethods === $numMethods ? 1 : 0,
1
);

$buffer .= $this->renderItemTemplate(
$template,
[
'name' => $this->abbreviateClassName($name),
'numClasses' => $numClasses,
'numTestedClasses' => $numTestedClasses,
'numMethods' => $numMethods,
'numTestedMethods' => $numTestedMethods,
'linesExecutedPercent' => Percentage::fromFractionAndTotal(
$item['executedLines'],
$item['executableLines'],
)->asFloat(),
'linesExecutedPercentAsString' => $linesExecutedPercentAsString,
'numExecutedLines' => $item['executedLines'],
'numExecutableLines' => $item['executableLines'],
'branchesExecutedPercent' => Percentage::fromFractionAndTotal(
$item['executedBranches'],
$item['executableBranches'],
)->asFloat(),
'branchesExecutedPercentAsString' => $branchesExecutedPercentAsString,
'numExecutedBranches' => $item['executedBranches'],
'numExecutableBranches' => $item['executableBranches'],
'pathsExecutedPercent' => Percentage::fromFractionAndTotal(
$item['executedPaths'],
$item['executablePaths']
)->asFloat(),
'pathsExecutedPercentAsString' => $pathsExecutedPercentAsString,
'numExecutedPaths' => $item['executedPaths'],
'numExecutablePaths' => $item['executablePaths'],
'testedMethodsPercent' => $testedMethodsPercentage->asFloat(),
'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(),
'testedClassesPercent' => $testedClassesPercentage->asFloat(),
'testedClassesPercentAsString' => $testedClassesPercentage->asString(),
'crap' => $item['crap'],
]
);

foreach ($item['methods'] as $method) {
$buffer .= $this->renderFunctionOrMethodItem(
$methodItemTemplate,
$method,
'&nbsp;'
);
}
}

return $buffer;
}

private function renderFunctionItems(array $functions, Template $template): string
{
if (empty($functions)) {
return '';
}

$buffer = '';

foreach ($functions as $function) {
$buffer .= $this->renderFunctionOrMethodItem(
$template,
$function
);
}

return $buffer;
}

private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = ''): string
{
$numMethods = 0;
$numTestedMethods = 0;

if ($item['executableLines'] > 0) {
$numMethods = 1;

if ($item['executedLines'] === $item['executableLines']) {
$numTestedMethods = 1;
}
}

$executedLinesPercentage = Percentage::fromFractionAndTotal(
$item['executedLines'],
$item['executableLines']
);

$executedBranchesPercentage = Percentage::fromFractionAndTotal(
$item['executedBranches'],
$item['executableBranches']
);

$executedPathsPercentage = Percentage::fromFractionAndTotal(
$item['executedPaths'],
$item['executablePaths']
);

$testedMethodsPercentage = Percentage::fromFractionAndTotal(
$numTestedMethods,
1
);

return $this->renderItemTemplate(
$template,
[
'name' => sprintf(
'%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
$indent,
$item['startLine'],
htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags),
$item['functionName'] ?? $item['methodName']
),
'numMethods' => $numMethods,
'numTestedMethods' => $numTestedMethods,
'linesExecutedPercent' => $executedLinesPercentage->asFloat(),
'linesExecutedPercentAsString' => $executedLinesPercentage->asString(),
'numExecutedLines' => $item['executedLines'],
'numExecutableLines' => $item['executableLines'],
'branchesExecutedPercent' => $executedBranchesPercentage->asFloat(),
'branchesExecutedPercentAsString' => $executedBranchesPercentage->asString(),
'numExecutedBranches' => $item['executedBranches'],
'numExecutableBranches' => $item['executableBranches'],
'pathsExecutedPercent' => $executedPathsPercentage->asFloat(),
'pathsExecutedPercentAsString' => $executedPathsPercentage->asString(),
'numExecutedPaths' => $item['executedPaths'],
'numExecutablePaths' => $item['executablePaths'],
'testedMethodsPercent' => $testedMethodsPercentage->asFloat(),
'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(),
'crap' => $item['crap'],
]
);
}

private function renderSourceWithLineCoverage(FileNode $node): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');

$coverageData = $node->lineCoverageData();
$testData = $node->testData();
$codeLines = $this->loadFile($node->pathAsString());
$lines = '';
$i = 1;

foreach ($codeLines as $line) {
$trClass = '';
$popoverContent = '';
$popoverTitle = '';

if (array_key_exists($i, $coverageData)) {
$numTests = ($coverageData[$i] ? count($coverageData[$i]) : 0);

if ($coverageData[$i] === null) {
$trClass = 'warning';
} elseif ($numTests === 0) {
$trClass = 'danger';
} else {
if ($numTests > 1) {
$popoverTitle = $numTests . ' tests cover line ' . $i;
} else {
$popoverTitle = '1 test covers line ' . $i;
}

$lineCss = 'covered-by-large-tests';
$popoverContent = '<ul>';

foreach ($coverageData[$i] as $test) {
if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') {
$lineCss = 'covered-by-medium-tests';
} elseif ($testData[$test]['size'] === 'small') {
$lineCss = 'covered-by-small-tests';
}

$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
}

$popoverContent .= '</ul>';
$trClass = $lineCss . ' popin';
}
}

$popover = '';

if (!empty($popoverTitle)) {
$popover = sprintf(
' data-title="%s" data-content="%s" data-placement="top" data-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)
);
}

$lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover);

$i++;
}

$linesTemplate->setVar(['lines' => $lines]);

return $linesTemplate->render();
}

private function renderSourceWithBranchCoverage(FileNode $node): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');

$functionCoverageData = $node->functionCoverageData();
$testData = $node->testData();
$codeLines = $this->loadFile($node->pathAsString());

$lineData = [];


foreach (array_keys($codeLines) as $line) {
$lineData[$line + 1] = [
'includedInBranches' => 0,
'includedInHitBranches' => 0,
'tests' => [],
];
}

foreach ($functionCoverageData as $method) {
foreach ($method['branches'] as $branch) {
foreach (range($branch['line_start'], $branch['line_end']) as $line) {
if (!isset($lineData[$line])) { 
continue;
}

$lineData[$line]['includedInBranches']++;

if ($branch['hit']) {
$lineData[$line]['includedInHitBranches']++;
$lineData[$line]['tests'] = array_unique(array_merge($lineData[$line]['tests'], $branch['hit']));
}
}
}
}

$lines = '';
$i = 1;


foreach ($codeLines as $line) {
$trClass = '';
$popover = '';

if ($lineData[$i]['includedInBranches'] > 0) {
$lineCss = 'success';

if ($lineData[$i]['includedInHitBranches'] === 0) {
$lineCss = 'danger';
} elseif ($lineData[$i]['includedInHitBranches'] !== $lineData[$i]['includedInBranches']) {
$lineCss = 'warning';
}

$popoverContent = '<ul>';

if (count($lineData[$i]['tests']) === 1) {
$popoverTitle = '1 test covers line ' . $i;
} else {
$popoverTitle = count($lineData[$i]['tests']) . ' tests cover line ' . $i;
}
$popoverTitle .= '. These are covering ' . $lineData[$i]['includedInHitBranches'] . ' out of the ' . $lineData[$i]['includedInBranches'] . ' code branches.';

foreach ($lineData[$i]['tests'] as $test) {
$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
}

$popoverContent .= '</ul>';
$trClass = $lineCss . ' popin';

$popover = sprintf(
' data-title="%s" data-content="%s" data-placement="top" data-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)
);
}

$lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover);

$i++;
}

$linesTemplate->setVar(['lines' => $lines]);

return $linesTemplate->render();
}

private function renderSourceWithPathCoverage(FileNode $node): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');

$functionCoverageData = $node->functionCoverageData();
$testData = $node->testData();
$codeLines = $this->loadFile($node->pathAsString());

$lineData = [];


foreach (array_keys($codeLines) as $line) {
$lineData[$line + 1] = [
'includedInPaths' => [],
'includedInHitPaths' => [],
'tests' => [],
];
}

foreach ($functionCoverageData as $method) {
foreach ($method['paths'] as $pathId => $path) {
foreach ($path['path'] as $branchTaken) {
foreach (range($method['branches'][$branchTaken]['line_start'], $method['branches'][$branchTaken]['line_end']) as $line) {
if (!isset($lineData[$line])) {
continue;
}
$lineData[$line]['includedInPaths'][] = $pathId;

if ($path['hit']) {
$lineData[$line]['includedInHitPaths'][] = $pathId;
$lineData[$line]['tests'] = array_unique(array_merge($lineData[$line]['tests'], $path['hit']));
}
}
}
}
}

$lines = '';
$i = 1;


foreach ($codeLines as $line) {
$trClass = '';
$popover = '';
$includedInPathsCount = count(array_unique($lineData[$i]['includedInPaths']));
$includedInHitPathsCount = count(array_unique($lineData[$i]['includedInHitPaths']));

if ($includedInPathsCount > 0) {
$lineCss = 'success';

if ($includedInHitPathsCount === 0) {
$lineCss = 'danger';
} elseif ($includedInHitPathsCount !== $includedInPathsCount) {
$lineCss = 'warning';
}

$popoverContent = '<ul>';

if (count($lineData[$i]['tests']) === 1) {
$popoverTitle = '1 test covers line ' . $i;
} else {
$popoverTitle = count($lineData[$i]['tests']) . ' tests cover line ' . $i;
}
$popoverTitle .= '. These are covering ' . $includedInHitPathsCount . ' out of the ' . $includedInPathsCount . ' code paths.';

foreach ($lineData[$i]['tests'] as $test) {
$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
}

$popoverContent .= '</ul>';
$trClass = $lineCss . ' popin';

$popover = sprintf(
' data-title="%s" data-content="%s" data-placement="top" data-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)
);
}

$lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover);

$i++;
}

$linesTemplate->setVar(['lines' => $lines]);

return $linesTemplate->render();
}

private function renderBranchStructure(FileNode $node): string
{
$branchesTemplate = new Template($this->templatePath . 'branches.html.dist', '{{', '}}');

$coverageData = $node->functionCoverageData();
$testData = $node->testData();
$codeLines = $this->loadFile($node->pathAsString());
$branches = '';

ksort($coverageData);

foreach ($coverageData as $methodName => $methodData) {
if (!$methodData['branches']) {
continue;
}

$branchStructure = '';

foreach ($methodData['branches'] as $branch) {
$branchStructure .= $this->renderBranchLines($branch, $codeLines, $testData);
}

if ($branchStructure !== '') { 
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, $this->htmlSpecialCharsFlags) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
$branches .= $branchStructure;
}
}

$branchesTemplate->setVar(['branches' => $branches]);

return $branchesTemplate->render();
}

private function renderBranchLines(array $branch, array $codeLines, array $testData): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');

$lines = '';

$branchLines = range($branch['line_start'], $branch['line_end']);
sort($branchLines); 


foreach ($branchLines as $line) {
if (!isset($codeLines[$line])) { 
continue;
}

$popoverContent = '';
$popoverTitle = '';

$numTests = count($branch['hit']);

if ($numTests === 0) {
$trClass = 'danger';
} else {
$lineCss = 'covered-by-large-tests';
$popoverContent = '<ul>';

if ($numTests > 1) {
$popoverTitle = $numTests . ' tests cover this branch';
} else {
$popoverTitle = '1 test covers this branch';
}

foreach ($branch['hit'] as $test) {
if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') {
$lineCss = 'covered-by-medium-tests';
} elseif ($testData[$test]['size'] === 'small') {
$lineCss = 'covered-by-small-tests';
}

$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
}
$trClass = $lineCss . ' popin';
}

$popover = '';

if (!empty($popoverTitle)) {
$popover = sprintf(
' data-title="%s" data-content="%s" data-placement="top" data-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)
);
}

$lines .= $this->renderLine($singleLineTemplate, $line, $codeLines[$line - 1], $trClass, $popover);
}

if ($lines === '') {
return '';
}

$linesTemplate->setVar(['lines' => $lines]);

return $linesTemplate->render();
}

private function renderPathStructure(FileNode $node): string
{
$pathsTemplate = new Template($this->templatePath . 'paths.html.dist', '{{', '}}');

$coverageData = $node->functionCoverageData();
$testData = $node->testData();
$codeLines = $this->loadFile($node->pathAsString());
$paths = '';

ksort($coverageData);

foreach ($coverageData as $methodName => $methodData) {
if (!$methodData['paths']) {
continue;
}

$pathStructure = '';

if (count($methodData['paths']) > 100) {
$pathStructure .= '<p>' . count($methodData['paths']) . ' is too many paths to sensibly render, consider refactoring your code to bring this number down.</p>';

continue;
}

foreach ($methodData['paths'] as $path) {
$pathStructure .= $this->renderPathLines($path, $methodData['branches'], $codeLines, $testData);
}

if ($pathStructure !== '') {
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, $this->htmlSpecialCharsFlags) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
$paths .= $pathStructure;
}
}

$pathsTemplate->setVar(['paths' => $paths]);

return $pathsTemplate->render();
}

private function renderPathLines(array $path, array $branches, array $codeLines, array $testData): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');

$lines = '';
$first = true;

foreach ($path['path'] as $branchId) {
if ($first) {
$first = false;
} else {
$lines .= '    <tr><td colspan="2">&nbsp;</td></tr>' . "\n";
}

$branchLines = range($branches[$branchId]['line_start'], $branches[$branchId]['line_end']);
sort($branchLines); 


foreach ($branchLines as $line) {
if (!isset($codeLines[$line])) { 
continue;
}

$popoverContent = '';
$popoverTitle = '';

$numTests = count($path['hit']);

if ($numTests === 0) {
$trClass = 'danger';
} else {
$lineCss = 'covered-by-large-tests';
$popoverContent = '<ul>';

if ($numTests > 1) {
$popoverTitle = $numTests . ' tests cover this path';
} else {
$popoverTitle = '1 test covers this path';
}

foreach ($path['hit'] as $test) {
if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') {
$lineCss = 'covered-by-medium-tests';
} elseif ($testData[$test]['size'] === 'small') {
$lineCss = 'covered-by-small-tests';
}

$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
}

$trClass = $lineCss . ' popin';
}

$popover = '';

if (!empty($popoverTitle)) {
$popover = sprintf(
' data-title="%s" data-content="%s" data-placement="top" data-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)
);
}

$lines .= $this->renderLine($singleLineTemplate, $line, $codeLines[$line - 1], $trClass, $popover);
}
}

if ($lines === '') {
return '';
}

$linesTemplate->setVar(['lines' => $lines]);

return $linesTemplate->render();
}

private function renderLine(Template $template, int $lineNumber, string $lineContent, string $class, string $popover): string
{
$template->setVar(
[
'lineNumber' => $lineNumber,
'lineContent' => $lineContent,
'class' => $class,
'popover' => $popover,
]
);

return $template->render();
}

private function loadFile(string $file): array
{
if (isset(self::$formattedSourceCache[$file])) {
return self::$formattedSourceCache[$file];
}

$buffer = file_get_contents($file);
$tokens = token_get_all($buffer);
$result = [''];
$i = 0;
$stringFlag = false;
$fileEndsWithNewLine = substr($buffer, -1) === "\n";

unset($buffer);

foreach ($tokens as $j => $token) {
if (is_string($token)) {
if ($token === '"' && $tokens[$j - 1] !== '\\') {
$result[$i] .= sprintf(
'<span class="string">%s</span>',
htmlspecialchars($token, $this->htmlSpecialCharsFlags)
);

$stringFlag = !$stringFlag;
} else {
$result[$i] .= sprintf(
'<span class="keyword">%s</span>',
htmlspecialchars($token, $this->htmlSpecialCharsFlags)
);
}

continue;
}

[$token, $value] = $token;

$value = str_replace(
["\t", ' '],
['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
htmlspecialchars($value, $this->htmlSpecialCharsFlags)
);

if ($value === "\n") {
$result[++$i] = '';
} else {
$lines = explode("\n", $value);

foreach ($lines as $jj => $line) {
$line = trim($line);

if ($line !== '') {
if ($stringFlag) {
$colour = 'string';
} else {
$colour = 'default';

if ($this->isInlineHtml($token)) {
$colour = 'html';
} elseif ($this->isComment($token)) {
$colour = 'comment';
} elseif ($this->isKeyword($token)) {
$colour = 'keyword';
}
}

$result[$i] .= sprintf(
'<span class="%s">%s</span>',
$colour,
$line
);
}

if (isset($lines[$jj + 1])) {
$result[++$i] = '';
}
}
}
}

if ($fileEndsWithNewLine) {
unset($result[count($result) - 1]);
}

self::$formattedSourceCache[$file] = $result;

return $result;
}

private function abbreviateClassName(string $className): string
{
$tmp = explode('\\', $className);

if (count($tmp) > 1) {
$className = sprintf(
'<abbr title="%s">%s</abbr>',
$className,
array_pop($tmp)
);
}

return $className;
}

private function abbreviateMethodName(string $methodName): string
{
$parts = explode('->', $methodName);

if (count($parts) === 2) {
return $this->abbreviateClassName($parts[0]) . '->' . $parts[1];
}

return $methodName;
}

private function createPopoverContentForTest(string $test, array $testData): string
{
$testCSS = '';

if ($testData['fromTestcase']) {
switch ($testData['status']) {
case BaseTestRunner::STATUS_PASSED:
switch ($testData['size']) {
case 'small':
$testCSS = ' class="covered-by-small-tests"';

break;

case 'medium':
$testCSS = ' class="covered-by-medium-tests"';

break;

default:
$testCSS = ' class="covered-by-large-tests"';

break;
}

break;

case BaseTestRunner::STATUS_SKIPPED:
case BaseTestRunner::STATUS_INCOMPLETE:
case BaseTestRunner::STATUS_RISKY:
case BaseTestRunner::STATUS_WARNING:
$testCSS = ' class="warning"';

break;

case BaseTestRunner::STATUS_FAILURE:
case BaseTestRunner::STATUS_ERROR:
$testCSS = ' class="danger"';

break;
}
}

return sprintf(
'<li%s>%s</li>',
$testCSS,
htmlspecialchars($test, $this->htmlSpecialCharsFlags)
);
}

private function isComment(int $token): bool
{
return $token === T_COMMENT || $token === T_DOC_COMMENT;
}

private function isInlineHtml(int $token): bool
{
return $token === T_INLINE_HTML;
}

private function isKeyword(int $token): bool
{
return isset(self::keywordTokens()[$token]);
}




private static function keywordTokens(): array
{
if (self::$keywordTokens !== []) {
return self::$keywordTokens;
}

self::$keywordTokens = [
T_ABSTRACT => true,
T_ARRAY => true,
T_AS => true,
T_BREAK => true,
T_CALLABLE => true,
T_CASE => true,
T_CATCH => true,
T_CLASS => true,
T_CLONE => true,
T_CONST => true,
T_CONTINUE => true,
T_DECLARE => true,
T_DEFAULT => true,
T_DO => true,
T_ECHO => true,
T_ELSE => true,
T_ELSEIF => true,
T_EMPTY => true,
T_ENDDECLARE => true,
T_ENDFOR => true,
T_ENDFOREACH => true,
T_ENDIF => true,
T_ENDSWITCH => true,
T_ENDWHILE => true,
T_EVAL => true,
T_EXIT => true,
T_EXTENDS => true,
T_FINAL => true,
T_FINALLY => true,
T_FOR => true,
T_FOREACH => true,
T_FUNCTION => true,
T_GLOBAL => true,
T_GOTO => true,
T_HALT_COMPILER => true,
T_IF => true,
T_IMPLEMENTS => true,
T_INCLUDE => true,
T_INCLUDE_ONCE => true,
T_INSTANCEOF => true,
T_INSTEADOF => true,
T_INTERFACE => true,
T_ISSET => true,
T_LIST => true,
T_NAMESPACE => true,
T_NEW => true,
T_PRINT => true,
T_PRIVATE => true,
T_PROTECTED => true,
T_PUBLIC => true,
T_REQUIRE => true,
T_REQUIRE_ONCE => true,
T_RETURN => true,
T_STATIC => true,
T_SWITCH => true,
T_THROW => true,
T_TRAIT => true,
T_TRY => true,
T_UNSET => true,
T_USE => true,
T_VAR => true,
T_WHILE => true,
T_YIELD => true,
T_YIELD_FROM => true,
];

if (defined('T_FN')) {
self::$keywordTokens[constant('T_FN')] = true;
}

if (defined('T_MATCH')) {
self::$keywordTokens[constant('T_MATCH')] = true;
}

if (defined('T_ENUM')) {
self::$keywordTokens[constant('T_ENUM')] = true;
}

if (defined('T_READONLY')) {
self::$keywordTokens[constant('T_READONLY')] = true;
}

return self::$keywordTokens;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report;

use function dirname;
use function file_put_contents;
use function serialize;
use function strpos;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Util\Filesystem;

final class PHP
{
public function process(CodeCoverage $coverage, ?string $target = null): string
{
$coverage->clearCache();

$buffer = "<?php
return \unserialize(<<<'END_OF_COVERAGE_SERIALIZATION'" . PHP_EOL . serialize($coverage) . PHP_EOL . 'END_OF_COVERAGE_SERIALIZATION' . PHP_EOL . ');';

if ($target !== null) {
if (!strpos($target, '://') !== false) {
Filesystem::createDirectory(dirname($target));
}

if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);
}
}

return $buffer;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;




final class Directory extends Node
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMElement;




final class Tests
{
private $contextNode;
private $codeMap = [
-1 => 'UNKNOWN', 
0 => 'PASSED', 
1 => 'SKIPPED', 
2 => 'INCOMPLETE', 
3 => 'FAILURE', 
4 => 'ERROR', 
5 => 'RISKY', 
6 => 'WARNING', 
];

public function __construct(DOMElement $context)
{
$this->contextNode = $context;
}

public function addTest(string $test, array $result): void
{
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'test'
)
);

$node->setAttribute('name', $test);
$node->setAttribute('size', $result['size']);
$node->setAttribute('result', (string) $result['status']);
$node->setAttribute('status', $this->codeMap[(int) $result['status']]);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use function sprintf;
use DOMElement;
use DOMNode;
use SebastianBergmann\CodeCoverage\Util\Percentage;




final class Totals
{



private $container;




private $linesNode;




private $methodsNode;




private $functionsNode;




private $classesNode;




private $traitsNode;

public function __construct(DOMElement $container)
{
$this->container = $container;
$dom = $container->ownerDocument;

$this->linesNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'lines'
);

$this->methodsNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'methods'
);

$this->functionsNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'functions'
);

$this->classesNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'classes'
);

$this->traitsNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'traits'
);

$container->appendChild($this->linesNode);
$container->appendChild($this->methodsNode);
$container->appendChild($this->functionsNode);
$container->appendChild($this->classesNode);
$container->appendChild($this->traitsNode);
}

public function container(): DOMNode
{
return $this->container;
}

public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void
{
$this->linesNode->setAttribute('total', (string) $loc);
$this->linesNode->setAttribute('comments', (string) $cloc);
$this->linesNode->setAttribute('code', (string) $ncloc);
$this->linesNode->setAttribute('executable', (string) $executable);
$this->linesNode->setAttribute('executed', (string) $executed);
$this->linesNode->setAttribute(
'percent',
$executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat())
);
}

public function setNumClasses(int $count, int $tested): void
{
$this->classesNode->setAttribute('count', (string) $count);
$this->classesNode->setAttribute('tested', (string) $tested);
$this->classesNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
);
}

public function setNumTraits(int $count, int $tested): void
{
$this->traitsNode->setAttribute('count', (string) $count);
$this->traitsNode->setAttribute('tested', (string) $tested);
$this->traitsNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
);
}

public function setNumMethods(int $count, int $tested): void
{
$this->methodsNode->setAttribute('count', (string) $count);
$this->methodsNode->setAttribute('tested', (string) $tested);
$this->methodsNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
);
}

public function setNumFunctions(int $count, int $tested): void
{
$this->functionsNode->setAttribute('count', (string) $count);
$this->functionsNode->setAttribute('tested', (string) $tested);
$this->functionsNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMElement;
use TheSeer\Tokenizer\NamespaceUri;
use TheSeer\Tokenizer\Tokenizer;
use TheSeer\Tokenizer\XMLSerializer;




final class Source
{

private $context;

public function __construct(DOMElement $context)
{
$this->context = $context;
}

public function setSourceCode(string $source): void
{
$context = $this->context;

$tokens = (new Tokenizer)->parse($source);
$srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens);

$context->parentNode->replaceChild(
$context->ownerDocument->importNode($srcDom->documentElement, true),
$context
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
use function count;
use function dirname;
use function file_get_contents;
use function file_put_contents;
use function is_array;
use function is_dir;
use function is_file;
use function is_writable;
use function libxml_clear_errors;
use function libxml_get_errors;
use function libxml_use_internal_errors;
use function sprintf;
use function strlen;
use function substr;
use DateTimeImmutable;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\XmlException;
use SebastianBergmann\Environment\Runtime;

final class Facade
{



private $target;




private $project;




private $phpUnitVersion;

public function __construct(string $version)
{
$this->phpUnitVersion = $version;
}




public function process(CodeCoverage $coverage, string $target): void
{
if (substr($target, -1, 1) !== DIRECTORY_SEPARATOR) {
$target .= DIRECTORY_SEPARATOR;
}

$this->target = $target;
$this->initTargetDirectory($target);

$report = $coverage->getReport();

$this->project = new Project(
$coverage->getReport()->name()
);

$this->setBuildInformation();
$this->processTests($coverage->getTests());
$this->processDirectory($report, $this->project);

$this->saveDocument($this->project->asDom(), 'index');
}

private function setBuildInformation(): void
{
$buildNode = $this->project->buildInformation();
$buildNode->setRuntimeInformation(new Runtime);
$buildNode->setBuildTime(new DateTimeImmutable);
$buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id());
}





private function initTargetDirectory(string $directory): void
{
if (is_file($directory)) {
if (!is_dir($directory)) {
throw new PathExistsButIsNotDirectoryException($directory);
}

if (!is_writable($directory)) {
throw new WriteOperationFailedException($directory);
}
}

DirectoryUtil::createDirectory($directory);
}




private function processDirectory(DirectoryNode $directory, Node $context): void
{
$directoryName = $directory->name();

if ($this->project->projectSourceDirectory() === $directoryName) {
$directoryName = '/';
}

$directoryObject = $context->addDirectory($directoryName);

$this->setTotals($directory, $directoryObject->totals());

foreach ($directory->directories() as $node) {
$this->processDirectory($node, $directoryObject);
}

foreach ($directory->files() as $node) {
$this->processFile($node, $directoryObject);
}
}




private function processFile(FileNode $file, Directory $context): void
{
$fileObject = $context->addFile(
$file->name(),
$file->id() . '.xml'
);

$this->setTotals($file, $fileObject->totals());

$path = substr(
$file->pathAsString(),
strlen($this->project->projectSourceDirectory())
);

$fileReport = new Report($path);

$this->setTotals($file, $fileReport->totals());

foreach ($file->classesAndTraits() as $unit) {
$this->processUnit($unit, $fileReport);
}

foreach ($file->functions() as $function) {
$this->processFunction($function, $fileReport);
}

foreach ($file->lineCoverageData() as $line => $tests) {
if (!is_array($tests) || count($tests) === 0) {
continue;
}

$coverage = $fileReport->lineCoverage((string) $line);

foreach ($tests as $test) {
$coverage->addTest($test);
}

$coverage->finalize();
}

$fileReport->source()->setSourceCode(
file_get_contents($file->pathAsString())
);

$this->saveDocument($fileReport->asDom(), $file->id());
}

private function processUnit(array $unit, Report $report): void
{
if (isset($unit['className'])) {
$unitObject = $report->classObject($unit['className']);
} else {
$unitObject = $report->traitObject($unit['traitName']);
}

$unitObject->setLines(
$unit['startLine'],
$unit['executableLines'],
$unit['executedLines']
);

$unitObject->setCrap((float) $unit['crap']);
$unitObject->setNamespace($unit['namespace']);

foreach ($unit['methods'] as $method) {
$methodObject = $unitObject->addMethod($method['methodName']);
$methodObject->setSignature($method['signature']);
$methodObject->setLines((string) $method['startLine'], (string) $method['endLine']);
$methodObject->setCrap($method['crap']);
$methodObject->setTotals(
(string) $method['executableLines'],
(string) $method['executedLines'],
(string) $method['coverage']
);
}
}

private function processFunction(array $function, Report $report): void
{
$functionObject = $report->functionObject($function['functionName']);

$functionObject->setSignature($function['signature']);
$functionObject->setLines((string) $function['startLine']);
$functionObject->setCrap($function['crap']);
$functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']);
}

private function processTests(array $tests): void
{
$testsObject = $this->project->tests();

foreach ($tests as $test => $result) {
$testsObject->addTest($test, $result);
}
}

private function setTotals(AbstractNode $node, Totals $totals): void
{
$loc = $node->linesOfCode();

$totals->setNumLines(
$loc['linesOfCode'],
$loc['commentLinesOfCode'],
$loc['nonCommentLinesOfCode'],
$node->numberOfExecutableLines(),
$node->numberOfExecutedLines()
);

$totals->setNumClasses(
$node->numberOfClasses(),
$node->numberOfTestedClasses()
);

$totals->setNumTraits(
$node->numberOfTraits(),
$node->numberOfTestedTraits()
);

$totals->setNumMethods(
$node->numberOfMethods(),
$node->numberOfTestedMethods()
);

$totals->setNumFunctions(
$node->numberOfFunctions(),
$node->numberOfTestedFunctions()
);
}

private function targetDirectory(): string
{
return $this->target;
}




private function saveDocument(DOMDocument $document, string $name): void
{
$filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name);

$document->formatOutput = true;
$document->preserveWhiteSpace = false;
$this->initTargetDirectory(dirname($filename));

file_put_contents($filename, $this->documentAsString($document));
}






private function documentAsString(DOMDocument $document): string
{
$xmlErrorHandling = libxml_use_internal_errors(true);
$xml = $document->saveXML();

if ($xml === false) {
$message = 'Unable to generate the XML';

foreach (libxml_get_errors() as $error) {
$message .= PHP_EOL . $error->message;
}

throw new XmlException($message);
}

libxml_clear_errors();
libxml_use_internal_errors($xmlErrorHandling);

return $xml;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use function basename;
use function dirname;
use DOMDocument;




final class Report extends File
{
public function __construct(string $name)
{
$dom = new DOMDocument;
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><file /></phpunit>');

$contextNode = $dom->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'file'
)->item(0);

parent::__construct($contextNode);

$this->setName($name);
}

public function asDom(): DOMDocument
{
return $this->dom();
}

public function functionObject($name): Method
{
$node = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'function'
)
);

return new Method($node, $name);
}

public function classObject($name): Unit
{
return $this->unitObject('class', $name);
}

public function traitObject($name): Unit
{
return $this->unitObject('trait', $name);
}

public function source(): Source
{
$source = $this->contextNode()->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'source'
)->item(0);

if (!$source) {
$source = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'source'
)
);
}

return new Source($source);
}

private function setName(string $name): void
{
$this->contextNode()->setAttribute('name', basename($name));
$this->contextNode()->setAttribute('path', dirname($name));
}

private function unitObject(string $tagName, $name): Unit
{
$node = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
$tagName
)
);

return new Unit($node, $name);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMDocument;




final class Project extends Node
{
public function __construct(string $directory)
{
$this->init();
$this->setProjectSourceDirectory($directory);
}

public function projectSourceDirectory(): string
{
return $this->contextNode()->getAttribute('source');
}

public function buildInformation(): BuildInformation
{
$buildNode = $this->dom()->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'build'
)->item(0);

if (!$buildNode) {
$buildNode = $this->dom()->documentElement->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'build'
)
);
}

return new BuildInformation($buildNode);
}

public function tests(): Tests
{
$testsNode = $this->contextNode()->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'tests'
)->item(0);

if (!$testsNode) {
$testsNode = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'tests'
)
);
}

return new Tests($testsNode);
}

public function asDom(): DOMDocument
{
return $this->dom();
}

private function init(): void
{
$dom = new DOMDocument;
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><build/><project/></phpunit>');

$this->setContextNode(
$dom->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'project'
)->item(0)
);
}

private function setProjectSourceDirectory(string $name): void
{
$this->contextNode()->setAttribute('source', $name);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMElement;




final class Unit
{



private $contextNode;

public function __construct(DOMElement $context, string $name)
{
$this->contextNode = $context;

$this->setName($name);
}

public function setLines(int $start, int $executable, int $executed): void
{
$this->contextNode->setAttribute('start', (string) $start);
$this->contextNode->setAttribute('executable', (string) $executable);
$this->contextNode->setAttribute('executed', (string) $executed);
}

public function setCrap(float $crap): void
{
$this->contextNode->setAttribute('crap', (string) $crap);
}

public function setNamespace(string $namespace): void
{
$node = $this->contextNode->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'namespace'
)->item(0);

if (!$node) {
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'namespace'
)
);
}

$node->setAttribute('name', $namespace);
}

public function addMethod(string $name): Method
{
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'method'
)
);

return new Method($node, $name);
}

private function setName(string $name): void
{
$this->contextNode->setAttribute('name', $name);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use function constant;
use function phpversion;
use DateTimeImmutable;
use DOMElement;
use SebastianBergmann\Environment\Runtime;




final class BuildInformation
{



private $contextNode;

public function __construct(DOMElement $contextNode)
{
$this->contextNode = $contextNode;
}

public function setRuntimeInformation(Runtime $runtime): void
{
$runtimeNode = $this->nodeByName('runtime');

$runtimeNode->setAttribute('name', $runtime->getName());
$runtimeNode->setAttribute('version', $runtime->getVersion());
$runtimeNode->setAttribute('url', $runtime->getVendorUrl());

$driverNode = $this->nodeByName('driver');

if ($runtime->hasPHPDBGCodeCoverage()) {
$driverNode->setAttribute('name', 'phpdbg');
$driverNode->setAttribute('version', constant('PHPDBG_VERSION'));
}

if ($runtime->hasXdebug()) {
$driverNode->setAttribute('name', 'xdebug');
$driverNode->setAttribute('version', phpversion('xdebug'));
}

if ($runtime->hasPCOV()) {
$driverNode->setAttribute('name', 'pcov');
$driverNode->setAttribute('version', phpversion('pcov'));
}
}

public function setBuildTime(DateTimeImmutable $date): void
{
$this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y'));
}

public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void
{
$this->contextNode->setAttribute('phpunit', $phpUnitVersion);
$this->contextNode->setAttribute('coverage', $coverageVersion);
}

private function nodeByName(string $name): DOMElement
{
$node = $this->contextNode->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
$name
)->item(0);

if (!$node) {
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
$name
)
);
}

return $node;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMElement;




final class Method
{



private $contextNode;

public function __construct(DOMElement $context, string $name)
{
$this->contextNode = $context;

$this->setName($name);
}

public function setSignature(string $signature): void
{
$this->contextNode->setAttribute('signature', $signature);
}

public function setLines(string $start, ?string $end = null): void
{
$this->contextNode->setAttribute('start', $start);

if ($end !== null) {
$this->contextNode->setAttribute('end', $end);
}
}

public function setTotals(string $executable, string $executed, string $coverage): void
{
$this->contextNode->setAttribute('executable', $executable);
$this->contextNode->setAttribute('executed', $executed);
$this->contextNode->setAttribute('coverage', $coverage);
}

public function setCrap(string $crap): void
{
$this->contextNode->setAttribute('crap', $crap);
}

private function setName(string $name): void
{
$this->contextNode->setAttribute('name', $name);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMDocument;
use DOMElement;




class File
{



private $dom;




private $contextNode;

public function __construct(DOMElement $context)
{
$this->dom = $context->ownerDocument;
$this->contextNode = $context;
}

public function totals(): Totals
{
$totalsContainer = $this->contextNode->firstChild;

if (!$totalsContainer) {
$totalsContainer = $this->contextNode->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'totals'
)
);
}

return new Totals($totalsContainer);
}

public function lineCoverage(string $line): Coverage
{
$coverage = $this->contextNode->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'coverage'
)->item(0);

if (!$coverage) {
$coverage = $this->contextNode->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'coverage'
)
);
}

$lineNode = $coverage->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'line'
)
);

return new Coverage($lineNode, $line);
}

protected function contextNode(): DOMElement
{
return $this->contextNode;
}

protected function dom(): DOMDocument
{
return $this->dom;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMElement;
use SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException;
use XMLWriter;




final class Coverage
{



private $writer;




private $contextNode;




private $finalized = false;

public function __construct(DOMElement $context, string $line)
{
$this->contextNode = $context;

$this->writer = new XMLWriter;
$this->writer->openMemory();
$this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0');
$this->writer->writeAttribute('nr', $line);
}




public function addTest(string $test): void
{
if ($this->finalized) {
throw new ReportAlreadyFinalizedException;
}

$this->writer->startElement('covered');
$this->writer->writeAttribute('by', $test);
$this->writer->endElement();
}

public function finalize(): void
{
$this->writer->endElement();

$fragment = $this->contextNode->ownerDocument->createDocumentFragment();
$fragment->appendXML($this->writer->outputMemory());

$this->contextNode->parentNode->replaceChild(
$fragment,
$this->contextNode
);

$this->finalized = true;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report\Xml;

use DOMDocument;
use DOMElement;




abstract class Node
{



private $dom;




private $contextNode;

public function __construct(DOMElement $context)
{
$this->setContextNode($context);
}

public function dom(): DOMDocument
{
return $this->dom;
}

public function totals(): Totals
{
$totalsContainer = $this->contextNode()->firstChild;

if (!$totalsContainer) {
$totalsContainer = $this->contextNode()->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'totals'
)
);
}

return new Totals($totalsContainer);
}

public function addDirectory(string $name): Directory
{
$dirNode = $this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'directory'
);

$dirNode->setAttribute('name', $name);
$this->contextNode()->appendChild($dirNode);

return new Directory($dirNode);
}

public function addFile(string $name, string $href): File
{
$fileNode = $this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'file'
);

$fileNode->setAttribute('name', $name);
$fileNode->setAttribute('href', $href);
$this->contextNode()->appendChild($fileNode);

return new File($fileNode);
}

protected function setContextNode(DOMElement $context): void
{
$this->dom = $context->ownerDocument;
$this->contextNode = $context;
}

protected function contextNode(): DOMElement
{
return $this->contextNode;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report;

use function basename;
use function count;
use function dirname;
use function file_put_contents;
use function preg_match;
use function range;
use function str_replace;
use function strpos;
use function time;
use DOMImplementation;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;

final class Cobertura
{



public function process(CodeCoverage $coverage, ?string $target = null): string
{
$time = (string) time();

$report = $coverage->getReport();

$implementation = new DOMImplementation;

$documentType = $implementation->createDocumentType(
'coverage',
'',
'http://cobertura.sourceforge.net/xml/coverage-04.dtd'
);

$document = $implementation->createDocument('', '', $documentType);
$document->xmlVersion = '1.0';
$document->encoding = 'UTF-8';
$document->formatOutput = true;

$coverageElement = $document->createElement('coverage');

$linesValid = $report->numberOfExecutableLines();
$linesCovered = $report->numberOfExecutedLines();
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
$coverageElement->setAttribute('line-rate', (string) $lineRate);

$branchesValid = $report->numberOfExecutableBranches();
$branchesCovered = $report->numberOfExecutedBranches();
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);
$coverageElement->setAttribute('branch-rate', (string) $branchRate);

$coverageElement->setAttribute('lines-covered', (string) $report->numberOfExecutedLines());
$coverageElement->setAttribute('lines-valid', (string) $report->numberOfExecutableLines());
$coverageElement->setAttribute('branches-covered', (string) $report->numberOfExecutedBranches());
$coverageElement->setAttribute('branches-valid', (string) $report->numberOfExecutableBranches());
$coverageElement->setAttribute('complexity', '');
$coverageElement->setAttribute('version', '0.4');
$coverageElement->setAttribute('timestamp', $time);

$document->appendChild($coverageElement);

$sourcesElement = $document->createElement('sources');
$coverageElement->appendChild($sourcesElement);

$sourceElement = $document->createElement('source', $report->pathAsString());
$sourcesElement->appendChild($sourceElement);

$packagesElement = $document->createElement('packages');
$coverageElement->appendChild($packagesElement);

$complexity = 0;

foreach ($report as $item) {
if (!$item instanceof File) {
continue;
}

$packageElement = $document->createElement('package');
$packageComplexity = 0;

$packageElement->setAttribute('name', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));

$linesValid = $item->numberOfExecutableLines();
$linesCovered = $item->numberOfExecutedLines();
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);

$packageElement->setAttribute('line-rate', (string) $lineRate);

$branchesValid = $item->numberOfExecutableBranches();
$branchesCovered = $item->numberOfExecutedBranches();
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);

$packageElement->setAttribute('branch-rate', (string) $branchRate);

$packageElement->setAttribute('complexity', '');
$packagesElement->appendChild($packageElement);

$classesElement = $document->createElement('classes');

$packageElement->appendChild($classesElement);

$classes = $item->classesAndTraits();
$coverageData = $item->lineCoverageData();

foreach ($classes as $className => $class) {
$complexity += $class['ccn'];
$packageComplexity += $class['ccn'];

if (!empty($class['package']['namespace'])) {
$className = $class['package']['namespace'] . '\\' . $className;
}

$linesValid = $class['executableLines'];
$linesCovered = $class['executedLines'];
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);

$branchesValid = $class['executableBranches'];
$branchesCovered = $class['executedBranches'];
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);

$classElement = $document->createElement('class');

$classElement->setAttribute('name', $className);
$classElement->setAttribute('filename', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));
$classElement->setAttribute('line-rate', (string) $lineRate);
$classElement->setAttribute('branch-rate', (string) $branchRate);
$classElement->setAttribute('complexity', (string) $class['ccn']);

$classesElement->appendChild($classElement);

$methodsElement = $document->createElement('methods');

$classElement->appendChild($methodsElement);

$classLinesElement = $document->createElement('lines');

$classElement->appendChild($classLinesElement);

foreach ($class['methods'] as $methodName => $method) {
if ($method['executableLines'] === 0) {
continue;
}

preg_match("/\((.*?)\)/", $method['signature'], $signature);

$linesValid = $method['executableLines'];
$linesCovered = $method['executedLines'];
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);

$branchesValid = $method['executableBranches'];
$branchesCovered = $method['executedBranches'];
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);

$methodElement = $document->createElement('method');

$methodElement->setAttribute('name', $methodName);
$methodElement->setAttribute('signature', $signature[1]);
$methodElement->setAttribute('line-rate', (string) $lineRate);
$methodElement->setAttribute('branch-rate', (string) $branchRate);
$methodElement->setAttribute('complexity', (string) $method['ccn']);

$methodLinesElement = $document->createElement('lines');

$methodElement->appendChild($methodLinesElement);

foreach (range($method['startLine'], $method['endLine']) as $line) {
if (!isset($coverageData[$line]) || $coverageData[$line] === null) {
continue;
}
$methodLineElement = $document->createElement('line');

$methodLineElement->setAttribute('number', (string) $line);
$methodLineElement->setAttribute('hits', (string) count($coverageData[$line]));

$methodLinesElement->appendChild($methodLineElement);

$classLineElement = $methodLineElement->cloneNode();

$classLinesElement->appendChild($classLineElement);
}

$methodsElement->appendChild($methodElement);
}
}

if ($item->numberOfFunctions() === 0) {
$packageElement->setAttribute('complexity', (string) $packageComplexity);

continue;
}

$functionsComplexity = 0;
$functionsLinesValid = 0;
$functionsLinesCovered = 0;
$functionsBranchesValid = 0;
$functionsBranchesCovered = 0;

$classElement = $document->createElement('class');
$classElement->setAttribute('name', basename($item->pathAsString()));
$classElement->setAttribute('filename', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));

$methodsElement = $document->createElement('methods');

$classElement->appendChild($methodsElement);

$classLinesElement = $document->createElement('lines');

$classElement->appendChild($classLinesElement);

$functions = $item->functions();

foreach ($functions as $functionName => $function) {
if ($function['executableLines'] === 0) {
continue;
}

$complexity += $function['ccn'];
$packageComplexity += $function['ccn'];
$functionsComplexity += $function['ccn'];

$linesValid = $function['executableLines'];
$linesCovered = $function['executedLines'];
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);

$functionsLinesValid += $linesValid;
$functionsLinesCovered += $linesCovered;

$branchesValid = $function['executableBranches'];
$branchesCovered = $function['executedBranches'];
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);

$functionsBranchesValid += $branchesValid;
$functionsBranchesCovered += $branchesValid;

$methodElement = $document->createElement('method');

$methodElement->setAttribute('name', $functionName);
$methodElement->setAttribute('signature', $function['signature']);
$methodElement->setAttribute('line-rate', (string) $lineRate);
$methodElement->setAttribute('branch-rate', (string) $branchRate);
$methodElement->setAttribute('complexity', (string) $function['ccn']);

$methodLinesElement = $document->createElement('lines');

$methodElement->appendChild($methodLinesElement);

foreach (range($function['startLine'], $function['endLine']) as $line) {
if (!isset($coverageData[$line]) || $coverageData[$line] === null) {
continue;
}
$methodLineElement = $document->createElement('line');

$methodLineElement->setAttribute('number', (string) $line);
$methodLineElement->setAttribute('hits', (string) count($coverageData[$line]));

$methodLinesElement->appendChild($methodLineElement);

$classLineElement = $methodLineElement->cloneNode();

$classLinesElement->appendChild($classLineElement);
}

$methodsElement->appendChild($methodElement);
}

$packageElement->setAttribute('complexity', (string) $packageComplexity);

if ($functionsLinesValid === 0) {
continue;
}

$lineRate = $functionsLinesCovered / $functionsLinesValid;
$branchRate = $functionsBranchesValid === 0 ? 0 : ($functionsBranchesCovered / $functionsBranchesValid);

$classElement->setAttribute('line-rate', (string) $lineRate);
$classElement->setAttribute('branch-rate', (string) $branchRate);
$classElement->setAttribute('complexity', (string) $functionsComplexity);

$classesElement->appendChild($classElement);
}

$coverageElement->setAttribute('complexity', (string) $complexity);

$buffer = $document->saveXML();

if ($target !== null) {
if (!strpos($target, '://') !== false) {
Filesystem::createDirectory(dirname($target));
}

if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);
}
}

return $buffer;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report;

use const PHP_EOL;
use function array_map;
use function date;
use function ksort;
use function max;
use function sprintf;
use function str_pad;
use function strlen;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Percentage;

final class Text
{



private const COLOR_GREEN = "\x1b[30;42m";




private const COLOR_YELLOW = "\x1b[30;43m";




private const COLOR_RED = "\x1b[37;41m";




private const COLOR_HEADER = "\x1b[1;37;40m";




private const COLOR_RESET = "\x1b[0m";




private const COLOR_EOL = "\x1b[2K";




private $lowUpperBound;




private $highLowerBound;




private $showUncoveredFiles;




private $showOnlySummary;

public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false)
{
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
$this->showUncoveredFiles = $showUncoveredFiles;
$this->showOnlySummary = $showOnlySummary;
}

public function process(CodeCoverage $coverage, bool $showColors = false): string
{
$hasBranchCoverage = !empty($coverage->getData(true)->functionCoverage());

$output = PHP_EOL . PHP_EOL;
$report = $coverage->getReport();

$colors = [
'header' => '',
'classes' => '',
'methods' => '',
'lines' => '',
'branches' => '',
'paths' => '',
'reset' => '',
'eol' => '',
];

if ($showColors) {
$colors['classes'] = $this->coverageColor(
$report->numberOfTestedClassesAndTraits(),
$report->numberOfClassesAndTraits()
);

$colors['methods'] = $this->coverageColor(
$report->numberOfTestedMethods(),
$report->numberOfMethods()
);

$colors['lines'] = $this->coverageColor(
$report->numberOfExecutedLines(),
$report->numberOfExecutableLines()
);

$colors['branches'] = $this->coverageColor(
$report->numberOfExecutedBranches(),
$report->numberOfExecutableBranches()
);

$colors['paths'] = $this->coverageColor(
$report->numberOfExecutedPaths(),
$report->numberOfExecutablePaths()
);

$colors['reset'] = self::COLOR_RESET;
$colors['header'] = self::COLOR_HEADER;
$colors['eol'] = self::COLOR_EOL;
}

$classes = sprintf(
'  Classes: %6s (%d/%d)',
Percentage::fromFractionAndTotal(
$report->numberOfTestedClassesAndTraits(),
$report->numberOfClassesAndTraits()
)->asString(),
$report->numberOfTestedClassesAndTraits(),
$report->numberOfClassesAndTraits()
);

$methods = sprintf(
'  Methods: %6s (%d/%d)',
Percentage::fromFractionAndTotal(
$report->numberOfTestedMethods(),
$report->numberOfMethods(),
)->asString(),
$report->numberOfTestedMethods(),
$report->numberOfMethods()
);

$paths = '';
$branches = '';

if ($hasBranchCoverage) {
$paths = sprintf(
'  Paths:   %6s (%d/%d)',
Percentage::fromFractionAndTotal(
$report->numberOfExecutedPaths(),
$report->numberOfExecutablePaths(),
)->asString(),
$report->numberOfExecutedPaths(),
$report->numberOfExecutablePaths()
);

$branches = sprintf(
'  Branches:   %6s (%d/%d)',
Percentage::fromFractionAndTotal(
$report->numberOfExecutedBranches(),
$report->numberOfExecutableBranches(),
)->asString(),
$report->numberOfExecutedBranches(),
$report->numberOfExecutableBranches()
);
}

$lines = sprintf(
'  Lines:   %6s (%d/%d)',
Percentage::fromFractionAndTotal(
$report->numberOfExecutedLines(),
$report->numberOfExecutableLines(),
)->asString(),
$report->numberOfExecutedLines(),
$report->numberOfExecutableLines()
);

$padding = max(array_map('strlen', [$classes, $methods, $lines]));

if ($this->showOnlySummary) {
$title = 'Code Coverage Report Summary:';
$padding = max($padding, strlen($title));

$output .= $this->format($colors['header'], $padding, $title);
} else {
$date = date('  Y-m-d H:i:s');
$title = 'Code Coverage Report:';

$output .= $this->format($colors['header'], $padding, $title);
$output .= $this->format($colors['header'], $padding, $date);
$output .= $this->format($colors['header'], $padding, '');
$output .= $this->format($colors['header'], $padding, ' Summary:');
}

$output .= $this->format($colors['classes'], $padding, $classes);
$output .= $this->format($colors['methods'], $padding, $methods);

if ($hasBranchCoverage) {
$output .= $this->format($colors['paths'], $padding, $paths);
$output .= $this->format($colors['branches'], $padding, $branches);
}
$output .= $this->format($colors['lines'], $padding, $lines);

if ($this->showOnlySummary) {
return $output . PHP_EOL;
}

$classCoverage = [];

foreach ($report as $item) {
if (!$item instanceof File) {
continue;
}

$classes = $item->classesAndTraits();

foreach ($classes as $className => $class) {
$classExecutableLines = 0;
$classExecutedLines = 0;
$classExecutableBranches = 0;
$classExecutedBranches = 0;
$classExecutablePaths = 0;
$classExecutedPaths = 0;
$coveredMethods = 0;
$classMethods = 0;

foreach ($class['methods'] as $method) {
if ($method['executableLines'] == 0) {
continue;
}

$classMethods++;
$classExecutableLines += $method['executableLines'];
$classExecutedLines += $method['executedLines'];
$classExecutableBranches += $method['executableBranches'];
$classExecutedBranches += $method['executedBranches'];
$classExecutablePaths += $method['executablePaths'];
$classExecutedPaths += $method['executedPaths'];

if ($method['coverage'] == 100) {
$coveredMethods++;
}
}

$classCoverage[$className] = [
'namespace' => $class['namespace'],
'className' => $className,
'methodsCovered' => $coveredMethods,
'methodCount' => $classMethods,
'statementsCovered' => $classExecutedLines,
'statementCount' => $classExecutableLines,
'branchesCovered' => $classExecutedBranches,
'branchesCount' => $classExecutableBranches,
'pathsCovered' => $classExecutedPaths,
'pathsCount' => $classExecutablePaths,
];
}
}

ksort($classCoverage);

$methodColor = '';
$pathsColor = '';
$branchesColor = '';
$linesColor = '';
$resetColor = '';

foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) {
if ($showColors) {
$methodColor = $this->coverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
$pathsColor = $this->coverageColor($classInfo['pathsCovered'], $classInfo['pathsCount']);
$branchesColor = $this->coverageColor($classInfo['branchesCovered'], $classInfo['branchesCount']);
$linesColor = $this->coverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
$resetColor = $colors['reset'];
}

$output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
. '  ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ';

if ($hasBranchCoverage) {
$output .= '  ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathsCount'], 3) . $resetColor . ' '
. '  ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchesCount'], 3) . $resetColor . ' ';
}
$output .= '  ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor;
}
}

return $output . PHP_EOL;
}

private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string
{
$coverage = Percentage::fromFractionAndTotal(
$numberOfCoveredElements,
$totalNumberOfElements
);

if ($coverage->asFloat() >= $this->highLowerBound) {
return self::COLOR_GREEN;
}

if ($coverage->asFloat() > $this->lowUpperBound) {
return self::COLOR_YELLOW;
}

return self::COLOR_RED;
}

private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string
{
$format = '%' . $precision . 's';

return Percentage::fromFractionAndTotal(
$numberOfCoveredElements,
$totalNumberOfElements
)->asFixedWidthString() .
' (' . sprintf($format, $numberOfCoveredElements) . '/' .
sprintf($format, $totalNumberOfElements) . ')';
}




private function format(string $color, int $padding, $string): string
{
$reset = $color ? self::COLOR_RESET : '';

return $color . str_pad((string) $string, $padding) . $reset . PHP_EOL;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report;

use function count;
use function dirname;
use function file_put_contents;
use function is_string;
use function ksort;
use function max;
use function range;
use function strpos;
use function time;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;

final class Clover
{



public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
{
$time = (string) time();

$xmlDocument = new DOMDocument('1.0', 'UTF-8');
$xmlDocument->formatOutput = true;

$xmlCoverage = $xmlDocument->createElement('coverage');
$xmlCoverage->setAttribute('generated', $time);
$xmlDocument->appendChild($xmlCoverage);

$xmlProject = $xmlDocument->createElement('project');
$xmlProject->setAttribute('timestamp', $time);

if (is_string($name)) {
$xmlProject->setAttribute('name', $name);
}

$xmlCoverage->appendChild($xmlProject);

$packages = [];
$report = $coverage->getReport();

foreach ($report as $item) {
if (!$item instanceof File) {
continue;
}



$xmlFile = $xmlDocument->createElement('file');
$xmlFile->setAttribute('name', $item->pathAsString());

$classes = $item->classesAndTraits();
$coverageData = $item->lineCoverageData();
$lines = [];
$namespace = 'global';

foreach ($classes as $className => $class) {
$classStatements = 0;
$coveredClassStatements = 0;
$coveredMethods = 0;
$classMethods = 0;

foreach ($class['methods'] as $methodName => $method) {
if ($method['executableLines'] == 0) {
continue;
}

$classMethods++;
$classStatements += $method['executableLines'];
$coveredClassStatements += $method['executedLines'];

if ($method['coverage'] == 100) {
$coveredMethods++;
}

$methodCount = 0;

foreach (range($method['startLine'], $method['endLine']) as $line) {
if (isset($coverageData[$line]) && ($coverageData[$line] !== null)) {
$methodCount = max($methodCount, count($coverageData[$line]));
}
}

$lines[$method['startLine']] = [
'ccn' => $method['ccn'],
'count' => $methodCount,
'crap' => $method['crap'],
'type' => 'method',
'visibility' => $method['visibility'],
'name' => $methodName,
];
}

if (!empty($class['package']['namespace'])) {
$namespace = $class['package']['namespace'];
}

$xmlClass = $xmlDocument->createElement('class');
$xmlClass->setAttribute('name', $className);
$xmlClass->setAttribute('namespace', $namespace);

if (!empty($class['package']['fullPackage'])) {
$xmlClass->setAttribute(
'fullPackage',
$class['package']['fullPackage']
);
}

if (!empty($class['package']['category'])) {
$xmlClass->setAttribute(
'category',
$class['package']['category']
);
}

if (!empty($class['package']['package'])) {
$xmlClass->setAttribute(
'package',
$class['package']['package']
);
}

if (!empty($class['package']['subpackage'])) {
$xmlClass->setAttribute(
'subpackage',
$class['package']['subpackage']
);
}

$xmlFile->appendChild($xmlClass);

$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('complexity', (string) $class['ccn']);
$xmlMetrics->setAttribute('methods', (string) $classMethods);
$xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods);
$xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']);
$xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']);
$xmlMetrics->setAttribute('statements', (string) $classStatements);
$xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements);
$xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches']));
$xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches']));
$xmlClass->appendChild($xmlMetrics);
}

foreach ($coverageData as $line => $data) {
if ($data === null || isset($lines[$line])) {
continue;
}

$lines[$line] = [
'count' => count($data), 'type' => 'stmt',
];
}

ksort($lines);

foreach ($lines as $line => $data) {
$xmlLine = $xmlDocument->createElement('line');
$xmlLine->setAttribute('num', (string) $line);
$xmlLine->setAttribute('type', $data['type']);

if (isset($data['name'])) {
$xmlLine->setAttribute('name', $data['name']);
}

if (isset($data['visibility'])) {
$xmlLine->setAttribute('visibility', $data['visibility']);
}

if (isset($data['ccn'])) {
$xmlLine->setAttribute('complexity', (string) $data['ccn']);
}

if (isset($data['crap'])) {
$xmlLine->setAttribute('crap', (string) $data['crap']);
}

$xmlLine->setAttribute('count', (string) $data['count']);
$xmlFile->appendChild($xmlLine);
}

$linesOfCode = $item->linesOfCode();

$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['linesOfCode']);
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['nonCommentLinesOfCode']);
$xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits());
$xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods());
$xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods());
$xmlMetrics->setAttribute('conditionals', (string) $item->numberOfExecutableBranches());
$xmlMetrics->setAttribute('coveredconditionals', (string) $item->numberOfExecutedBranches());
$xmlMetrics->setAttribute('statements', (string) $item->numberOfExecutableLines());
$xmlMetrics->setAttribute('coveredstatements', (string) $item->numberOfExecutedLines());
$xmlMetrics->setAttribute('elements', (string) ($item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches()));
$xmlMetrics->setAttribute('coveredelements', (string) ($item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches()));
$xmlFile->appendChild($xmlMetrics);

if ($namespace === 'global') {
$xmlProject->appendChild($xmlFile);
} else {
if (!isset($packages[$namespace])) {
$packages[$namespace] = $xmlDocument->createElement(
'package'
);

$packages[$namespace]->setAttribute('name', $namespace);
$xmlProject->appendChild($packages[$namespace]);
}

$packages[$namespace]->appendChild($xmlFile);
}
}

$linesOfCode = $report->linesOfCode();

$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('files', (string) count($report));
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['linesOfCode']);
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['nonCommentLinesOfCode']);
$xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits());
$xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods());
$xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods());
$xmlMetrics->setAttribute('conditionals', (string) $report->numberOfExecutableBranches());
$xmlMetrics->setAttribute('coveredconditionals', (string) $report->numberOfExecutedBranches());
$xmlMetrics->setAttribute('statements', (string) $report->numberOfExecutableLines());
$xmlMetrics->setAttribute('coveredstatements', (string) $report->numberOfExecutedLines());
$xmlMetrics->setAttribute('elements', (string) ($report->numberOfMethods() + $report->numberOfExecutableLines() + $report->numberOfExecutableBranches()));
$xmlMetrics->setAttribute('coveredelements', (string) ($report->numberOfTestedMethods() + $report->numberOfExecutedLines() + $report->numberOfExecutedBranches()));
$xmlProject->appendChild($xmlMetrics);

$buffer = $xmlDocument->saveXML();

if ($target !== null) {
if (!strpos($target, '://') !== false) {
Filesystem::createDirectory(dirname($target));
}

if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);
}
}

return $buffer;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Report;

use function date;
use function dirname;
use function file_put_contents;
use function htmlspecialchars;
use function is_string;
use function round;
use function strpos;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;

final class Crap4j
{



private $threshold;

public function __construct(int $threshold = 30)
{
$this->threshold = $threshold;
}




public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
{
$document = new DOMDocument('1.0', 'UTF-8');
$document->formatOutput = true;

$root = $document->createElement('crap_result');
$document->appendChild($root);

$project = $document->createElement('project', is_string($name) ? $name : '');
$root->appendChild($project);
$root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s')));

$stats = $document->createElement('stats');
$methodsNode = $document->createElement('methods');

$report = $coverage->getReport();
unset($coverage);

$fullMethodCount = 0;
$fullCrapMethodCount = 0;
$fullCrapLoad = 0;
$fullCrap = 0;

foreach ($report as $item) {
$namespace = 'global';

if (!$item instanceof File) {
continue;
}

$file = $document->createElement('file');
$file->setAttribute('name', $item->pathAsString());

$classes = $item->classesAndTraits();

foreach ($classes as $className => $class) {
foreach ($class['methods'] as $methodName => $method) {
$crapLoad = $this->crapLoad((float) $method['crap'], $method['ccn'], $method['coverage']);

$fullCrap += $method['crap'];
$fullCrapLoad += $crapLoad;
$fullMethodCount++;

if ($method['crap'] >= $this->threshold) {
$fullCrapMethodCount++;
}

$methodNode = $document->createElement('method');

if (!empty($class['namespace'])) {
$namespace = $class['namespace'];
}

$methodNode->appendChild($document->createElement('package', $namespace));
$methodNode->appendChild($document->createElement('className', $className));
$methodNode->appendChild($document->createElement('methodName', $methodName));
$methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature'])));
$methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature'])));
$methodNode->appendChild($document->createElement('crap', (string) $this->roundValue((float) $method['crap'])));
$methodNode->appendChild($document->createElement('complexity', (string) $method['ccn']));
$methodNode->appendChild($document->createElement('coverage', (string) $this->roundValue($method['coverage'])));
$methodNode->appendChild($document->createElement('crapLoad', (string) round($crapLoad)));

$methodsNode->appendChild($methodNode);
}
}
}

$stats->appendChild($document->createElement('name', 'Method Crap Stats'));
$stats->appendChild($document->createElement('methodCount', (string) $fullMethodCount));
$stats->appendChild($document->createElement('crapMethodCount', (string) $fullCrapMethodCount));
$stats->appendChild($document->createElement('crapLoad', (string) round($fullCrapLoad)));
$stats->appendChild($document->createElement('totalCrap', (string) $fullCrap));

$crapMethodPercent = 0;

if ($fullMethodCount > 0) {
$crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount);
}

$stats->appendChild($document->createElement('crapMethodPercent', (string) $crapMethodPercent));

$root->appendChild($stats);
$root->appendChild($methodsNode);

$buffer = $document->saveXML();

if ($target !== null) {
if (!strpos($target, '://') !== false) {
Filesystem::createDirectory(dirname($target));
}

if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);
}
}

return $buffer;
}

private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent): float
{
$crapLoad = 0;

if ($crapValue >= $this->threshold) {
$crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100);
$crapLoad += $cyclomaticComplexity / $this->threshold;
}

return $crapLoad;
}

private function roundValue(float $value): float
{
return round($value, 2);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use function array_keys;
use function is_file;
use function realpath;
use function strpos;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;

final class Filter
{



private $files = [];




private $isFileCache = [];

public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void
{
foreach ((new FileIteratorFacade)->getFilesAsArray($directory, $suffix, $prefix) as $file) {
$this->includeFile($file);
}
}




public function includeFiles(array $filenames): void
{
foreach ($filenames as $filename) {
$this->includeFile($filename);
}
}

public function includeFile(string $filename): void
{
$filename = realpath($filename);

if (!$filename) {
return;
}

$this->files[$filename] = true;
}

public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void
{
foreach ((new FileIteratorFacade)->getFilesAsArray($directory, $suffix, $prefix) as $file) {
$this->excludeFile($file);
}
}

public function excludeFile(string $filename): void
{
$filename = realpath($filename);

if (!$filename || !isset($this->files[$filename])) {
return;
}

unset($this->files[$filename]);
}

public function isFile(string $filename): bool
{
if (isset($this->isFileCache[$filename])) {
return $this->isFileCache[$filename];
}

if ($filename === '-' ||
strpos($filename, 'vfs://') === 0 ||
strpos($filename, 'xdebug://debug-eval') !== false ||
strpos($filename, 'eval()\'d code') !== false ||
strpos($filename, 'runtime-created function') !== false ||
strpos($filename, 'runkit created function') !== false ||
strpos($filename, 'assert code') !== false ||
strpos($filename, 'regexp code') !== false ||
strpos($filename, 'Standard input code') !== false) {
$isFile = false;
} else {
$isFile = is_file($filename);
}

$this->isFileCache[$filename] = $isFile;

return $isFile;
}

public function isExcluded(string $filename): bool
{
return !isset($this->files[$filename]) || !$this->isFile($filename);
}




public function files(): array
{
return array_keys($this->files);
}

public function isEmpty(): bool
{
return empty($this->files);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;

use function file_get_contents;
use function file_put_contents;
use function implode;
use function is_file;
use function md5;
use function serialize;
use function unserialize;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;




final class CachingFileAnalyser implements FileAnalyser
{



private static $cacheVersion;




private $directory;




private $analyser;




private $useAnnotationsForIgnoringCode;




private $ignoreDeprecatedCode;




private $cache = [];

public function __construct(string $directory, FileAnalyser $analyser, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode)
{
Filesystem::createDirectory($directory);

$this->analyser = $analyser;
$this->directory = $directory;
$this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode;
$this->ignoreDeprecatedCode = $ignoreDeprecatedCode;
}

public function classesIn(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}

return $this->cache[$filename]['classesIn'];
}

public function traitsIn(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}

return $this->cache[$filename]['traitsIn'];
}

public function functionsIn(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}

return $this->cache[$filename]['functionsIn'];
}




public function linesOfCodeFor(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}

return $this->cache[$filename]['linesOfCodeFor'];
}

public function executableLinesIn(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}

return $this->cache[$filename]['executableLinesIn'];
}

public function ignoredLinesFor(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}

return $this->cache[$filename]['ignoredLinesFor'];
}

public function process(string $filename): void
{
$cache = $this->read($filename);

if ($cache !== false) {
$this->cache[$filename] = $cache;

return;
}

$this->cache[$filename] = [
'classesIn' => $this->analyser->classesIn($filename),
'traitsIn' => $this->analyser->traitsIn($filename),
'functionsIn' => $this->analyser->functionsIn($filename),
'linesOfCodeFor' => $this->analyser->linesOfCodeFor($filename),
'ignoredLinesFor' => $this->analyser->ignoredLinesFor($filename),
'executableLinesIn' => $this->analyser->executableLinesIn($filename),
];

$this->write($filename, $this->cache[$filename]);
}




private function read(string $filename)
{
$cacheFile = $this->cacheFile($filename);

if (!is_file($cacheFile)) {
return false;
}

return unserialize(
file_get_contents($cacheFile),
['allowed_classes' => false]
);
}




private function write(string $filename, $data): void
{
file_put_contents(
$this->cacheFile($filename),
serialize($data)
);
}

private function cacheFile(string $filename): string
{
$cacheKey = md5(
implode(
"\0",
[
$filename,
file_get_contents($filename),
self::cacheVersion(),
$this->useAnnotationsForIgnoringCode,
$this->ignoreDeprecatedCode,
]
)
);

return $this->directory . DIRECTORY_SEPARATOR . $cacheKey;
}

private static function cacheVersion(): string
{
if (self::$cacheVersion !== null) {
return self::$cacheVersion;
}

$buffer = [];

foreach ((new FileIteratorFacade)->getFilesAsArray(__DIR__, '.php') as $file) {
$buffer[] = $file;
$buffer[] = file_get_contents($file);
}

self::$cacheVersion = md5(implode("\0", $buffer));

return self::$cacheVersion;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;

use function array_diff_key;
use function assert;
use function count;
use function current;
use function end;
use function explode;
use function max;
use function preg_match;
use function preg_quote;
use function range;
use function reset;
use function sprintf;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;




final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
{



private $nextBranch = 0;




private $source;




private $executableLinesGroupedByBranch = [];




private $unsets = [];




private $commentsToCheckForUnset = [];

public function __construct(string $source)
{
$this->source = $source;
}

public function enterNode(Node $node): void
{
foreach ($node->getComments() as $comment) {
$commentLine = $comment->getStartLine();

if (!isset($this->executableLinesGroupedByBranch[$commentLine])) {
continue;
}

foreach (explode("\n", $comment->getText()) as $text) {
$this->commentsToCheckForUnset[$commentLine] = $text;
$commentLine++;
}
}

if ($node instanceof Node\Scalar\String_ ||
$node instanceof Node\Scalar\EncapsedStringPart) {
$startLine = $node->getStartLine() + 1;
$endLine = $node->getEndLine() - 1;

if ($startLine <= $endLine) {
foreach (range($startLine, $endLine) as $line) {
unset($this->executableLinesGroupedByBranch[$line]);
}
}

return;
}

if ($node instanceof Node\Stmt\Interface_) {
foreach (range($node->getStartLine(), $node->getEndLine()) as $line) {
$this->unsets[$line] = true;
}

return;
}

if ($node instanceof Node\Stmt\Declare_ ||
$node instanceof Node\Stmt\DeclareDeclare ||
$node instanceof Node\Stmt\Else_ ||
$node instanceof Node\Stmt\EnumCase ||
$node instanceof Node\Stmt\Finally_ ||
$node instanceof Node\Stmt\GroupUse ||
$node instanceof Node\Stmt\Label ||
$node instanceof Node\Stmt\Namespace_ ||
$node instanceof Node\Stmt\Nop ||
$node instanceof Node\Stmt\Switch_ ||
$node instanceof Node\Stmt\TryCatch ||
$node instanceof Node\Stmt\Use_ ||
$node instanceof Node\Stmt\UseUse ||
$node instanceof Node\Expr\ConstFetch ||
$node instanceof Node\Expr\Match_ ||
$node instanceof Node\Expr\Variable ||
$node instanceof Node\Expr\Throw_ ||
$node instanceof Node\ComplexType ||
$node instanceof Node\Const_ ||
$node instanceof Node\Identifier ||
$node instanceof Node\Name ||
$node instanceof Node\Param ||
$node instanceof Node\Scalar) {
return;
}





if ($node instanceof Node\Stmt\Throw_) {
$this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);

return;
}






if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\Throw_) {
$this->setLineBranch($node->expr->expr->getEndLine(), $node->expr->expr->getEndLine(), ++$this->nextBranch);

return;
}

if ($node instanceof Node\Stmt\Enum_ ||
$node instanceof Node\Stmt\Function_ ||
$node instanceof Node\Stmt\Class_ ||
$node instanceof Node\Stmt\ClassMethod ||
$node instanceof Node\Expr\Closure ||
$node instanceof Node\Stmt\Trait_) {
$isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_;

if (null !== $node->stmts) {
foreach ($node->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Nop) {
continue;
}

foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) {
unset($this->executableLinesGroupedByBranch[$line]);

if (
$isConcreteClassLike &&
!$stmt instanceof Node\Stmt\ClassMethod
) {
$this->unsets[$line] = true;
}
}
}
}

if ($isConcreteClassLike) {
return;
}

$hasEmptyBody = [] === $node->stmts ||
null === $node->stmts ||
(
1 === count($node->stmts) &&
$node->stmts[0] instanceof Node\Stmt\Nop
);

if ($hasEmptyBody) {
if ($node->getEndLine() === $node->getStartLine()) {
return;
}

$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);

return;
}

return;
}

if ($node instanceof Node\Expr\ArrowFunction) {
$startLine = max(
$node->getStartLine() + 1,
$node->expr->getStartLine()
);

$endLine = $node->expr->getEndLine();

if ($endLine < $startLine) {
return;
}

$this->setLineBranch($startLine, $endLine, ++$this->nextBranch);

return;
}

if ($node instanceof Node\Expr\Ternary) {
if (null !== $node->if &&
$node->getStartLine() !== $node->if->getEndLine()) {
$this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch);
}

if ($node->getStartLine() !== $node->else->getEndLine()) {
$this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch);
}

return;
}

if ($node instanceof Node\Expr\BinaryOp\Coalesce) {
if ($node->getStartLine() !== $node->getEndLine()) {
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
}

return;
}

if ($node instanceof Node\Stmt\If_ ||
$node instanceof Node\Stmt\ElseIf_ ||
$node instanceof Node\Stmt\Case_) {
if (null === $node->cond) {
return;
}

$this->setLineBranch(
$node->cond->getStartLine(),
$node->cond->getStartLine(),
++$this->nextBranch
);

return;
}

if ($node instanceof Node\Stmt\For_) {
$startLine = null;
$endLine = null;

if ([] !== $node->init) {
$startLine = $node->init[0]->getStartLine();

end($node->init);

$endLine = current($node->init)->getEndLine();

reset($node->init);
}

if ([] !== $node->cond) {
if (null === $startLine) {
$startLine = $node->cond[0]->getStartLine();
}

end($node->cond);

$endLine = current($node->cond)->getEndLine();

reset($node->cond);
}

if ([] !== $node->loop) {
if (null === $startLine) {
$startLine = $node->loop[0]->getStartLine();
}

end($node->loop);

$endLine = current($node->loop)->getEndLine();

reset($node->loop);
}

if (null === $startLine || null === $endLine) {
return;
}

$this->setLineBranch(
$startLine,
$endLine,
++$this->nextBranch
);

return;
}

if ($node instanceof Node\Stmt\Foreach_) {
$this->setLineBranch(
$node->expr->getStartLine(),
$node->valueVar->getEndLine(),
++$this->nextBranch
);

return;
}

if ($node instanceof Node\Stmt\While_ ||
$node instanceof Node\Stmt\Do_) {
$this->setLineBranch(
$node->cond->getStartLine(),
$node->cond->getEndLine(),
++$this->nextBranch
);

return;
}

if ($node instanceof Node\Stmt\Catch_) {
assert([] !== $node->types);
$startLine = $node->types[0]->getStartLine();
end($node->types);
$endLine = current($node->types)->getEndLine();

$this->setLineBranch(
$startLine,
$endLine,
++$this->nextBranch
);

return;
}

if ($node instanceof Node\Expr\CallLike) {
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
$branch = $this->executableLinesGroupedByBranch[$node->getStartLine()];
} else {
$branch = ++$this->nextBranch;
}

$this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);

return;
}

if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
return;
}

$this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch);
}

public function afterTraverse(array $nodes): void
{
$lines = explode("\n", $this->source);

foreach ($lines as $lineNumber => $line) {
$lineNumber++;

if (1 === preg_match('/^\s*$/', $line) ||
(
isset($this->commentsToCheckForUnset[$lineNumber]) &&
1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)
)) {
unset($this->executableLinesGroupedByBranch[$lineNumber]);
}
}

$this->executableLinesGroupedByBranch = array_diff_key(
$this->executableLinesGroupedByBranch,
$this->unsets
);
}

public function executableLinesGroupedByBranch(): array
{
return $this->executableLinesGroupedByBranch;
}

private function setLineBranch(int $start, int $end, int $branch): void
{
foreach (range($start, $end) as $line) {
$this->executableLinesGroupedByBranch[$line] = $branch;
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;

use function assert;
use function implode;
use function rtrim;
use function trim;
use PhpParser\Node;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Identifier;
use PhpParser\Node\IntersectionType;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\UnionType;
use PhpParser\NodeAbstract;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor;




final class CodeUnitFindingVisitor extends NodeVisitorAbstract
{



private $classes = [];




private $traits = [];




private $functions = [];

public function enterNode(Node $node): void
{
if ($node instanceof Class_) {
if ($node->isAnonymous()) {
return;
}

$this->processClass($node);
}

if ($node instanceof Trait_) {
$this->processTrait($node);
}

if (!$node instanceof ClassMethod && !$node instanceof Function_) {
return;
}

if ($node instanceof ClassMethod) {
$parentNode = $node->getAttribute('parent');

if ($parentNode instanceof Class_ && $parentNode->isAnonymous()) {
return;
}

$this->processMethod($node);

return;
}

$this->processFunction($node);
}




public function classes(): array
{
return $this->classes;
}




public function traits(): array
{
return $this->traits;
}




public function functions(): array
{
return $this->functions;
}




private function cyclomaticComplexity(Node $node): int
{
assert($node instanceof ClassMethod || $node instanceof Function_);

$nodes = $node->getStmts();

if ($nodes === null) {
return 0;
}

$traverser = new NodeTraverser;

$cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor;

$traverser->addVisitor($cyclomaticComplexityCalculatingVisitor);


$traverser->traverse($nodes);

return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity();
}




private function signature(Node $node): string
{
assert($node instanceof ClassMethod || $node instanceof Function_);

$signature = ($node->returnsByRef() ? '&' : '') . $node->name->toString() . '(';
$parameters = [];

foreach ($node->getParams() as $parameter) {
assert(isset($parameter->var->name));

$parameterAsString = '';

if ($parameter->type !== null) {
$parameterAsString = $this->type($parameter->type) . ' ';
}

$parameterAsString .= '$' . $parameter->var->name;



$parameters[] = $parameterAsString;
}

$signature .= implode(', ', $parameters) . ')';

$returnType = $node->getReturnType();

if ($returnType !== null) {
$signature .= ': ' . $this->type($returnType);
}

return $signature;
}




private function type(Node $type): string
{
assert($type instanceof Identifier || $type instanceof Name || $type instanceof ComplexType);

if ($type instanceof NullableType) {
return '?' . $type->type;
}

if ($type instanceof UnionType) {
return $this->unionTypeAsString($type);
}

if ($type instanceof IntersectionType) {
return $this->intersectionTypeAsString($type);
}

return $type->toString();
}

private function visibility(ClassMethod $node): string
{
if ($node->isPrivate()) {
return 'private';
}

if ($node->isProtected()) {
return 'protected';
}

return 'public';
}

private function processClass(Class_ $node): void
{
$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();

$this->classes[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'methods' => [],
];
}

private function processTrait(Trait_ $node): void
{
$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();

$this->traits[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'methods' => [],
];
}

private function processMethod(ClassMethod $node): void
{
$parentNode = $node->getAttribute('parent');

if ($parentNode instanceof Interface_) {
return;
}

assert($parentNode instanceof Class_ || $parentNode instanceof Trait_ || $parentNode instanceof Enum_);
assert(isset($parentNode->name));
assert(isset($parentNode->namespacedName));
assert($parentNode->namespacedName instanceof Name);

$parentName = $parentNode->name->toString();
$parentNamespacedName = $parentNode->namespacedName->toString();

if ($parentNode instanceof Class_) {
$storage = &$this->classes;
} else {
$storage = &$this->traits;
}

if (!isset($storage[$parentNamespacedName])) {
$storage[$parentNamespacedName] = [
'name' => $parentName,
'namespacedName' => $parentNamespacedName,
'namespace' => $this->namespace($parentNamespacedName, $parentName),
'startLine' => $parentNode->getStartLine(),
'endLine' => $parentNode->getEndLine(),
'methods' => [],
];
}

$storage[$parentNamespacedName]['methods'][$node->name->toString()] = [
'methodName' => $node->name->toString(),
'signature' => $this->signature($node),
'visibility' => $this->visibility($node),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'ccn' => $this->cyclomaticComplexity($node),
];
}

private function processFunction(Function_ $node): void
{
assert(isset($node->name));
assert(isset($node->namespacedName));
assert($node->namespacedName instanceof Name);

$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();

$this->functions[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'signature' => $this->signature($node),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'ccn' => $this->cyclomaticComplexity($node),
];
}

private function namespace(string $namespacedName, string $name): string
{
return trim(rtrim($namespacedName, $name), '\\');
}

private function unionTypeAsString(UnionType $node): string
{
$types = [];

foreach ($node->types as $type) {
if ($type instanceof IntersectionType) {
$types[] = '(' . $this->intersectionTypeAsString($type) . ')';

continue;
}

$types[] = $this->typeAsString($type);
}

return implode('|', $types);
}

private function intersectionTypeAsString(IntersectionType $node): string
{
$types = [];

foreach ($node->types as $type) {
$types[] = $this->typeAsString($type);
}

return implode('&', $types);
}




private function typeAsString(NodeAbstract $node): string
{
if ($node instanceof Name) {
return $node->toCodeString();
}

return $node->toString();
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;

use function array_merge;
use function assert;
use function range;
use function strpos;
use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeVisitorAbstract;




final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
{



private $ignoredLines = [];




private $useAnnotationsForIgnoringCode;




private $ignoreDeprecated;

public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated)
{
$this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode;
$this->ignoreDeprecated = $ignoreDeprecated;
}

public function enterNode(Node $node): void
{
if (!$node instanceof Class_ &&
!$node instanceof Trait_ &&
!$node instanceof Interface_ &&
!$node instanceof ClassMethod &&
!$node instanceof Function_ &&
!$node instanceof Attribute) {
return;
}

if ($node instanceof Class_ && $node->isAnonymous()) {
return;
}

if ($node instanceof Class_ ||
$node instanceof Trait_ ||
$node instanceof Interface_ ||
$node instanceof Attribute) {
$this->ignoredLines[] = $node->getStartLine();

assert($node->name !== null);


$this->ignoredLines[] = $node->name->getStartLine();
}

if (!$this->useAnnotationsForIgnoringCode) {
return;
}

if ($node instanceof Interface_) {
return;
}

$this->processDocComment($node);
}




public function ignoredLines(): array
{
return $this->ignoredLines;
}

private function processDocComment(Node $node): void
{
$docComment = $node->getDocComment();

if ($docComment === null) {
return;
}

if (strpos($docComment->getText(), '@codeCoverageIgnore') !== false) {
$this->ignoredLines = array_merge(
$this->ignoredLines,
range($node->getStartLine(), $node->getEndLine())
);
}

if ($this->ignoreDeprecated && strpos($docComment->getText(), '@deprecated') !== false) {
$this->ignoredLines = array_merge(
$this->ignoredLines,
range($node->getStartLine(), $node->getEndLine())
);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;

use function array_merge;
use function array_unique;
use function assert;
use function file_get_contents;
use function is_array;
use function max;
use function range;
use function sort;
use function sprintf;
use function substr_count;
use function token_get_all;
use function trim;
use PhpParser\Error;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitor\ParentConnectingVisitor;
use PhpParser\ParserFactory;
use SebastianBergmann\CodeCoverage\ParserException;
use SebastianBergmann\LinesOfCode\LineCountingVisitor;




final class ParsingFileAnalyser implements FileAnalyser
{



private $classes = [];




private $traits = [];




private $functions = [];




private $linesOfCode = [];




private $ignoredLines = [];




private $executableLines = [];




private $useAnnotationsForIgnoringCode;




private $ignoreDeprecatedCode;

public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode)
{
$this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode;
$this->ignoreDeprecatedCode = $ignoreDeprecatedCode;
}

public function classesIn(string $filename): array
{
$this->analyse($filename);

return $this->classes[$filename];
}

public function traitsIn(string $filename): array
{
$this->analyse($filename);

return $this->traits[$filename];
}

public function functionsIn(string $filename): array
{
$this->analyse($filename);

return $this->functions[$filename];
}




public function linesOfCodeFor(string $filename): array
{
$this->analyse($filename);

return $this->linesOfCode[$filename];
}

public function executableLinesIn(string $filename): array
{
$this->analyse($filename);

return $this->executableLines[$filename];
}

public function ignoredLinesFor(string $filename): array
{
$this->analyse($filename);

return $this->ignoredLines[$filename];
}




private function analyse(string $filename): void
{
if (isset($this->classes[$filename])) {
return;
}

$source = file_get_contents($filename);
$linesOfCode = max(substr_count($source, "\n") + 1, substr_count($source, "\r") + 1);

if ($linesOfCode === 0 && !empty($source)) {
$linesOfCode = 1;
}

$parser = (new ParserFactory)->createForHostVersion();

try {
$nodes = $parser->parse($source);

assert($nodes !== null);

$traverser = new NodeTraverser;
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($source);

$traverser->addVisitor(new NameResolver);
$traverser->addVisitor(new ParentConnectingVisitor);
$traverser->addVisitor($codeUnitFindingVisitor);
$traverser->addVisitor($lineCountingVisitor);
$traverser->addVisitor($ignoredLinesFindingVisitor);
$traverser->addVisitor($executableLinesFindingVisitor);


$traverser->traverse($nodes);

} catch (Error $error) {
throw new ParserException(
sprintf(
'Cannot parse %s: %s',
$filename,
$error->getMessage()
),
$error->getCode(),
$error
);
}


$this->classes[$filename] = $codeUnitFindingVisitor->classes();
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
$this->executableLines[$filename] = $executableLinesFindingVisitor->executableLinesGroupedByBranch();
$this->ignoredLines[$filename] = [];

$this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode);

$this->ignoredLines[$filename] = array_unique(
array_merge(
$this->ignoredLines[$filename],
$ignoredLinesFindingVisitor->ignoredLines()
)
);

sort($this->ignoredLines[$filename]);

$result = $lineCountingVisitor->result();

$this->linesOfCode[$filename] = [
'linesOfCode' => $result->linesOfCode(),
'commentLinesOfCode' => $result->commentLinesOfCode(),
'nonCommentLinesOfCode' => $result->nonCommentLinesOfCode(),
];
}

private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void
{
if (!$useAnnotationsForIgnoringCode) {
return;
}

$start = false;

foreach (token_get_all($source) as $token) {
if (!is_array($token) ||
!(T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0])) {
continue;
}

$comment = trim($token[1]);

if ($comment === '// @codeCoverageIgnore' ||
$comment === '//@codeCoverageIgnore') {
$this->ignoredLines[$filename][] = $token[2];

continue;
}

if ($comment === '// @codeCoverageIgnoreStart' ||
$comment === '//@codeCoverageIgnoreStart') {
$start = $token[2];

continue;
}

if ($comment === '// @codeCoverageIgnoreEnd' ||
$comment === '//@codeCoverageIgnoreEnd') {
if (false === $start) {
$start = $token[2];
}

$this->ignoredLines[$filename] = array_merge(
$this->ignoredLines[$filename],
range($start, $token[2])
);
}
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;

use SebastianBergmann\CodeCoverage\Filter;

final class CacheWarmer
{
public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): void
{
$analyser = new CachingFileAnalyser(
$cacheDirectory,
new ParsingFileAnalyser(
$useAnnotationsForIgnoringCode,
$ignoreDeprecatedCode
),
$useAnnotationsForIgnoringCode,
$ignoreDeprecatedCode,
);

foreach ($filter->files() as $file) {
$analyser->process($file);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\StaticAnalysis;




interface FileAnalyser
{
public function classesIn(string $filename): array;

public function traitsIn(string $filename): array;

public function functionsIn(string $filename): array;




public function linesOfCodeFor(string $filename): array;

public function executableLinesIn(string $filename): array;

public function ignoredLinesFor(string $filename): array;
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Util;

use function sprintf;




final class Percentage
{



private $fraction;




private $total;

public static function fromFractionAndTotal(float $fraction, float $total): self
{
return new self($fraction, $total);
}

private function __construct(float $fraction, float $total)
{
$this->fraction = $fraction;
$this->total = $total;
}

public function asFloat(): float
{
if ($this->total > 0) {
return ($this->fraction / $this->total) * 100;
}

return 100.0;
}

public function asString(): string
{
if ($this->total > 0) {
return sprintf('%01.2F%%', $this->asFloat());
}

return '';
}

public function asFixedWidthString(): string
{
if ($this->total > 0) {
return sprintf('%6.2F%%', $this->asFloat());
}

return '';
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Util;

use function is_dir;
use function mkdir;
use function sprintf;




final class Filesystem
{



public static function createDirectory(string $directory): void
{
$success = !(!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory));

if (!$success) {
throw new DirectoryCouldNotBeCreatedException(
sprintf(
'Directory "%s" could not be created',
$directory
)
);
}
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Node;

use function array_merge;
use function count;
use IteratorAggregate;
use RecursiveIteratorIterator;




final class Directory extends AbstractNode implements IteratorAggregate
{



private $children = [];




private $directories = [];




private $files = [];




private $classes;




private $traits;




private $functions;




private $linesOfCode;




private $numFiles = -1;




private $numExecutableLines = -1;




private $numExecutedLines = -1;




private $numExecutableBranches = -1;




private $numExecutedBranches = -1;




private $numExecutablePaths = -1;




private $numExecutedPaths = -1;




private $numClasses = -1;




private $numTestedClasses = -1;




private $numTraits = -1;




private $numTestedTraits = -1;




private $numMethods = -1;




private $numTestedMethods = -1;




private $numFunctions = -1;




private $numTestedFunctions = -1;

public function count(): int
{
if ($this->numFiles === -1) {
$this->numFiles = 0;

foreach ($this->children as $child) {
$this->numFiles += count($child);
}
}

return $this->numFiles;
}

public function getIterator(): RecursiveIteratorIterator
{
return new RecursiveIteratorIterator(
new Iterator($this),
RecursiveIteratorIterator::SELF_FIRST
);
}

public function addDirectory(string $name): self
{
$directory = new self($name, $this);

$this->children[] = $directory;
$this->directories[] = &$this->children[count($this->children) - 1];

return $directory;
}

public function addFile(File $file): void
{
$this->children[] = $file;
$this->files[] = &$this->children[count($this->children) - 1];

$this->numExecutableLines = -1;
$this->numExecutedLines = -1;
}

public function directories(): array
{
return $this->directories;
}

public function files(): array
{
return $this->files;
}

public function children(): array
{
return $this->children;
}

public function classes(): array
{
if ($this->classes === null) {
$this->classes = [];

foreach ($this->children as $child) {
$this->classes = array_merge(
$this->classes,
$child->classes()
);
}
}

return $this->classes;
}

public function traits(): array
{
if ($this->traits === null) {
$this->traits = [];

foreach ($this->children as $child) {
$this->traits = array_merge(
$this->traits,
$child->traits()
);
}
}

return $this->traits;
}

public function functions(): array
{
if ($this->functions === null) {
$this->functions = [];

foreach ($this->children as $child) {
$this->functions = array_merge(
$this->functions,
$child->functions()
);
}
}

return $this->functions;
}




public function linesOfCode(): array
{
if ($this->linesOfCode === null) {
$this->linesOfCode = [
'linesOfCode' => 0,
'commentLinesOfCode' => 0,
'nonCommentLinesOfCode' => 0,
];

foreach ($this->children as $child) {
$childLinesOfCode = $child->linesOfCode();

$this->linesOfCode['linesOfCode'] += $childLinesOfCode['linesOfCode'];
$this->linesOfCode['commentLinesOfCode'] += $childLinesOfCode['commentLinesOfCode'];
$this->linesOfCode['nonCommentLinesOfCode'] += $childLinesOfCode['nonCommentLinesOfCode'];
}
}

return $this->linesOfCode;
}

public function numberOfExecutableLines(): int
{
if ($this->numExecutableLines === -1) {
$this->numExecutableLines = 0;

foreach ($this->children as $child) {
$this->numExecutableLines += $child->numberOfExecutableLines();
}
}

return $this->numExecutableLines;
}

public function numberOfExecutedLines(): int
{
if ($this->numExecutedLines === -1) {
$this->numExecutedLines = 0;

foreach ($this->children as $child) {
$this->numExecutedLines += $child->numberOfExecutedLines();
}
}

return $this->numExecutedLines;
}

public function numberOfExecutableBranches(): int
{
if ($this->numExecutableBranches === -1) {
$this->numExecutableBranches = 0;

foreach ($this->children as $child) {
$this->numExecutableBranches += $child->numberOfExecutableBranches();
}
}

return $this->numExecutableBranches;
}

public function numberOfExecutedBranches(): int
{
if ($this->numExecutedBranches === -1) {
$this->numExecutedBranches = 0;

foreach ($this->children as $child) {
$this->numExecutedBranches += $child->numberOfExecutedBranches();
}
}

return $this->numExecutedBranches;
}

public function numberOfExecutablePaths(): int
{
if ($this->numExecutablePaths === -1) {
$this->numExecutablePaths = 0;

foreach ($this->children as $child) {
$this->numExecutablePaths += $child->numberOfExecutablePaths();
}
}

return $this->numExecutablePaths;
}

public function numberOfExecutedPaths(): int
{
if ($this->numExecutedPaths === -1) {
$this->numExecutedPaths = 0;

foreach ($this->children as $child) {
$this->numExecutedPaths += $child->numberOfExecutedPaths();
}
}

return $this->numExecutedPaths;
}

public function numberOfClasses(): int
{
if ($this->numClasses === -1) {
$this->numClasses = 0;

foreach ($this->children as $child) {
$this->numClasses += $child->numberOfClasses();
}
}

return $this->numClasses;
}

public function numberOfTestedClasses(): int
{
if ($this->numTestedClasses === -1) {
$this->numTestedClasses = 0;

foreach ($this->children as $child) {
$this->numTestedClasses += $child->numberOfTestedClasses();
}
}

return $this->numTestedClasses;
}

public function numberOfTraits(): int
{
if ($this->numTraits === -1) {
$this->numTraits = 0;

foreach ($this->children as $child) {
$this->numTraits += $child->numberOfTraits();
}
}

return $this->numTraits;
}

public function numberOfTestedTraits(): int
{
if ($this->numTestedTraits === -1) {
$this->numTestedTraits = 0;

foreach ($this->children as $child) {
$this->numTestedTraits += $child->numberOfTestedTraits();
}
}

return $this->numTestedTraits;
}

public function numberOfMethods(): int
{
if ($this->numMethods === -1) {
$this->numMethods = 0;

foreach ($this->children as $child) {
$this->numMethods += $child->numberOfMethods();
}
}

return $this->numMethods;
}

public function numberOfTestedMethods(): int
{
if ($this->numTestedMethods === -1) {
$this->numTestedMethods = 0;

foreach ($this->children as $child) {
$this->numTestedMethods += $child->numberOfTestedMethods();
}
}

return $this->numTestedMethods;
}

public function numberOfFunctions(): int
{
if ($this->numFunctions === -1) {
$this->numFunctions = 0;

foreach ($this->children as $child) {
$this->numFunctions += $child->numberOfFunctions();
}
}

return $this->numFunctions;
}

public function numberOfTestedFunctions(): int
{
if ($this->numTestedFunctions === -1) {
$this->numTestedFunctions = 0;

foreach ($this->children as $child) {
$this->numTestedFunctions += $child->numberOfTestedFunctions();
}
}

return $this->numTestedFunctions;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Node;

use function count;
use RecursiveIterator;




final class Iterator implements RecursiveIterator
{



private $position;




private $nodes;

public function __construct(Directory $node)
{
$this->nodes = $node->children();
}




public function rewind(): void
{
$this->position = 0;
}




public function valid(): bool
{
return $this->position < count($this->nodes);
}




public function key(): int
{
return $this->position;
}




public function current(): ?AbstractNode
{
return $this->valid() ? $this->nodes[$this->position] : null;
}




public function next(): void
{
$this->position++;
}




public function getChildren(): self
{
return new self($this->nodes[$this->position]);
}




public function hasChildren(): bool
{
return $this->nodes[$this->position] instanceof Directory;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Node;

use const DIRECTORY_SEPARATOR;
use function array_merge;
use function str_replace;
use function substr;
use Countable;
use SebastianBergmann\CodeCoverage\Util\Percentage;




abstract class AbstractNode implements Countable
{



private $name;




private $pathAsString;




private $pathAsArray;




private $parent;




private $id;

public function __construct(string $name, ?self $parent = null)
{
if (substr($name, -1) === DIRECTORY_SEPARATOR) {
$name = substr($name, 0, -1);
}

$this->name = $name;
$this->parent = $parent;
}

public function name(): string
{
return $this->name;
}

public function id(): string
{
if ($this->id === null) {
$parent = $this->parent();

if ($parent === null) {
$this->id = 'index';
} else {
$parentId = $parent->id();

if ($parentId === 'index') {
$this->id = str_replace(':', '_', $this->name);
} else {
$this->id = $parentId . '/' . $this->name;
}
}
}

return $this->id;
}

public function pathAsString(): string
{
if ($this->pathAsString === null) {
if ($this->parent === null) {
$this->pathAsString = $this->name;
} else {
$this->pathAsString = $this->parent->pathAsString() . DIRECTORY_SEPARATOR . $this->name;
}
}

return $this->pathAsString;
}

public function pathAsArray(): array
{
if ($this->pathAsArray === null) {
if ($this->parent === null) {
$this->pathAsArray = [];
} else {
$this->pathAsArray = $this->parent->pathAsArray();
}

$this->pathAsArray[] = $this;
}

return $this->pathAsArray;
}

public function parent(): ?self
{
return $this->parent;
}

public function percentageOfTestedClasses(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfTestedClasses(),
$this->numberOfClasses(),
);
}

public function percentageOfTestedTraits(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfTestedTraits(),
$this->numberOfTraits(),
);
}

public function percentageOfTestedClassesAndTraits(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfTestedClassesAndTraits(),
$this->numberOfClassesAndTraits(),
);
}

public function percentageOfTestedFunctions(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfTestedFunctions(),
$this->numberOfFunctions(),
);
}

public function percentageOfTestedMethods(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfTestedMethods(),
$this->numberOfMethods(),
);
}

public function percentageOfTestedFunctionsAndMethods(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfTestedFunctionsAndMethods(),
$this->numberOfFunctionsAndMethods(),
);
}

public function percentageOfExecutedLines(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfExecutedLines(),
$this->numberOfExecutableLines(),
);
}

public function percentageOfExecutedBranches(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfExecutedBranches(),
$this->numberOfExecutableBranches()
);
}

public function percentageOfExecutedPaths(): Percentage
{
return Percentage::fromFractionAndTotal(
$this->numberOfExecutedPaths(),
$this->numberOfExecutablePaths()
);
}

public function numberOfClassesAndTraits(): int
{
return $this->numberOfClasses() + $this->numberOfTraits();
}

public function numberOfTestedClassesAndTraits(): int
{
return $this->numberOfTestedClasses() + $this->numberOfTestedTraits();
}

public function classesAndTraits(): array
{
return array_merge($this->classes(), $this->traits());
}

public function numberOfFunctionsAndMethods(): int
{
return $this->numberOfFunctions() + $this->numberOfMethods();
}

public function numberOfTestedFunctionsAndMethods(): int
{
return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods();
}

abstract public function classes(): array;

abstract public function traits(): array;

abstract public function functions(): array;




abstract public function linesOfCode(): array;

abstract public function numberOfExecutableLines(): int;

abstract public function numberOfExecutedLines(): int;

abstract public function numberOfExecutableBranches(): int;

abstract public function numberOfExecutedBranches(): int;

abstract public function numberOfExecutablePaths(): int;

abstract public function numberOfExecutedPaths(): int;

abstract public function numberOfClasses(): int;

abstract public function numberOfTestedClasses(): int;

abstract public function numberOfTraits(): int;

abstract public function numberOfTestedTraits(): int;

abstract public function numberOfMethods(): int;

abstract public function numberOfTestedMethods(): int;

abstract public function numberOfFunctions(): int;

abstract public function numberOfTestedFunctions(): int;
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Node;

use function sprintf;




final class CrapIndex
{



private $cyclomaticComplexity;




private $codeCoverage;

public function __construct(int $cyclomaticComplexity, float $codeCoverage)
{
$this->cyclomaticComplexity = $cyclomaticComplexity;
$this->codeCoverage = $codeCoverage;
}

public function asString(): string
{
if ($this->codeCoverage === 0.0) {
return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity);
}

if ($this->codeCoverage >= 95) {
return (string) $this->cyclomaticComplexity;
}

return sprintf(
'%01.2F',
$this->cyclomaticComplexity ** 2 * (1 - $this->codeCoverage / 100) ** 3 + $this->cyclomaticComplexity
);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Node;

use function array_filter;
use function count;
use function range;




final class File extends AbstractNode
{



private $lineCoverageData;




private $functionCoverageData;




private $testData;




private $numExecutableLines = 0;




private $numExecutedLines = 0;




private $numExecutableBranches = 0;




private $numExecutedBranches = 0;




private $numExecutablePaths = 0;




private $numExecutedPaths = 0;




private $classes = [];




private $traits = [];




private $functions = [];




private $linesOfCode;




private $numClasses;




private $numTestedClasses = 0;




private $numTraits;




private $numTestedTraits = 0;




private $numMethods;




private $numTestedMethods;




private $numTestedFunctions;




private $codeUnitsByLine = [];




public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, array $linesOfCode)
{
parent::__construct($name, $parent);

$this->lineCoverageData = $lineCoverageData;
$this->functionCoverageData = $functionCoverageData;
$this->testData = $testData;
$this->linesOfCode = $linesOfCode;

$this->calculateStatistics($classes, $traits, $functions);
}

public function count(): int
{
return 1;
}

public function lineCoverageData(): array
{
return $this->lineCoverageData;
}

public function functionCoverageData(): array
{
return $this->functionCoverageData;
}

public function testData(): array
{
return $this->testData;
}

public function classes(): array
{
return $this->classes;
}

public function traits(): array
{
return $this->traits;
}

public function functions(): array
{
return $this->functions;
}




public function linesOfCode(): array
{
return $this->linesOfCode;
}

public function numberOfExecutableLines(): int
{
return $this->numExecutableLines;
}

public function numberOfExecutedLines(): int
{
return $this->numExecutedLines;
}

public function numberOfExecutableBranches(): int
{
return $this->numExecutableBranches;
}

public function numberOfExecutedBranches(): int
{
return $this->numExecutedBranches;
}

public function numberOfExecutablePaths(): int
{
return $this->numExecutablePaths;
}

public function numberOfExecutedPaths(): int
{
return $this->numExecutedPaths;
}

public function numberOfClasses(): int
{
if ($this->numClasses === null) {
$this->numClasses = 0;

foreach ($this->classes as $class) {
foreach ($class['methods'] as $method) {
if ($method['executableLines'] > 0) {
$this->numClasses++;

continue 2;
}
}
}
}

return $this->numClasses;
}

public function numberOfTestedClasses(): int
{
return $this->numTestedClasses;
}

public function numberOfTraits(): int
{
if ($this->numTraits === null) {
$this->numTraits = 0;

foreach ($this->traits as $trait) {
foreach ($trait['methods'] as $method) {
if ($method['executableLines'] > 0) {
$this->numTraits++;

continue 2;
}
}
}
}

return $this->numTraits;
}

public function numberOfTestedTraits(): int
{
return $this->numTestedTraits;
}

public function numberOfMethods(): int
{
if ($this->numMethods === null) {
$this->numMethods = 0;

foreach ($this->classes as $class) {
foreach ($class['methods'] as $method) {
if ($method['executableLines'] > 0) {
$this->numMethods++;
}
}
}

foreach ($this->traits as $trait) {
foreach ($trait['methods'] as $method) {
if ($method['executableLines'] > 0) {
$this->numMethods++;
}
}
}
}

return $this->numMethods;
}

public function numberOfTestedMethods(): int
{
if ($this->numTestedMethods === null) {
$this->numTestedMethods = 0;

foreach ($this->classes as $class) {
foreach ($class['methods'] as $method) {
if ($method['executableLines'] > 0 &&
$method['coverage'] === 100) {
$this->numTestedMethods++;
}
}
}

foreach ($this->traits as $trait) {
foreach ($trait['methods'] as $method) {
if ($method['executableLines'] > 0 &&
$method['coverage'] === 100) {
$this->numTestedMethods++;
}
}
}
}

return $this->numTestedMethods;
}

public function numberOfFunctions(): int
{
return count($this->functions);
}

public function numberOfTestedFunctions(): int
{
if ($this->numTestedFunctions === null) {
$this->numTestedFunctions = 0;

foreach ($this->functions as $function) {
if ($function['executableLines'] > 0 &&
$function['coverage'] === 100) {
$this->numTestedFunctions++;
}
}
}

return $this->numTestedFunctions;
}

private function calculateStatistics(array $classes, array $traits, array $functions): void
{
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [];
}

$this->processClasses($classes);
$this->processTraits($traits);
$this->processFunctions($functions);

foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
if (isset($this->lineCoverageData[$lineNumber])) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executableLines']++;
}

unset($codeUnit);

$this->numExecutableLines++;

if (count($this->lineCoverageData[$lineNumber]) > 0) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executedLines']++;
}

unset($codeUnit);

$this->numExecutedLines++;
}
}
}

foreach ($this->traits as &$trait) {
foreach ($trait['methods'] as &$method) {
$methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100;
$methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0;
$methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0;

$method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage;
$method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString();

$trait['ccn'] += $method['ccn'];
}

unset($method);

$traitLineCoverage = $trait['executableLines'] ? ($trait['executedLines'] / $trait['executableLines']) * 100 : 100;
$traitBranchCoverage = $trait['executableBranches'] ? ($trait['executedBranches'] / $trait['executableBranches']) * 100 : 0;
$traitPathCoverage = $trait['executablePaths'] ? ($trait['executedPaths'] / $trait['executablePaths']) * 100 : 0;

$trait['coverage'] = $traitBranchCoverage ?: $traitLineCoverage;
$trait['crap'] = (new CrapIndex($trait['ccn'], $traitPathCoverage ?: $traitLineCoverage))->asString();

if ($trait['executableLines'] > 0 && $trait['coverage'] === 100) {
$this->numTestedClasses++;
}
}

unset($trait);

foreach ($this->classes as &$class) {
foreach ($class['methods'] as &$method) {
$methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100;
$methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0;
$methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0;

$method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage;
$method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString();

$class['ccn'] += $method['ccn'];
}

unset($method);

$classLineCoverage = $class['executableLines'] ? ($class['executedLines'] / $class['executableLines']) * 100 : 100;
$classBranchCoverage = $class['executableBranches'] ? ($class['executedBranches'] / $class['executableBranches']) * 100 : 0;
$classPathCoverage = $class['executablePaths'] ? ($class['executedPaths'] / $class['executablePaths']) * 100 : 0;

$class['coverage'] = $classBranchCoverage ?: $classLineCoverage;
$class['crap'] = (new CrapIndex($class['ccn'], $classPathCoverage ?: $classLineCoverage))->asString();

if ($class['executableLines'] > 0 && $class['coverage'] === 100) {
$this->numTestedClasses++;
}
}

unset($class);

foreach ($this->functions as &$function) {
$functionLineCoverage = $function['executableLines'] ? ($function['executedLines'] / $function['executableLines']) * 100 : 100;
$functionBranchCoverage = $function['executableBranches'] ? ($function['executedBranches'] / $function['executableBranches']) * 100 : 0;
$functionPathCoverage = $function['executablePaths'] ? ($function['executedPaths'] / $function['executablePaths']) * 100 : 0;

$function['coverage'] = $functionBranchCoverage ?: $functionLineCoverage;
$function['crap'] = (new CrapIndex($function['ccn'], $functionPathCoverage ?: $functionLineCoverage))->asString();

if ($function['coverage'] === 100) {
$this->numTestedFunctions++;
}
}
}

private function processClasses(array $classes): void
{
$link = $this->id() . '.html#';

foreach ($classes as $className => $class) {
$this->classes[$className] = [
'className' => $className,
'namespace' => $class['namespace'],
'methods' => [],
'startLine' => $class['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
'executedBranches' => 0,
'executablePaths' => 0,
'executedPaths' => 0,
'ccn' => 0,
'coverage' => 0,
'crap' => 0,
'link' => $link . $class['startLine'],
];

foreach ($class['methods'] as $methodName => $method) {
$methodData = $this->newMethod($className, $methodName, $method, $link);
$this->classes[$className]['methods'][$methodName] = $methodData;

$this->classes[$className]['executableBranches'] += $methodData['executableBranches'];
$this->classes[$className]['executedBranches'] += $methodData['executedBranches'];
$this->classes[$className]['executablePaths'] += $methodData['executablePaths'];
$this->classes[$className]['executedPaths'] += $methodData['executedPaths'];

$this->numExecutableBranches += $methodData['executableBranches'];
$this->numExecutedBranches += $methodData['executedBranches'];
$this->numExecutablePaths += $methodData['executablePaths'];
$this->numExecutedPaths += $methodData['executedPaths'];

foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [
&$this->classes[$className],
&$this->classes[$className]['methods'][$methodName],
];
}
}
}
}

private function processTraits(array $traits): void
{
$link = $this->id() . '.html#';

foreach ($traits as $traitName => $trait) {
$this->traits[$traitName] = [
'traitName' => $traitName,
'namespace' => $trait['namespace'],
'methods' => [],
'startLine' => $trait['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
'executedBranches' => 0,
'executablePaths' => 0,
'executedPaths' => 0,
'ccn' => 0,
'coverage' => 0,
'crap' => 0,
'link' => $link . $trait['startLine'],
];

foreach ($trait['methods'] as $methodName => $method) {
$methodData = $this->newMethod($traitName, $methodName, $method, $link);
$this->traits[$traitName]['methods'][$methodName] = $methodData;

$this->traits[$traitName]['executableBranches'] += $methodData['executableBranches'];
$this->traits[$traitName]['executedBranches'] += $methodData['executedBranches'];
$this->traits[$traitName]['executablePaths'] += $methodData['executablePaths'];
$this->traits[$traitName]['executedPaths'] += $methodData['executedPaths'];

$this->numExecutableBranches += $methodData['executableBranches'];
$this->numExecutedBranches += $methodData['executedBranches'];
$this->numExecutablePaths += $methodData['executablePaths'];
$this->numExecutedPaths += $methodData['executedPaths'];

foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [
&$this->traits[$traitName],
&$this->traits[$traitName]['methods'][$methodName],
];
}
}
}
}

private function processFunctions(array $functions): void
{
$link = $this->id() . '.html#';

foreach ($functions as $functionName => $function) {
$this->functions[$functionName] = [
'functionName' => $functionName,
'namespace' => $function['namespace'],
'signature' => $function['signature'],
'startLine' => $function['startLine'],
'endLine' => $function['endLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
'executedBranches' => 0,
'executablePaths' => 0,
'executedPaths' => 0,
'ccn' => $function['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $function['startLine'],
];

foreach (range($function['startLine'], $function['endLine']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]];
}

if (isset($this->functionCoverageData[$functionName]['branches'])) {
$this->functions[$functionName]['executableBranches'] = count(
$this->functionCoverageData[$functionName]['branches']
);

$this->functions[$functionName]['executedBranches'] = count(
array_filter(
$this->functionCoverageData[$functionName]['branches'],
static function (array $branch)
{
return (bool) $branch['hit'];
}
)
);
}

if (isset($this->functionCoverageData[$functionName]['paths'])) {
$this->functions[$functionName]['executablePaths'] = count(
$this->functionCoverageData[$functionName]['paths']
);

$this->functions[$functionName]['executedPaths'] = count(
array_filter(
$this->functionCoverageData[$functionName]['paths'],
static function (array $path)
{
return (bool) $path['hit'];
}
)
);
}

$this->numExecutableBranches += $this->functions[$functionName]['executableBranches'];
$this->numExecutedBranches += $this->functions[$functionName]['executedBranches'];
$this->numExecutablePaths += $this->functions[$functionName]['executablePaths'];
$this->numExecutedPaths += $this->functions[$functionName]['executedPaths'];
}
}

private function newMethod(string $className, string $methodName, array $method, string $link): array
{
$methodData = [
'methodName' => $methodName,
'visibility' => $method['visibility'],
'signature' => $method['signature'],
'startLine' => $method['startLine'],
'endLine' => $method['endLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
'executedBranches' => 0,
'executablePaths' => 0,
'executedPaths' => 0,
'ccn' => $method['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $method['startLine'],
];

$key = $className . '->' . $methodName;

if (isset($this->functionCoverageData[$key]['branches'])) {
$methodData['executableBranches'] = count(
$this->functionCoverageData[$key]['branches']
);

$methodData['executedBranches'] = count(
array_filter(
$this->functionCoverageData[$key]['branches'],
static function (array $branch)
{
return (bool) $branch['hit'];
}
)
);
}

if (isset($this->functionCoverageData[$key]['paths'])) {
$methodData['executablePaths'] = count(
$this->functionCoverageData[$key]['paths']
);

$methodData['executedPaths'] = count(
array_filter(
$this->functionCoverageData[$key]['paths'],
static function (array $path)
{
return (bool) $path['hit'];
}
)
);
}

return $methodData;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Node;

use const DIRECTORY_SEPARATOR;
use function array_shift;
use function basename;
use function count;
use function dirname;
use function explode;
use function implode;
use function is_file;
use function str_replace;
use function strpos;
use function substr;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;




final class Builder
{



private $analyser;

public function __construct(FileAnalyser $analyser)
{
$this->analyser = $analyser;
}

public function build(CodeCoverage $coverage): Directory
{
$data = clone $coverage->getData(); 
$commonPath = $this->reducePaths($data);
$root = new Directory(
$commonPath,
null
);

$this->addItems(
$root,
$this->buildDirectoryStructure($data),
$coverage->getTests()
);

return $root;
}

private function addItems(Directory $root, array $items, array $tests): void
{
foreach ($items as $key => $value) {
$key = (string) $key;

if (substr($key, -2) === '/f') {
$key = substr($key, 0, -2);
$filename = $root->pathAsString() . DIRECTORY_SEPARATOR . $key;

if (is_file($filename)) {
$root->addFile(
new File(
$key,
$root,
$value['lineCoverage'],
$value['functionCoverage'],
$tests,
$this->analyser->classesIn($filename),
$this->analyser->traitsIn($filename),
$this->analyser->functionsIn($filename),
$this->analyser->linesOfCodeFor($filename)
)
);
}
} else {
$child = $root->addDirectory($key);

$this->addItems($child, $value, $tests);
}
}
}









































private function buildDirectoryStructure(ProcessedCodeCoverageData $data): array
{
$result = [];

foreach ($data->coveredFiles() as $originalPath) {
$path = explode(DIRECTORY_SEPARATOR, $originalPath);
$pointer = &$result;
$max = count($path);

for ($i = 0; $i < $max; $i++) {
$type = '';

if ($i === ($max - 1)) {
$type = '/f';
}

$pointer = &$pointer[$path[$i] . $type];
}

$pointer = [
'lineCoverage' => $data->lineCoverage()[$originalPath] ?? [],
'functionCoverage' => $data->functionCoverage()[$originalPath] ?? [],
];
}

return $result;
}






































private function reducePaths(ProcessedCodeCoverageData $coverage): string
{
if (empty($coverage->coveredFiles())) {
return '.';
}

$commonPath = '';
$paths = $coverage->coveredFiles();

if (count($paths) === 1) {
$commonPath = dirname($paths[0]) . DIRECTORY_SEPARATOR;
$coverage->renameFile($paths[0], basename($paths[0]));

return $commonPath;
}

$max = count($paths);

for ($i = 0; $i < $max; $i++) {

if (strpos($paths[$i], 'phar://') === 0) {
$paths[$i] = substr($paths[$i], 7);
$paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]);
}
$paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);

if (empty($paths[$i][0])) {
$paths[$i][0] = DIRECTORY_SEPARATOR;
}
}

$done = false;
$max = count($paths);

while (!$done) {
for ($i = 0; $i < $max - 1; $i++) {
if (!isset($paths[$i][0]) ||
!isset($paths[$i + 1][0]) ||
$paths[$i][0] !== $paths[$i + 1][0]) {
$done = true;

break;
}
}

if (!$done) {
$commonPath .= $paths[0][0];

if ($paths[0][0] !== DIRECTORY_SEPARATOR) {
$commonPath .= DIRECTORY_SEPARATOR;
}

for ($i = 0; $i < $max; $i++) {
array_shift($paths[$i]);
}
}
}

$original = $coverage->coveredFiles();
$max = count($original);

for ($i = 0; $i < $max; $i++) {
$coverage->renameFile($original[$i], implode(DIRECTORY_SEPARATOR, $paths[$i]));
}

return substr($commonPath, 0, -1);
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class ReflectionException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class ReportAlreadyFinalizedException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('The code coverage report has already been finalized');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class NoCodeCoverageDriverWithPathCoverageSupportAvailableException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('No code coverage driver with path coverage support available');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class Xdebug3NotEnabledException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class StaticAnalysisCacheNotConfiguredException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class DeadCodeDetectionNotSupportedException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Util;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class DirectoryCouldNotBeCreatedException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class NoCodeCoverageDriverAvailableException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('No code coverage driver available');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use Throwable;

interface Exception extends Throwable
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class PcovNotAvailableException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('The PCOV extension is not available');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class ParserException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class TestIdMissingException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('Test ID is missing');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class XdebugNotAvailableException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('The Xdebug extension is not available');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use function sprintf;
use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class PathExistsButIsNotDirectoryException extends RuntimeException implements Exception
{
public function __construct(string $path)
{
parent::__construct(sprintf('"%s" exists but is not a directory', $path));
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class UnintentionallyCoveredCodeException extends RuntimeException implements Exception
{



private $unintentionallyCoveredUnits;

public function __construct(array $unintentionallyCoveredUnits)
{
$this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits;

parent::__construct($this->toString());
}

public function getUnintentionallyCoveredUnits(): array
{
return $this->unintentionallyCoveredUnits;
}

private function toString(): string
{
$message = '';

foreach ($this->unintentionallyCoveredUnits as $unit) {
$message .= '- ' . $unit . "\n";
}

return $message;
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class BranchAndPathCoverageNotSupportedException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class PhpdbgNotAvailableException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('The PHPDBG SAPI is not available');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class WrongXdebugVersionException extends RuntimeException implements Exception
{
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use function sprintf;
use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class WriteOperationFailedException extends RuntimeException implements Exception
{
public function __construct(string $path)
{
parent::__construct(sprintf('Cannot write to "%s"', $path));
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage\Driver;

use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;

final class Xdebug2NotEnabledException extends RuntimeException implements Exception
{
public function __construct()
{
parent::__construct('xdebug.coverage_enable=On has to be set');
}
}
<?php declare(strict_types=1);








namespace SebastianBergmann\CodeCoverage;

use RuntimeException;

final class XmlException extends RuntimeException implements Exception
{
}
<?php

if (!function_exists('getallheaders')) {






function getallheaders()
{
$headers = array();

$copy_server = array(
'CONTENT_TYPE' => 'Content-Type',
'CONTENT_LENGTH' => 'Content-Length',
'CONTENT_MD5' => 'Content-Md5',
);

foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) === 'HTTP_') {
$key = substr($key, 5);
if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
$key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
$headers[$key] = $value;
}
} elseif (isset($copy_server[$key])) {
$headers[$copy_server[$key]] = $value;
}
}

if (!isset($headers['Authorization'])) {
if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
$basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
$headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
} elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
$headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
}
}

return $headers;
}

}
<?php










namespace Symfony\Polyfill\Intl\Grapheme;

\define('SYMFONY_GRAPHEME_CLUSTER_RX', ((float) \PCRE_VERSION < 10 ? (float) \PCRE_VERSION >= 8.32 : (float) \PCRE_VERSION >= 10.39) ? '\X' : Grapheme::GRAPHEME_CLUSTER_RX);



















final class Grapheme
{


public const GRAPHEME_CLUSTER_RX = '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';

private const CASE_FOLD = [
['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
];

public static function grapheme_extract($s, $size, $type = \GRAPHEME_EXTR_COUNT, $start = 0, &$next = 0)
{
if (0 > $start) {
$start = \strlen($s) + $start;
}

if (!\is_scalar($s)) {
$hasError = false;
set_error_handler(function () use (&$hasError) { $hasError = true; });
$next = substr($s, $start);
restore_error_handler();
if ($hasError) {
substr($s, $start);
$s = '';
} else {
$s = $next;
}
} else {
$s = substr($s, $start);
}
$size = (int) $size;
$type = (int) $type;
$start = (int) $start;

if (\GRAPHEME_EXTR_COUNT !== $type && \GRAPHEME_EXTR_MAXBYTES !== $type && \GRAPHEME_EXTR_MAXCHARS !== $type) {
if (80000 > \PHP_VERSION_ID) {
return false;
}

throw new \ValueError('grapheme_extract(): Argument #3 ($type) must be one of GRAPHEME_EXTR_COUNT, GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS');
}

if (!isset($s[0]) || 0 > $size || 0 > $start) {
return false;
}
if (0 === $size) {
return '';
}

$next = $start;

$s = preg_split('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', "\r\n".$s, $size + 1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);

if (!isset($s[1])) {
return false;
}

$i = 1;
$ret = '';

do {
if (\GRAPHEME_EXTR_COUNT === $type) {
--$size;
} elseif (\GRAPHEME_EXTR_MAXBYTES === $type) {
$size -= \strlen($s[$i]);
} else {
$size -= iconv_strlen($s[$i], 'UTF-8//IGNORE');
}

if ($size >= 0) {
$ret .= $s[$i];
}
} while (isset($s[++$i]) && $size > 0);

$next += \strlen($ret);

return $ret;
}

public static function grapheme_strlen($s)
{
preg_replace('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', '', $s, -1, $len);

return 0 === $len && '' !== $s ? null : $len;
}

public static function grapheme_substr($s, $start, $len = null)
{
if (null === $len) {
$len = 2147483647;
}

preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s, $s);

$slen = \count($s[0]);
$start = (int) $start;

if (0 > $start) {
$start += $slen;
}
if (0 > $start) {
if (\PHP_VERSION_ID < 80000) {
return false;
}

$start = 0;
}
if ($start >= $slen) {
return \PHP_VERSION_ID >= 80000 ? '' : false;
}

$rem = $slen - $start;

if (0 > $len) {
$len += $rem;
}
if (0 === $len) {
return '';
}
if (0 > $len) {
return \PHP_VERSION_ID >= 80000 ? '' : false;
}
if ($len > $rem) {
$len = $rem;
}

return implode('', \array_slice($s[0], $start, $len));
}

public static function grapheme_strpos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 0);
}

public static function grapheme_stripos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 1);
}

public static function grapheme_strrpos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 2);
}

public static function grapheme_strripos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 3);
}

public static function grapheme_stristr($s, $needle, $beforeNeedle = false)
{
return mb_stristr($s, $needle, $beforeNeedle, 'UTF-8');
}

public static function grapheme_strstr($s, $needle, $beforeNeedle = false)
{
return mb_strstr($s, $needle, $beforeNeedle, 'UTF-8');
}

private static function grapheme_position($s, $needle, $offset, $mode)
{
$needle = (string) $needle;
if (80000 > \PHP_VERSION_ID && !preg_match('/./us', $needle)) {
return false;
}
$s = (string) $s;
if (!preg_match('/./us', $s)) {
return false;
}
if ($offset > 0) {
$s = self::grapheme_substr($s, $offset);
} elseif ($offset < 0) {
if (2 > $mode) {
$offset += self::grapheme_strlen($s);
$s = self::grapheme_substr($s, $offset);
if (0 > $offset) {
$offset = 0;
}
} elseif (0 > $offset += self::grapheme_strlen($needle)) {
$s = self::grapheme_substr($s, 0, $offset);
$offset = 0;
} else {
$offset = 0;
}
}




$caseInsensitive = $mode & 1;
$reverse = $mode & 2;
if ($caseInsensitive) {



$mode = \defined('MB_CASE_FOLD_SIMPLE') ? \MB_CASE_FOLD_SIMPLE : \MB_CASE_LOWER;
$s = mb_convert_case($s, $mode, 'UTF-8');
$needle = mb_convert_case($needle, $mode, 'UTF-8');

if (!\defined('MB_CASE_FOLD_SIMPLE')) {
$s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s);
$needle = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $needle);
}
}
if ($reverse) {
$needlePos = strrpos($s, $needle);
} else {
$needlePos = strpos($s, $needle);
}

return false !== $needlePos ? self::grapheme_strlen(substr($s, 0, $needlePos)) + $offset : false;
}
}
<?php










use Symfony\Polyfill\Intl\Grapheme as p;

if (extension_loaded('intl')) {
return;
}

if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}

if (!defined('GRAPHEME_EXTR_COUNT')) {
define('GRAPHEME_EXTR_COUNT', 0);
}
if (!defined('GRAPHEME_EXTR_MAXBYTES')) {
define('GRAPHEME_EXTR_MAXBYTES', 1);
}
if (!defined('GRAPHEME_EXTR_MAXCHARS')) {
define('GRAPHEME_EXTR_MAXCHARS', 2);
}

if (!function_exists('grapheme_extract')) {
function grapheme_extract($haystack, $size, $type = 0, $start = 0, &$next = 0) { return p\Grapheme::grapheme_extract($haystack, $size, $type, $start, $next); }
}
if (!function_exists('grapheme_stripos')) {
function grapheme_stripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_stripos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_stristr')) {
function grapheme_stristr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_stristr($haystack, $needle, $beforeNeedle); }
}
if (!function_exists('grapheme_strlen')) {
function grapheme_strlen($input) { return p\Grapheme::grapheme_strlen($input); }
}
if (!function_exists('grapheme_strpos')) {
function grapheme_strpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strpos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_strripos')) {
function grapheme_strripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strripos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_strrpos')) {
function grapheme_strrpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strrpos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_strstr')) {
function grapheme_strstr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_strstr($haystack, $needle, $beforeNeedle); }
}
if (!function_exists('grapheme_substr')) {
function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::grapheme_substr($string, $offset, $length); }
}
<?php










use Symfony\Polyfill\Intl\Grapheme as p;

if (!defined('GRAPHEME_EXTR_COUNT')) {
define('GRAPHEME_EXTR_COUNT', 0);
}
if (!defined('GRAPHEME_EXTR_MAXBYTES')) {
define('GRAPHEME_EXTR_MAXBYTES', 1);
}
if (!defined('GRAPHEME_EXTR_MAXCHARS')) {
define('GRAPHEME_EXTR_MAXCHARS', 2);
}

if (!function_exists('grapheme_extract')) {
function grapheme_extract(?string $haystack, ?int $size, ?int $type = GRAPHEME_EXTR_COUNT, ?int $offset = 0, &$next = null): string|false { return p\Grapheme::grapheme_extract((string) $haystack, (int) $size, (int) $type, (int) $offset, $next); }
}
if (!function_exists('grapheme_stripos')) {
function grapheme_stripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_stripos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_stristr')) {
function grapheme_stristr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_stristr((string) $haystack, (string) $needle, (bool) $beforeNeedle); }
}
if (!function_exists('grapheme_strlen')) {
function grapheme_strlen(?string $string): int|false|null { return p\Grapheme::grapheme_strlen((string) $string); }
}
if (!function_exists('grapheme_strpos')) {
function grapheme_strpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strpos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_strripos')) {
function grapheme_strripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strripos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_strrpos')) {
function grapheme_strrpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strrpos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_strstr')) {
function grapheme_strstr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_strstr((string) $haystack, (string) $needle, (bool) $beforeNeedle); }
}
if (!function_exists('grapheme_substr')) {
function grapheme_substr(?string $string, ?int $offset, ?int $length = null): string|false { return p\Grapheme::grapheme_substr((string) $string, (int) $offset, $length); }
}
<?php










namespace Symfony\Component\EventDispatcher;

use Symfony\Contracts\EventDispatcher\Event;











class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
{
protected $subject;
protected $arguments;







public function __construct(mixed $subject = null, array $arguments = [])
{
$this->subject = $subject;
$this->arguments = $arguments;
}




public function getSubject(): mixed
{
return $this->subject;
}






public function getArgument(string $key): mixed
{
if ($this->hasArgument($key)) {
return $this->arguments[$key];
}

throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
}






public function setArgument(string $key, mixed $value): static
{
$this->arguments[$key] = $value;

return $this;
}




public function getArguments(): array
{
return $this->arguments;
}






public function setArguments(array $args = []): static
{
$this->arguments = $args;

return $this;
}




public function hasArgument(string $key): bool
{
return \array_key_exists($key, $this->arguments);
}








public function offsetGet(mixed $key): mixed
{
return $this->getArgument($key);
}






public function offsetSet(mixed $key, mixed $value): void
{
$this->setArgument($key, $value);
}






public function offsetUnset(mixed $key): void
{
if ($this->hasArgument($key)) {
unset($this->arguments[$key]);
}
}






public function offsetExists(mixed $key): bool
{
return $this->hasArgument($key);
}






public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->arguments);
}
}
<?php










namespace Symfony\Component\EventDispatcher;

use Psr\EventDispatcher\StoppableEventInterface;
use Symfony\Component\EventDispatcher\Debug\WrappedListener;
















class EventDispatcher implements EventDispatcherInterface
{
private array $listeners = [];
private array $sorted = [];
private array $optimized;

public function __construct()
{
if (__CLASS__ === static::class) {
$this->optimized = [];
}
}




public function dispatch(object $event, string $eventName = null): object
{
$eventName = $eventName ?? \get_class($event);

if (isset($this->optimized)) {
$listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
} else {
$listeners = $this->getListeners($eventName);
}

if ($listeners) {
$this->callListeners($listeners, $eventName, $event);
}

return $event;
}




public function getListeners(string $eventName = null): array
{
if (null !== $eventName) {
if (empty($this->listeners[$eventName])) {
return [];
}

if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}

return $this->sorted[$eventName];
}

foreach ($this->listeners as $eventName => $eventListeners) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
}

return array_filter($this->sorted);
}




public function getListenerPriority(string $eventName, callable|array $listener): ?int
{
if (empty($this->listeners[$eventName])) {
return null;
}

if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}

foreach ($this->listeners[$eventName] as $priority => &$listeners) {
foreach ($listeners as &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
return $priority;
}
}
}

return null;
}




public function hasListeners(string $eventName = null): bool
{
if (null !== $eventName) {
return !empty($this->listeners[$eventName]);
}

foreach ($this->listeners as $eventListeners) {
if ($eventListeners) {
return true;
}
}

return false;
}




public function addListener(string $eventName, callable|array $listener, int $priority = 0)
{
$this->listeners[$eventName][$priority][] = $listener;
unset($this->sorted[$eventName], $this->optimized[$eventName]);
}




public function removeListener(string $eventName, callable|array $listener)
{
if (empty($this->listeners[$eventName])) {
return;
}

if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}

foreach ($this->listeners[$eventName] as $priority => &$listeners) {
foreach ($listeners as $k => &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
}
}

if (!$listeners) {
unset($this->listeners[$eventName][$priority]);
}
}
}




public function addSubscriber(EventSubscriberInterface $subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (\is_string($params)) {
$this->addListener($eventName, [$subscriber, $params]);
} elseif (\is_string($params[0])) {
$this->addListener($eventName, [$subscriber, $params[0]], $params[1] ?? 0);
} else {
foreach ($params as $listener) {
$this->addListener($eventName, [$subscriber, $listener[0]], $listener[1] ?? 0);
}
}
}
}




public function removeSubscriber(EventSubscriberInterface $subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (\is_array($params) && \is_array($params[0])) {
foreach ($params as $listener) {
$this->removeListener($eventName, [$subscriber, $listener[0]]);
}
} else {
$this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]);
}
}
}











protected function callListeners(iterable $listeners, string $eventName, object $event)
{
$stoppable = $event instanceof StoppableEventInterface;

foreach ($listeners as $listener) {
if ($stoppable && $event->isPropagationStopped()) {
break;
}
$listener($event, $eventName, $this);
}
}




private function sortListeners(string $eventName)
{
krsort($this->listeners[$eventName]);
$this->sorted[$eventName] = [];

foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as $k => &$listener) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
$this->sorted[$eventName][] = $listener;
}
}
}




private function optimizeListeners(string $eventName): array
{
krsort($this->listeners[$eventName]);
$this->optimized[$eventName] = [];

foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as &$listener) {
$closure = &$this->optimized[$eventName][];
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$closure = static function (...$args) use (&$listener, &$closure) {
if ($listener[0] instanceof \Closure) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
($closure = \Closure::fromCallable($listener))(...$args);
};
} else {
$closure = $listener instanceof \Closure || $listener instanceof WrappedListener ? $listener : \Closure::fromCallable($listener);
}
}
}

return $this->optimized[$eventName];
}
}
<?php










namespace Symfony\Component\EventDispatcher\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;






class AddEventAliasesPass implements CompilerPassInterface
{
private array $eventAliases;

public function __construct(array $eventAliases)
{
$this->eventAliases = $eventAliases;
}

public function process(ContainerBuilder $container): void
{
$eventAliases = $container->hasParameter('event_dispatcher.event_aliases') ? $container->getParameter('event_dispatcher.event_aliases') : [];

$container->setParameter(
'event_dispatcher.event_aliases',
array_merge($eventAliases, $this->eventAliases)
);
}
}
<?php










namespace Symfony\Component\EventDispatcher\DependencyInjection;

use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event;




class RegisterListenersPass implements CompilerPassInterface
{
private array $hotPathEvents = [];
private array $noPreloadEvents = [];




public function setHotPathEvents(array $hotPathEvents): static
{
$this->hotPathEvents = array_flip($hotPathEvents);

return $this;
}




public function setNoPreloadEvents(array $noPreloadEvents): static
{
$this->noPreloadEvents = array_flip($noPreloadEvents);

return $this;
}

public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('event_dispatcher') && !$container->hasAlias('event_dispatcher')) {
return;
}

$aliases = [];

if ($container->hasParameter('event_dispatcher.event_aliases')) {
$aliases = $container->getParameter('event_dispatcher.event_aliases');
}

$globalDispatcherDefinition = $container->findDefinition('event_dispatcher');

foreach ($container->findTaggedServiceIds('kernel.event_listener', true) as $id => $events) {
$noPreload = 0;

foreach ($events as $event) {
$priority = $event['priority'] ?? 0;

if (!isset($event['event'])) {
if ($container->getDefinition($id)->hasTag('kernel.event_subscriber')) {
continue;
}

$event['method'] = $event['method'] ?? '__invoke';
$event['event'] = $this->getEventFromTypeDeclaration($container, $id, $event['method']);
}

$event['event'] = $aliases[$event['event']] ?? $event['event'];

if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace_callback([
'/(?<=\b|_)[a-z]/i',
'/[^a-z0-9]/i',
], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);

if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) {
$event['method'] = '__invoke';
}
}

$dispatcherDefinition = $globalDispatcherDefinition;
if (isset($event['dispatcher'])) {
$dispatcherDefinition = $container->getDefinition($event['dispatcher']);
}

$dispatcherDefinition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);

if (isset($this->hotPathEvents[$event['event']])) {
$container->getDefinition($id)->addTag('container.hot_path');
} elseif (isset($this->noPreloadEvents[$event['event']])) {
++$noPreload;
}
}

if ($noPreload && \count($events) === $noPreload) {
$container->getDefinition($id)->addTag('container.no_preload');
}
}

$extractingDispatcher = new ExtractingEventDispatcher();

foreach ($container->findTaggedServiceIds('kernel.event_subscriber', true) as $id => $tags) {
$def = $container->getDefinition($id);


$class = $def->getClass();

if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class));
}
$class = $r->name;

$dispatcherDefinitions = [];
foreach ($tags as $attributes) {
if (!isset($attributes['dispatcher']) || isset($dispatcherDefinitions[$attributes['dispatcher']])) {
continue;
}

$dispatcherDefinitions[$attributes['dispatcher']] = $container->getDefinition($attributes['dispatcher']);
}

if (!$dispatcherDefinitions) {
$dispatcherDefinitions = [$globalDispatcherDefinition];
}

$noPreload = 0;
ExtractingEventDispatcher::$aliases = $aliases;
ExtractingEventDispatcher::$subscriber = $class;
$extractingDispatcher->addSubscriber($extractingDispatcher);
foreach ($extractingDispatcher->listeners as $args) {
$args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]];
foreach ($dispatcherDefinitions as $dispatcherDefinition) {
$dispatcherDefinition->addMethodCall('addListener', $args);
}

if (isset($this->hotPathEvents[$args[0]])) {
$container->getDefinition($id)->addTag('container.hot_path');
} elseif (isset($this->noPreloadEvents[$args[0]])) {
++$noPreload;
}
}
if ($noPreload && \count($extractingDispatcher->listeners) === $noPreload) {
$container->getDefinition($id)->addTag('container.no_preload');
}
$extractingDispatcher->listeners = [];
ExtractingEventDispatcher::$aliases = [];
}
}

private function getEventFromTypeDeclaration(ContainerBuilder $container, string $id, string $method): string
{
if (
null === ($class = $container->getDefinition($id)->getClass())
|| !($r = $container->getReflectionClass($class, false))
|| !$r->hasMethod($method)
|| 1 > ($m = $r->getMethod($method))->getNumberOfParameters()
|| !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
|| $type->isBuiltin()
|| Event::class === ($name = $type->getName())
) {
throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "kernel.event_listener" tags.', $id));
}

return $name;
}
}




class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
{
public array $listeners = [];

public static array $aliases = [];
public static string $subscriber;

public function addListener(string $eventName, callable|array $listener, int $priority = 0)
{
$this->listeners[] = [$eventName, $listener[1], $priority];
}

public static function getSubscribedEvents(): array
{
$events = [];

foreach ([self::$subscriber, 'getSubscribedEvents']() as $eventName => $params) {
$events[self::$aliases[$eventName] ?? $eventName] = $params;
}

return $events;
}
}
<?php










namespace Symfony\Component\EventDispatcher;












interface EventSubscriberInterface
{





















public static function getSubscribedEvents();
}
<?php










namespace Symfony\Component\EventDispatcher;






class ImmutableEventDispatcher implements EventDispatcherInterface
{
private $dispatcher;

public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}




public function dispatch(object $event, string $eventName = null): object
{
return $this->dispatcher->dispatch($event, $eventName);
}




public function addListener(string $eventName, callable|array $listener, int $priority = 0)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}




public function addSubscriber(EventSubscriberInterface $subscriber)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}




public function removeListener(string $eventName, callable|array $listener)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}




public function removeSubscriber(EventSubscriberInterface $subscriber)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}




public function getListeners(string $eventName = null): array
{
return $this->dispatcher->getListeners($eventName);
}




public function getListenerPriority(string $eventName, callable|array $listener): ?int
{
return $this->dispatcher->getListenerPriority($eventName, $listener);
}




public function hasListeners(string $eventName = null): bool
{
return $this->dispatcher->hasListeners($eventName);
}
}
<?php










namespace Symfony\Component\EventDispatcher;

use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;








interface EventDispatcherInterface extends ContractsEventDispatcherInterface
{






public function addListener(string $eventName, callable $listener, int $priority = 0);







public function addSubscriber(EventSubscriberInterface $subscriber);




public function removeListener(string $eventName, callable $listener);

public function removeSubscriber(EventSubscriberInterface $subscriber);






public function getListeners(string $eventName = null): array;






public function getListenerPriority(string $eventName, callable $listener): ?int;




public function hasListeners(string $eventName = null): bool;
}
<?php










namespace Symfony\Component\EventDispatcher\Debug;

use Psr\EventDispatcher\StoppableEventInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Caster\ClassStub;




final class WrappedListener
{
private string|array|object $listener;
private ?\Closure $optimizedListener;
private string $name;
private bool $called = false;
private bool $stoppedPropagation = false;
private $stopwatch;
private $dispatcher;
private string $pretty;
private $stub;
private ?int $priority = null;
private static bool $hasClassStub;

public function __construct(callable|array $listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
{
$this->listener = $listener;
$this->optimizedListener = $listener instanceof \Closure ? $listener : (\is_callable($listener) ? \Closure::fromCallable($listener) : null);
$this->stopwatch = $stopwatch;
$this->dispatcher = $dispatcher;

if (\is_array($listener)) {
$this->name = \is_object($listener[0]) ? get_debug_type($listener[0]) : $listener[0];
$this->pretty = $this->name.'::'.$listener[1];
} elseif ($listener instanceof \Closure) {
$r = new \ReflectionFunction($listener);
if (str_contains($r->name, '{closure}')) {
$this->pretty = $this->name = 'closure';
} elseif ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
$this->name = $class->name;
$this->pretty = $this->name.'::'.$r->name;
} else {
$this->pretty = $this->name = $r->name;
}
} elseif (\is_string($listener)) {
$this->pretty = $this->name = $listener;
} else {
$this->name = get_debug_type($listener);
$this->pretty = $this->name.'::__invoke';
}

if (null !== $name) {
$this->name = $name;
}

self::$hasClassStub ??= class_exists(ClassStub::class);
}

public function getWrappedListener(): callable|array
{
return $this->listener;
}

public function wasCalled(): bool
{
return $this->called;
}

public function stoppedPropagation(): bool
{
return $this->stoppedPropagation;
}

public function getPretty(): string
{
return $this->pretty;
}

public function getInfo(string $eventName): array
{
$this->stub ??= self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()';

return [
'event' => $eventName,
'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null),
'pretty' => $this->pretty,
'stub' => $this->stub,
];
}

public function __invoke(object $event, string $eventName, EventDispatcherInterface $dispatcher): void
{
$dispatcher = $this->dispatcher ?: $dispatcher;

$this->called = true;
$this->priority = $dispatcher->getListenerPriority($eventName, $this->listener);

$e = $this->stopwatch->start($this->name, 'event_listener');

($this->optimizedListener ?? $this->listener)($event, $eventName, $dispatcher);

if ($e->isStarted()) {
$e->stop();
}

if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
$this->stoppedPropagation = true;
}
}
}
<?php










namespace Symfony\Component\EventDispatcher\Debug;

use Psr\EventDispatcher\StoppableEventInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Contracts\Service\ResetInterface;








class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterface
{
protected $logger;
protected $stopwatch;




private ?\SplObjectStorage $callStack = null;
private $dispatcher;
private array $wrappedListeners = [];
private array $orphanedEvents = [];
private $requestStack;
private string $currentRequestHash = '';

public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null)
{
$this->dispatcher = $dispatcher;
$this->stopwatch = $stopwatch;
$this->logger = $logger;
$this->requestStack = $requestStack;
}




public function addListener(string $eventName, callable|array $listener, int $priority = 0)
{
$this->dispatcher->addListener($eventName, $listener, $priority);
}




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




public function removeListener(string $eventName, callable|array $listener)
{
if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
$listener = $wrappedListener;
unset($this->wrappedListeners[$eventName][$index]);
break;
}
}
}

return $this->dispatcher->removeListener($eventName, $listener);
}




public function removeSubscriber(EventSubscriberInterface $subscriber)
{
return $this->dispatcher->removeSubscriber($subscriber);
}




public function getListeners(string $eventName = null): array
{
return $this->dispatcher->getListeners($eventName);
}




public function getListenerPriority(string $eventName, callable|array $listener): ?int
{


if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
}
}
}

return $this->dispatcher->getListenerPriority($eventName, $listener);
}




public function hasListeners(string $eventName = null): bool
{
return $this->dispatcher->hasListeners($eventName);
}




public function dispatch(object $event, string $eventName = null): object
{
$eventName = $eventName ?? \get_class($event);

if (null === $this->callStack) {
$this->callStack = new \SplObjectStorage();
}

$currentRequestHash = $this->currentRequestHash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';

if (null !== $this->logger && $event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
$this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
}

$this->preProcess($eventName);
try {
$this->beforeDispatch($eventName, $event);
try {
$e = $this->stopwatch->start($eventName, 'section');
try {
$this->dispatcher->dispatch($event, $eventName);
} finally {
if ($e->isStarted()) {
$e->stop();
}
}
} finally {
$this->afterDispatch($eventName, $event);
}
} finally {
$this->currentRequestHash = $currentRequestHash;
$this->postProcess($eventName);
}

return $event;
}

public function getCalledListeners(Request $request = null): array
{
if (null === $this->callStack) {
return [];
}

$hash = $request ? spl_object_hash($request) : null;
$called = [];
foreach ($this->callStack as $listener) {
[$eventName, $requestHash] = $this->callStack->getInfo();
if (null === $hash || $hash === $requestHash) {
$called[] = $listener->getInfo($eventName);
}
}

return $called;
}

public function getNotCalledListeners(Request $request = null): array
{
try {
$allListeners = $this->getListeners();
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]);
}


return [];
}

$hash = $request ? spl_object_hash($request) : null;
$calledListeners = [];

if (null !== $this->callStack) {
foreach ($this->callStack as $calledListener) {
[, $requestHash] = $this->callStack->getInfo();

if (null === $hash || $hash === $requestHash) {
$calledListeners[] = $calledListener->getWrappedListener();
}
}
}

$notCalled = [];
foreach ($allListeners as $eventName => $listeners) {
foreach ($listeners as $listener) {
if (!\in_array($listener, $calledListeners, true)) {
if (!$listener instanceof WrappedListener) {
$listener = new WrappedListener($listener, null, $this->stopwatch, $this);
}
$notCalled[] = $listener->getInfo($eventName);
}
}
}

uasort($notCalled, [$this, 'sortNotCalledListeners']);

return $notCalled;
}

public function getOrphanedEvents(Request $request = null): array
{
if ($request) {
return $this->orphanedEvents[spl_object_hash($request)] ?? [];
}

if (!$this->orphanedEvents) {
return [];
}

return array_merge(...array_values($this->orphanedEvents));
}

public function reset()
{
$this->callStack = null;
$this->orphanedEvents = [];
$this->currentRequestHash = '';
}







public function __call(string $method, array $arguments): mixed
{
return $this->dispatcher->{$method}(...$arguments);
}




protected function beforeDispatch(string $eventName, object $event)
{
}




protected function afterDispatch(string $eventName, object $event)
{
}

private function preProcess(string $eventName): void
{
if (!$this->dispatcher->hasListeners($eventName)) {
$this->orphanedEvents[$this->currentRequestHash][] = $eventName;

return;
}

foreach ($this->dispatcher->getListeners($eventName) as $listener) {
$priority = $this->getListenerPriority($eventName, $listener);
$wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this);
$this->wrappedListeners[$eventName][] = $wrappedListener;
$this->dispatcher->removeListener($eventName, $listener);
$this->dispatcher->addListener($eventName, $wrappedListener, $priority);
$this->callStack->attach($wrappedListener, [$eventName, $this->currentRequestHash]);
}
}

private function postProcess(string $eventName): void
{
unset($this->wrappedListeners[$eventName]);
$skipped = false;
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
if (!$listener instanceof WrappedListener) { 
continue;
}

$priority = $this->getListenerPriority($eventName, $listener);
$this->dispatcher->removeListener($eventName, $listener);
$this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);

if (null !== $this->logger) {
$context = ['event' => $eventName, 'listener' => $listener->getPretty()];
}

if ($listener->wasCalled()) {
if (null !== $this->logger) {
$this->logger->debug('Notified event "{event}" to listener "{listener}".', $context);
}
} else {
$this->callStack->detach($listener);
}

if (null !== $this->logger && $skipped) {
$this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
}

if ($listener->stoppedPropagation()) {
if (null !== $this->logger) {
$this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context);
}

$skipped = true;
}
}
}

private function sortNotCalledListeners(array $a, array $b)
{
if (0 !== $cmp = strcmp($a['event'], $b['event'])) {
return $cmp;
}

if (\is_int($a['priority']) && !\is_int($b['priority'])) {
return 1;
}

if (!\is_int($a['priority']) && \is_int($b['priority'])) {
return -1;
}

if ($a['priority'] === $b['priority']) {
return 0;
}

if ($a['priority'] > $b['priority']) {
return -1;
}

return 1;
}
}
<?php










namespace Symfony\Component\EventDispatcher\Attribute;






#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsEventListener
{
public function __construct(
public ?string $event = null,
public ?string $method = null,
public int $priority = 0,
public ?string $dispatcher = null,
) {
}
}
<?php










if (!function_exists('trigger_deprecation')) {










function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void
{
@trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
}
}
<?php

class Normalizer extends Symfony\Polyfill\Intl\Normalizer\Normalizer
{



public const NONE = 2;
public const FORM_D = 4;
public const FORM_KD = 8;
public const FORM_C = 16;
public const FORM_KC = 32;
public const NFD = 4;
public const NFKD = 8;
public const NFC = 16;
public const NFKC = 32;
}
<?php

return array (
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'ÿ' => 'ÿ',
'Ā' => 'Ā',
'ā' => 'ā',
'Ă' => 'Ă',
'ă' => 'ă',
'Ą' => 'Ą',
'ą' => 'ą',
'Ć' => 'Ć',
'ć' => 'ć',
'Ĉ' => 'Ĉ',
'ĉ' => 'ĉ',
'Ċ' => 'Ċ',
'ċ' => 'ċ',
'Č' => 'Č',
'č' => 'č',
'Ď' => 'Ď',
'ď' => 'ď',
'Ē' => 'Ē',
'ē' => 'ē',
'Ĕ' => 'Ĕ',
'ĕ' => 'ĕ',
'Ė' => 'Ė',
'ė' => 'ė',
'Ę' => 'Ę',
'ę' => 'ę',
'Ě' => 'Ě',
'ě' => 'ě',
'Ĝ' => 'Ĝ',
'ĝ' => 'ĝ',
'Ğ' => 'Ğ',
'ğ' => 'ğ',
'Ġ' => 'Ġ',
'ġ' => 'ġ',
'Ģ' => 'Ģ',
'ģ' => 'ģ',
'Ĥ' => 'Ĥ',
'ĥ' => 'ĥ',
'Ĩ' => 'Ĩ',
'ĩ' => 'ĩ',
'Ī' => 'Ī',
'ī' => 'ī',
'Ĭ' => 'Ĭ',
'ĭ' => 'ĭ',
'Į' => 'Į',
'į' => 'į',
'İ' => 'İ',
'Ĵ' => 'Ĵ',
'ĵ' => 'ĵ',
'Ķ' => 'Ķ',
'ķ' => 'ķ',
'Ĺ' => 'Ĺ',
'ĺ' => 'ĺ',
'Ļ' => 'Ļ',
'ļ' => 'ļ',
'Ľ' => 'Ľ',
'ľ' => 'ľ',
'Ń' => 'Ń',
'ń' => 'ń',
'Ņ' => 'Ņ',
'ņ' => 'ņ',
'Ň' => 'Ň',
'ň' => 'ň',
'Ō' => 'Ō',
'ō' => 'ō',
'Ŏ' => 'Ŏ',
'ŏ' => 'ŏ',
'Ő' => 'Ő',
'ő' => 'ő',
'Ŕ' => 'Ŕ',
'ŕ' => 'ŕ',
'Ŗ' => 'Ŗ',
'ŗ' => 'ŗ',
'Ř' => 'Ř',
'ř' => 'ř',
'Ś' => 'Ś',
'ś' => 'ś',
'Ŝ' => 'Ŝ',
'ŝ' => 'ŝ',
'Ş' => 'Ş',
'ş' => 'ş',
'Š' => 'Š',
'š' => 'š',
'Ţ' => 'Ţ',
'ţ' => 'ţ',
'Ť' => 'Ť',
'ť' => 'ť',
'Ũ' => 'Ũ',
'ũ' => 'ũ',
'Ū' => 'Ū',
'ū' => 'ū',
'Ŭ' => 'Ŭ',
'ŭ' => 'ŭ',
'Ů' => 'Ů',
'ů' => 'ů',
'Ű' => 'Ű',
'ű' => 'ű',
'Ų' => 'Ų',
'ų' => 'ų',
'Ŵ' => 'Ŵ',
'ŵ' => 'ŵ',
'Ŷ' => 'Ŷ',
'ŷ' => 'ŷ',
'Ÿ' => 'Ÿ',
'Ź' => 'Ź',
'ź' => 'ź',
'Ż' => 'Ż',
'ż' => 'ż',
'Ž' => 'Ž',
'ž' => 'ž',
'Ơ' => 'Ơ',
'ơ' => 'ơ',
'Ư' => 'Ư',
'ư' => 'ư',
'Ǎ' => 'Ǎ',
'ǎ' => 'ǎ',
'Ǐ' => 'Ǐ',
'ǐ' => 'ǐ',
'Ǒ' => 'Ǒ',
'ǒ' => 'ǒ',
'Ǔ' => 'Ǔ',
'ǔ' => 'ǔ',
'Ǖ' => 'Ǖ',
'ǖ' => 'ǖ',
'Ǘ' => 'Ǘ',
'ǘ' => 'ǘ',
'Ǚ' => 'Ǚ',
'ǚ' => 'ǚ',
'Ǜ' => 'Ǜ',
'ǜ' => 'ǜ',
'Ǟ' => 'Ǟ',
'ǟ' => 'ǟ',
'Ǡ' => 'Ǡ',
'ǡ' => 'ǡ',
'Ǣ' => 'Ǣ',
'ǣ' => 'ǣ',
'Ǧ' => 'Ǧ',
'ǧ' => 'ǧ',
'Ǩ' => 'Ǩ',
'ǩ' => 'ǩ',
'Ǫ' => 'Ǫ',
'ǫ' => 'ǫ',
'Ǭ' => 'Ǭ',
'ǭ' => 'ǭ',
'Ǯ' => 'Ǯ',
'ǯ' => 'ǯ',
'ǰ' => 'ǰ',
'Ǵ' => 'Ǵ',
'ǵ' => 'ǵ',
'Ǹ' => 'Ǹ',
'ǹ' => 'ǹ',
'Ǻ' => 'Ǻ',
'ǻ' => 'ǻ',
'Ǽ' => 'Ǽ',
'ǽ' => 'ǽ',
'Ǿ' => 'Ǿ',
'ǿ' => 'ǿ',
'Ȁ' => 'Ȁ',
'ȁ' => 'ȁ',
'Ȃ' => 'Ȃ',
'ȃ' => 'ȃ',
'Ȅ' => 'Ȅ',
'ȅ' => 'ȅ',
'Ȇ' => 'Ȇ',
'ȇ' => 'ȇ',
'Ȉ' => 'Ȉ',
'ȉ' => 'ȉ',
'Ȋ' => 'Ȋ',
'ȋ' => 'ȋ',
'Ȍ' => 'Ȍ',
'ȍ' => 'ȍ',
'Ȏ' => 'Ȏ',
'ȏ' => 'ȏ',
'Ȑ' => 'Ȑ',
'ȑ' => 'ȑ',
'Ȓ' => 'Ȓ',
'ȓ' => 'ȓ',
'Ȕ' => 'Ȕ',
'ȕ' => 'ȕ',
'Ȗ' => 'Ȗ',
'ȗ' => 'ȗ',
'Ș' => 'Ș',
'ș' => 'ș',
'Ț' => 'Ț',
'ț' => 'ț',
'Ȟ' => 'Ȟ',
'ȟ' => 'ȟ',
'Ȧ' => 'Ȧ',
'ȧ' => 'ȧ',
'Ȩ' => 'Ȩ',
'ȩ' => 'ȩ',
'Ȫ' => 'Ȫ',
'ȫ' => 'ȫ',
'Ȭ' => 'Ȭ',
'ȭ' => 'ȭ',
'Ȯ' => 'Ȯ',
'ȯ' => 'ȯ',
'Ȱ' => 'Ȱ',
'ȱ' => 'ȱ',
'Ȳ' => 'Ȳ',
'ȳ' => 'ȳ',
'̀' => '̀',
'́' => '́',
'̓' => '̓',
'̈́' => '̈́',
'ʹ' => 'ʹ',
';' => ';',
'΅' => '΅',
'Ά' => 'Ά',
'·' => '·',
'Έ' => 'Έ',
'Ή' => 'Ή',
'Ί' => 'Ί',
'Ό' => 'Ό',
'Ύ' => 'Ύ',
'Ώ' => 'Ώ',
'ΐ' => 'ΐ',
'Ϊ' => 'Ϊ',
'Ϋ' => 'Ϋ',
'ά' => 'ά',
'έ' => 'έ',
'ή' => 'ή',
'ί' => 'ί',
'ΰ' => 'ΰ',
'ϊ' => 'ϊ',
'ϋ' => 'ϋ',
'ό' => 'ό',
'ύ' => 'ύ',
'ώ' => 'ώ',
'ϓ' => 'ϓ',
'ϔ' => 'ϔ',
'Ѐ' => 'Ѐ',
'Ё' => 'Ё',
'Ѓ' => 'Ѓ',
'Ї' => 'Ї',
'Ќ' => 'Ќ',
'Ѝ' => 'Ѝ',
'Ў' => 'Ў',
'Й' => 'Й',
'й' => 'й',
'ѐ' => 'ѐ',
'ё' => 'ё',
'ѓ' => 'ѓ',
'ї' => 'ї',
'ќ' => 'ќ',
'ѝ' => 'ѝ',
'ў' => 'ў',
'Ѷ' => 'Ѷ',
'ѷ' => 'ѷ',
'Ӂ' => 'Ӂ',
'ӂ' => 'ӂ',
'Ӑ' => 'Ӑ',
'ӑ' => 'ӑ',
'Ӓ' => 'Ӓ',
'ӓ' => 'ӓ',
'Ӗ' => 'Ӗ',
'ӗ' => 'ӗ',
'Ӛ' => 'Ӛ',
'ӛ' => 'ӛ',
'Ӝ' => 'Ӝ',
'ӝ' => 'ӝ',
'Ӟ' => 'Ӟ',
'ӟ' => 'ӟ',
'Ӣ' => 'Ӣ',
'ӣ' => 'ӣ',
'Ӥ' => 'Ӥ',
'ӥ' => 'ӥ',
'Ӧ' => 'Ӧ',
'ӧ' => 'ӧ',
'Ӫ' => 'Ӫ',
'ӫ' => 'ӫ',
'Ӭ' => 'Ӭ',
'ӭ' => 'ӭ',
'Ӯ' => 'Ӯ',
'ӯ' => 'ӯ',
'Ӱ' => 'Ӱ',
'ӱ' => 'ӱ',
'Ӳ' => 'Ӳ',
'ӳ' => 'ӳ',
'Ӵ' => 'Ӵ',
'ӵ' => 'ӵ',
'Ӹ' => 'Ӹ',
'ӹ' => 'ӹ',
'آ' => 'آ',
'أ' => 'أ',
'ؤ' => 'ؤ',
'إ' => 'إ',
'ئ' => 'ئ',
'ۀ' => 'ۀ',
'ۂ' => 'ۂ',
'ۓ' => 'ۓ',
'ऩ' => 'ऩ',
'ऱ' => 'ऱ',
'ऴ' => 'ऴ',
'क़' => 'क़',
'ख़' => 'ख़',
'ग़' => 'ग़',
'ज़' => 'ज़',
'ड़' => 'ड़',
'ढ़' => 'ढ़',
'फ़' => 'फ़',
'य़' => 'य़',
'ো' => 'ো',
'ৌ' => 'ৌ',
'ড়' => 'ড়',
'ঢ়' => 'ঢ়',
'য়' => 'য়',
'ਲ਼' => 'ਲ਼',
'ਸ਼' => 'ਸ਼',
'ਖ਼' => 'ਖ਼',
'ਗ਼' => 'ਗ਼',
'ਜ਼' => 'ਜ਼',
'ਫ਼' => 'ਫ਼',
'ୈ' => 'ୈ',
'ୋ' => 'ୋ',
'ୌ' => 'ୌ',
'ଡ଼' => 'ଡ଼',
'ଢ଼' => 'ଢ଼',
'ஔ' => 'ஔ',
'ொ' => 'ொ',
'ோ' => 'ோ',
'ௌ' => 'ௌ',
'ై' => 'ై',
'ೀ' => 'ೀ',
'ೇ' => 'ೇ',
'ೈ' => 'ೈ',
'ೊ' => 'ೊ',
'ೋ' => 'ೋ',
'ൊ' => 'ൊ',
'ോ' => 'ോ',
'ൌ' => 'ൌ',
'ේ' => 'ේ',
'ො' => 'ො',
'ෝ' => 'ෝ',
'ෞ' => 'ෞ',
'གྷ' => 'གྷ',
'ཌྷ' => 'ཌྷ',
'དྷ' => 'དྷ',
'བྷ' => 'བྷ',
'ཛྷ' => 'ཛྷ',
'ཀྵ' => 'ཀྵ',
'ཱི' => 'ཱི',
'ཱུ' => 'ཱུ',
'ྲྀ' => 'ྲྀ',
'ླྀ' => 'ླྀ',
'ཱྀ' => 'ཱྀ',
'ྒྷ' => 'ྒྷ',
'ྜྷ' => 'ྜྷ',
'ྡྷ' => 'ྡྷ',
'ྦྷ' => 'ྦྷ',
'ྫྷ' => 'ྫྷ',
'ྐྵ' => 'ྐྵ',
'ဦ' => 'ဦ',
'ᬆ' => 'ᬆ',
'ᬈ' => 'ᬈ',
'ᬊ' => 'ᬊ',
'ᬌ' => 'ᬌ',
'ᬎ' => 'ᬎ',
'ᬒ' => 'ᬒ',
'ᬻ' => 'ᬻ',
'ᬽ' => 'ᬽ',
'ᭀ' => 'ᭀ',
'ᭁ' => 'ᭁ',
'ᭃ' => 'ᭃ',
'Ḁ' => 'Ḁ',
'ḁ' => 'ḁ',
'Ḃ' => 'Ḃ',
'ḃ' => 'ḃ',
'Ḅ' => 'Ḅ',
'ḅ' => 'ḅ',
'Ḇ' => 'Ḇ',
'ḇ' => 'ḇ',
'Ḉ' => 'Ḉ',
'ḉ' => 'ḉ',
'Ḋ' => 'Ḋ',
'ḋ' => 'ḋ',
'Ḍ' => 'Ḍ',
'ḍ' => 'ḍ',
'Ḏ' => 'Ḏ',
'ḏ' => 'ḏ',
'Ḑ' => 'Ḑ',
'ḑ' => 'ḑ',
'Ḓ' => 'Ḓ',
'ḓ' => 'ḓ',
'Ḕ' => 'Ḕ',
'ḕ' => 'ḕ',
'Ḗ' => 'Ḗ',
'ḗ' => 'ḗ',
'Ḙ' => 'Ḙ',
'ḙ' => 'ḙ',
'Ḛ' => 'Ḛ',
'ḛ' => 'ḛ',
'Ḝ' => 'Ḝ',
'ḝ' => 'ḝ',
'Ḟ' => 'Ḟ',
'ḟ' => 'ḟ',
'Ḡ' => 'Ḡ',
'ḡ' => 'ḡ',
'Ḣ' => 'Ḣ',
'ḣ' => 'ḣ',
'Ḥ' => 'Ḥ',
'ḥ' => 'ḥ',
'Ḧ' => 'Ḧ',
'ḧ' => 'ḧ',
'Ḩ' => 'Ḩ',
'ḩ' => 'ḩ',
'Ḫ' => 'Ḫ',
'ḫ' => 'ḫ',
'Ḭ' => 'Ḭ',
'ḭ' => 'ḭ',
'Ḯ' => 'Ḯ',
'ḯ' => 'ḯ',
'Ḱ' => 'Ḱ',
'ḱ' => 'ḱ',
'Ḳ' => 'Ḳ',
'ḳ' => 'ḳ',
'Ḵ' => 'Ḵ',
'ḵ' => 'ḵ',
'Ḷ' => 'Ḷ',
'ḷ' => 'ḷ',
'Ḹ' => 'Ḹ',
'ḹ' => 'ḹ',
'Ḻ' => 'Ḻ',
'ḻ' => 'ḻ',
'Ḽ' => 'Ḽ',
'ḽ' => 'ḽ',
'Ḿ' => 'Ḿ',
'ḿ' => 'ḿ',
'Ṁ' => 'Ṁ',
'ṁ' => 'ṁ',
'Ṃ' => 'Ṃ',
'ṃ' => 'ṃ',
'Ṅ' => 'Ṅ',
'ṅ' => 'ṅ',
'Ṇ' => 'Ṇ',
'ṇ' => 'ṇ',
'Ṉ' => 'Ṉ',
'ṉ' => 'ṉ',
'Ṋ' => 'Ṋ',
'ṋ' => 'ṋ',
'Ṍ' => 'Ṍ',
'ṍ' => 'ṍ',
'Ṏ' => 'Ṏ',
'ṏ' => 'ṏ',
'Ṑ' => 'Ṑ',
'ṑ' => 'ṑ',
'Ṓ' => 'Ṓ',
'ṓ' => 'ṓ',
'Ṕ' => 'Ṕ',
'ṕ' => 'ṕ',
'Ṗ' => 'Ṗ',
'ṗ' => 'ṗ',
'Ṙ' => 'Ṙ',
'ṙ' => 'ṙ',
'Ṛ' => 'Ṛ',
'ṛ' => 'ṛ',
'Ṝ' => 'Ṝ',
'ṝ' => 'ṝ',
'Ṟ' => 'Ṟ',
'ṟ' => 'ṟ',
'Ṡ' => 'Ṡ',
'ṡ' => 'ṡ',
'Ṣ' => 'Ṣ',
'ṣ' => 'ṣ',
'Ṥ' => 'Ṥ',
'ṥ' => 'ṥ',
'Ṧ' => 'Ṧ',
'ṧ' => 'ṧ',
'Ṩ' => 'Ṩ',
'ṩ' => 'ṩ',
'Ṫ' => 'Ṫ',
'ṫ' => 'ṫ',
'Ṭ' => 'Ṭ',
'ṭ' => 'ṭ',
'Ṯ' => 'Ṯ',
'ṯ' => 'ṯ',
'Ṱ' => 'Ṱ',
'ṱ' => 'ṱ',
'Ṳ' => 'Ṳ',
'ṳ' => 'ṳ',
'Ṵ' => 'Ṵ',
'ṵ' => 'ṵ',
'Ṷ' => 'Ṷ',
'ṷ' => 'ṷ',
'Ṹ' => 'Ṹ',
'ṹ' => 'ṹ',
'Ṻ' => 'Ṻ',
'ṻ' => 'ṻ',
'Ṽ' => 'Ṽ',
'ṽ' => 'ṽ',
'Ṿ' => 'Ṿ',
'ṿ' => 'ṿ',
'Ẁ' => 'Ẁ',
'ẁ' => 'ẁ',
'Ẃ' => 'Ẃ',
'ẃ' => 'ẃ',
'Ẅ' => 'Ẅ',
'ẅ' => 'ẅ',
'Ẇ' => 'Ẇ',
'ẇ' => 'ẇ',
'Ẉ' => 'Ẉ',
'ẉ' => 'ẉ',
'Ẋ' => 'Ẋ',
'ẋ' => 'ẋ',
'Ẍ' => 'Ẍ',
'ẍ' => 'ẍ',
'Ẏ' => 'Ẏ',
'ẏ' => 'ẏ',
'Ẑ' => 'Ẑ',
'ẑ' => 'ẑ',
'Ẓ' => 'Ẓ',
'ẓ' => 'ẓ',
'Ẕ' => 'Ẕ',
'ẕ' => 'ẕ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẛ' => 'ẛ',
'Ạ' => 'Ạ',
'ạ' => 'ạ',
'Ả' => 'Ả',
'ả' => 'ả',
'Ấ' => 'Ấ',
'ấ' => 'ấ',
'Ầ' => 'Ầ',
'ầ' => 'ầ',
'Ẩ' => 'Ẩ',
'ẩ' => 'ẩ',
'Ẫ' => 'Ẫ',
'ẫ' => 'ẫ',
'Ậ' => 'Ậ',
'ậ' => 'ậ',
'Ắ' => 'Ắ',
'ắ' => 'ắ',
'Ằ' => 'Ằ',
'ằ' => 'ằ',
'Ẳ' => 'Ẳ',
'ẳ' => 'ẳ',
'Ẵ' => 'Ẵ',
'ẵ' => 'ẵ',
'Ặ' => 'Ặ',
'ặ' => 'ặ',
'Ẹ' => 'Ẹ',
'ẹ' => 'ẹ',
'Ẻ' => 'Ẻ',
'ẻ' => 'ẻ',
'Ẽ' => 'Ẽ',
'ẽ' => 'ẽ',
'Ế' => 'Ế',
'ế' => 'ế',
'Ề' => 'Ề',
'ề' => 'ề',
'Ể' => 'Ể',
'ể' => 'ể',
'Ễ' => 'Ễ',
'ễ' => 'ễ',
'Ệ' => 'Ệ',
'ệ' => 'ệ',
'Ỉ' => 'Ỉ',
'ỉ' => 'ỉ',
'Ị' => 'Ị',
'ị' => 'ị',
'Ọ' => 'Ọ',
'ọ' => 'ọ',
'Ỏ' => 'Ỏ',
'ỏ' => 'ỏ',
'Ố' => 'Ố',
'ố' => 'ố',
'Ồ' => 'Ồ',
'ồ' => 'ồ',
'Ổ' => 'Ổ',
'ổ' => 'ổ',
'Ỗ' => 'Ỗ',
'ỗ' => 'ỗ',
'Ộ' => 'Ộ',
'ộ' => 'ộ',
'Ớ' => 'Ớ',
'ớ' => 'ớ',
'Ờ' => 'Ờ',
'ờ' => 'ờ',
'Ở' => 'Ở',
'ở' => 'ở',
'Ỡ' => 'Ỡ',
'ỡ' => 'ỡ',
'Ợ' => 'Ợ',
'ợ' => 'ợ',
'Ụ' => 'Ụ',
'ụ' => 'ụ',
'Ủ' => 'Ủ',
'ủ' => 'ủ',
'Ứ' => 'Ứ',
'ứ' => 'ứ',
'Ừ' => 'Ừ',
'ừ' => 'ừ',
'Ử' => 'Ử',
'ử' => 'ử',
'Ữ' => 'Ữ',
'ữ' => 'ữ',
'Ự' => 'Ự',
'ự' => 'ự',
'Ỳ' => 'Ỳ',
'ỳ' => 'ỳ',
'Ỵ' => 'Ỵ',
'ỵ' => 'ỵ',
'Ỷ' => 'Ỷ',
'ỷ' => 'ỷ',
'Ỹ' => 'Ỹ',
'ỹ' => 'ỹ',
'ἀ' => 'ἀ',
'ἁ' => 'ἁ',
'ἂ' => 'ἂ',
'ἃ' => 'ἃ',
'ἄ' => 'ἄ',
'ἅ' => 'ἅ',
'ἆ' => 'ἆ',
'ἇ' => 'ἇ',
'Ἀ' => 'Ἀ',
'Ἁ' => 'Ἁ',
'Ἂ' => 'Ἂ',
'Ἃ' => 'Ἃ',
'Ἄ' => 'Ἄ',
'Ἅ' => 'Ἅ',
'Ἆ' => 'Ἆ',
'Ἇ' => 'Ἇ',
'ἐ' => 'ἐ',
'ἑ' => 'ἑ',
'ἒ' => 'ἒ',
'ἓ' => 'ἓ',
'ἔ' => 'ἔ',
'ἕ' => 'ἕ',
'Ἐ' => 'Ἐ',
'Ἑ' => 'Ἑ',
'Ἒ' => 'Ἒ',
'Ἓ' => 'Ἓ',
'Ἔ' => 'Ἔ',
'Ἕ' => 'Ἕ',
'ἠ' => 'ἠ',
'ἡ' => 'ἡ',
'ἢ' => 'ἢ',
'ἣ' => 'ἣ',
'ἤ' => 'ἤ',
'ἥ' => 'ἥ',
'ἦ' => 'ἦ',
'ἧ' => 'ἧ',
'Ἠ' => 'Ἠ',
'Ἡ' => 'Ἡ',
'Ἢ' => 'Ἢ',
'Ἣ' => 'Ἣ',
'Ἤ' => 'Ἤ',
'Ἥ' => 'Ἥ',
'Ἦ' => 'Ἦ',
'Ἧ' => 'Ἧ',
'ἰ' => 'ἰ',
'ἱ' => 'ἱ',
'ἲ' => 'ἲ',
'ἳ' => 'ἳ',
'ἴ' => 'ἴ',
'ἵ' => 'ἵ',
'ἶ' => 'ἶ',
'ἷ' => 'ἷ',
'Ἰ' => 'Ἰ',
'Ἱ' => 'Ἱ',
'Ἲ' => 'Ἲ',
'Ἳ' => 'Ἳ',
'Ἴ' => 'Ἴ',
'Ἵ' => 'Ἵ',
'Ἶ' => 'Ἶ',
'Ἷ' => 'Ἷ',
'ὀ' => 'ὀ',
'ὁ' => 'ὁ',
'ὂ' => 'ὂ',
'ὃ' => 'ὃ',
'ὄ' => 'ὄ',
'ὅ' => 'ὅ',
'Ὀ' => 'Ὀ',
'Ὁ' => 'Ὁ',
'Ὂ' => 'Ὂ',
'Ὃ' => 'Ὃ',
'Ὄ' => 'Ὄ',
'Ὅ' => 'Ὅ',
'ὐ' => 'ὐ',
'ὑ' => 'ὑ',
'ὒ' => 'ὒ',
'ὓ' => 'ὓ',
'ὔ' => 'ὔ',
'ὕ' => 'ὕ',
'ὖ' => 'ὖ',
'ὗ' => 'ὗ',
'Ὑ' => 'Ὑ',
'Ὓ' => 'Ὓ',
'Ὕ' => 'Ὕ',
'Ὗ' => 'Ὗ',
'ὠ' => 'ὠ',
'ὡ' => 'ὡ',
'ὢ' => 'ὢ',
'ὣ' => 'ὣ',
'ὤ' => 'ὤ',
'ὥ' => 'ὥ',
'ὦ' => 'ὦ',
'ὧ' => 'ὧ',
'Ὠ' => 'Ὠ',
'Ὡ' => 'Ὡ',
'Ὢ' => 'Ὢ',
'Ὣ' => 'Ὣ',
'Ὤ' => 'Ὤ',
'Ὥ' => 'Ὥ',
'Ὦ' => 'Ὦ',
'Ὧ' => 'Ὧ',
'ὰ' => 'ὰ',
'ά' => 'ά',
'ὲ' => 'ὲ',
'έ' => 'έ',
'ὴ' => 'ὴ',
'ή' => 'ή',
'ὶ' => 'ὶ',
'ί' => 'ί',
'ὸ' => 'ὸ',
'ό' => 'ό',
'ὺ' => 'ὺ',
'ύ' => 'ύ',
'ὼ' => 'ὼ',
'ώ' => 'ώ',
'ᾀ' => 'ᾀ',
'ᾁ' => 'ᾁ',
'ᾂ' => 'ᾂ',
'ᾃ' => 'ᾃ',
'ᾄ' => 'ᾄ',
'ᾅ' => 'ᾅ',
'ᾆ' => 'ᾆ',
'ᾇ' => 'ᾇ',
'ᾈ' => 'ᾈ',
'ᾉ' => 'ᾉ',
'ᾊ' => 'ᾊ',
'ᾋ' => 'ᾋ',
'ᾌ' => 'ᾌ',
'ᾍ' => 'ᾍ',
'ᾎ' => 'ᾎ',
'ᾏ' => 'ᾏ',
'ᾐ' => 'ᾐ',
'ᾑ' => 'ᾑ',
'ᾒ' => 'ᾒ',
'ᾓ' => 'ᾓ',
'ᾔ' => 'ᾔ',
'ᾕ' => 'ᾕ',
'ᾖ' => 'ᾖ',
'ᾗ' => 'ᾗ',
'ᾘ' => 'ᾘ',
'ᾙ' => 'ᾙ',
'ᾚ' => 'ᾚ',
'ᾛ' => 'ᾛ',
'ᾜ' => 'ᾜ',
'ᾝ' => 'ᾝ',
'ᾞ' => 'ᾞ',
'ᾟ' => 'ᾟ',
'ᾠ' => 'ᾠ',
'ᾡ' => 'ᾡ',
'ᾢ' => 'ᾢ',
'ᾣ' => 'ᾣ',
'ᾤ' => 'ᾤ',
'ᾥ' => 'ᾥ',
'ᾦ' => 'ᾦ',
'ᾧ' => 'ᾧ',
'ᾨ' => 'ᾨ',
'ᾩ' => 'ᾩ',
'ᾪ' => 'ᾪ',
'ᾫ' => 'ᾫ',
'ᾬ' => 'ᾬ',
'ᾭ' => 'ᾭ',
'ᾮ' => 'ᾮ',
'ᾯ' => 'ᾯ',
'ᾰ' => 'ᾰ',
'ᾱ' => 'ᾱ',
'ᾲ' => 'ᾲ',
'ᾳ' => 'ᾳ',
'ᾴ' => 'ᾴ',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾷ',
'Ᾰ' => 'Ᾰ',
'Ᾱ' => 'Ᾱ',
'Ὰ' => 'Ὰ',
'Ά' => 'Ά',
'ᾼ' => 'ᾼ',
'ι' => 'ι',
'῁' => '῁',
'ῂ' => 'ῂ',
'ῃ' => 'ῃ',
'ῄ' => 'ῄ',
'ῆ' => 'ῆ',
'ῇ' => 'ῇ',
'Ὲ' => 'Ὲ',
'Έ' => 'Έ',
'Ὴ' => 'Ὴ',
'Ή' => 'Ή',
'ῌ' => 'ῌ',
'῍' => '῍',
'῎' => '῎',
'῏' => '῏',
'ῐ' => 'ῐ',
'ῑ' => 'ῑ',
'ῒ' => 'ῒ',
'ΐ' => 'ΐ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'Ῐ' => 'Ῐ',
'Ῑ' => 'Ῑ',
'Ὶ' => 'Ὶ',
'Ί' => 'Ί',
'῝' => '῝',
'῞' => '῞',
'῟' => '῟',
'ῠ' => 'ῠ',
'ῡ' => 'ῡ',
'ῢ' => 'ῢ',
'ΰ' => 'ΰ',
'ῤ' => 'ῤ',
'ῥ' => 'ῥ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'Ῠ' => 'Ῠ',
'Ῡ' => 'Ῡ',
'Ὺ' => 'Ὺ',
'Ύ' => 'Ύ',
'Ῥ' => 'Ῥ',
'῭' => '῭',
'΅' => '΅',
'`' => '`',
'ῲ' => 'ῲ',
'ῳ' => 'ῳ',
'ῴ' => 'ῴ',
'ῶ' => 'ῶ',
'ῷ' => 'ῷ',
'Ὸ' => 'Ὸ',
'Ό' => 'Ό',
'Ὼ' => 'Ὼ',
'Ώ' => 'Ώ',
'ῼ' => 'ῼ',
'´' => '´',
' ' => ' ',
' ' => ' ',
'Ω' => 'Ω',
'K' => 'K',
'Å' => 'Å',
'↚' => '↚',
'↛' => '↛',
'↮' => '↮',
'⇍' => '⇍',
'⇎' => '⇎',
'⇏' => '⇏',
'∄' => '∄',
'∉' => '∉',
'∌' => '∌',
'∤' => '∤',
'∦' => '∦',
'≁' => '≁',
'≄' => '≄',
'≇' => '≇',
'≉' => '≉',
'≠' => '≠',
'≢' => '≢',
'≭' => '≭',
'≮' => '≮',
'≯' => '≯',
'≰' => '≰',
'≱' => '≱',
'≴' => '≴',
'≵' => '≵',
'≸' => '≸',
'≹' => '≹',
'⊀' => '⊀',
'⊁' => '⊁',
'⊄' => '⊄',
'⊅' => '⊅',
'⊈' => '⊈',
'⊉' => '⊉',
'⊬' => '⊬',
'⊭' => '⊭',
'⊮' => '⊮',
'⊯' => '⊯',
'⋠' => '⋠',
'⋡' => '⋡',
'⋢' => '⋢',
'⋣' => '⋣',
'⋪' => '⋪',
'⋫' => '⋫',
'⋬' => '⋬',
'⋭' => '⋭',
'〈' => '〈',
'〉' => '〉',
'⫝̸' => '⫝̸',
'が' => 'が',
'ぎ' => 'ぎ',
'ぐ' => 'ぐ',
'げ' => 'げ',
'ご' => 'ご',
'ざ' => 'ざ',
'じ' => 'じ',
'ず' => 'ず',
'ぜ' => 'ぜ',
'ぞ' => 'ぞ',
'だ' => 'だ',
'ぢ' => 'ぢ',
'づ' => 'づ',
'で' => 'で',
'ど' => 'ど',
'ば' => 'ば',
'ぱ' => 'ぱ',
'び' => 'び',
'ぴ' => 'ぴ',
'ぶ' => 'ぶ',
'ぷ' => 'ぷ',
'べ' => 'べ',
'ぺ' => 'ぺ',
'ぼ' => 'ぼ',
'ぽ' => 'ぽ',
'ゔ' => 'ゔ',
'ゞ' => 'ゞ',
'ガ' => 'ガ',
'ギ' => 'ギ',
'グ' => 'グ',
'ゲ' => 'ゲ',
'ゴ' => 'ゴ',
'ザ' => 'ザ',
'ジ' => 'ジ',
'ズ' => 'ズ',
'ゼ' => 'ゼ',
'ゾ' => 'ゾ',
'ダ' => 'ダ',
'ヂ' => 'ヂ',
'ヅ' => 'ヅ',
'デ' => 'デ',
'ド' => 'ド',
'バ' => 'バ',
'パ' => 'パ',
'ビ' => 'ビ',
'ピ' => 'ピ',
'ブ' => 'ブ',
'プ' => 'プ',
'ベ' => 'ベ',
'ペ' => 'ペ',
'ボ' => 'ボ',
'ポ' => 'ポ',
'ヴ' => 'ヴ',
'ヷ' => 'ヷ',
'ヸ' => 'ヸ',
'ヹ' => 'ヹ',
'ヺ' => 'ヺ',
'ヾ' => 'ヾ',
'豈' => '豈',
'更' => '更',
'車' => '車',
'賈' => '賈',
'滑' => '滑',
'串' => '串',
'句' => '句',
'龜' => '龜',
'龜' => '龜',
'契' => '契',
'金' => '金',
'喇' => '喇',
'奈' => '奈',
'懶' => '懶',
'癩' => '癩',
'羅' => '羅',
'蘿' => '蘿',
'螺' => '螺',
'裸' => '裸',
'邏' => '邏',
'樂' => '樂',
'洛' => '洛',
'烙' => '烙',
'珞' => '珞',
'落' => '落',
'酪' => '酪',
'駱' => '駱',
'亂' => '亂',
'卵' => '卵',
'欄' => '欄',
'爛' => '爛',
'蘭' => '蘭',
'鸞' => '鸞',
'嵐' => '嵐',
'濫' => '濫',
'藍' => '藍',
'襤' => '襤',
'拉' => '拉',
'臘' => '臘',
'蠟' => '蠟',
'廊' => '廊',
'朗' => '朗',
'浪' => '浪',
'狼' => '狼',
'郎' => '郎',
'來' => '來',
'冷' => '冷',
'勞' => '勞',
'擄' => '擄',
'櫓' => '櫓',
'爐' => '爐',
'盧' => '盧',
'老' => '老',
'蘆' => '蘆',
'虜' => '虜',
'路' => '路',
'露' => '露',
'魯' => '魯',
'鷺' => '鷺',
'碌' => '碌',
'祿' => '祿',
'綠' => '綠',
'菉' => '菉',
'錄' => '錄',
'鹿' => '鹿',
'論' => '論',
'壟' => '壟',
'弄' => '弄',
'籠' => '籠',
'聾' => '聾',
'牢' => '牢',
'磊' => '磊',
'賂' => '賂',
'雷' => '雷',
'壘' => '壘',
'屢' => '屢',
'樓' => '樓',
'淚' => '淚',
'漏' => '漏',
'累' => '累',
'縷' => '縷',
'陋' => '陋',
'勒' => '勒',
'肋' => '肋',
'凜' => '凜',
'凌' => '凌',
'稜' => '稜',
'綾' => '綾',
'菱' => '菱',
'陵' => '陵',
'讀' => '讀',
'拏' => '拏',
'樂' => '樂',
'諾' => '諾',
'丹' => '丹',
'寧' => '寧',
'怒' => '怒',
'率' => '率',
'異' => '異',
'北' => '北',
'磻' => '磻',
'便' => '便',
'復' => '復',
'不' => '不',
'泌' => '泌',
'數' => '數',
'索' => '索',
'參' => '參',
'塞' => '塞',
'省' => '省',
'葉' => '葉',
'說' => '說',
'殺' => '殺',
'辰' => '辰',
'沈' => '沈',
'拾' => '拾',
'若' => '若',
'掠' => '掠',
'略' => '略',
'亮' => '亮',
'兩' => '兩',
'凉' => '凉',
'梁' => '梁',
'糧' => '糧',
'良' => '良',
'諒' => '諒',
'量' => '量',
'勵' => '勵',
'呂' => '呂',
'女' => '女',
'廬' => '廬',
'旅' => '旅',
'濾' => '濾',
'礪' => '礪',
'閭' => '閭',
'驪' => '驪',
'麗' => '麗',
'黎' => '黎',
'力' => '力',
'曆' => '曆',
'歷' => '歷',
'轢' => '轢',
'年' => '年',
'憐' => '憐',
'戀' => '戀',
'撚' => '撚',
'漣' => '漣',
'煉' => '煉',
'璉' => '璉',
'秊' => '秊',
'練' => '練',
'聯' => '聯',
'輦' => '輦',
'蓮' => '蓮',
'連' => '連',
'鍊' => '鍊',
'列' => '列',
'劣' => '劣',
'咽' => '咽',
'烈' => '烈',
'裂' => '裂',
'說' => '說',
'廉' => '廉',
'念' => '念',
'捻' => '捻',
'殮' => '殮',
'簾' => '簾',
'獵' => '獵',
'令' => '令',
'囹' => '囹',
'寧' => '寧',
'嶺' => '嶺',
'怜' => '怜',
'玲' => '玲',
'瑩' => '瑩',
'羚' => '羚',
'聆' => '聆',
'鈴' => '鈴',
'零' => '零',
'靈' => '靈',
'領' => '領',
'例' => '例',
'禮' => '禮',
'醴' => '醴',
'隸' => '隸',
'惡' => '惡',
'了' => '了',
'僚' => '僚',
'寮' => '寮',
'尿' => '尿',
'料' => '料',
'樂' => '樂',
'燎' => '燎',
'療' => '療',
'蓼' => '蓼',
'遼' => '遼',
'龍' => '龍',
'暈' => '暈',
'阮' => '阮',
'劉' => '劉',
'杻' => '杻',
'柳' => '柳',
'流' => '流',
'溜' => '溜',
'琉' => '琉',
'留' => '留',
'硫' => '硫',
'紐' => '紐',
'類' => '類',
'六' => '六',
'戮' => '戮',
'陸' => '陸',
'倫' => '倫',
'崙' => '崙',
'淪' => '淪',
'輪' => '輪',
'律' => '律',
'慄' => '慄',
'栗' => '栗',
'率' => '率',
'隆' => '隆',
'利' => '利',
'吏' => '吏',
'履' => '履',
'易' => '易',
'李' => '李',
'梨' => '梨',
'泥' => '泥',
'理' => '理',
'痢' => '痢',
'罹' => '罹',
'裏' => '裏',
'裡' => '裡',
'里' => '里',
'離' => '離',
'匿' => '匿',
'溺' => '溺',
'吝' => '吝',
'燐' => '燐',
'璘' => '璘',
'藺' => '藺',
'隣' => '隣',
'鱗' => '鱗',
'麟' => '麟',
'林' => '林',
'淋' => '淋',
'臨' => '臨',
'立' => '立',
'笠' => '笠',
'粒' => '粒',
'狀' => '狀',
'炙' => '炙',
'識' => '識',
'什' => '什',
'茶' => '茶',
'刺' => '刺',
'切' => '切',
'度' => '度',
'拓' => '拓',
'糖' => '糖',
'宅' => '宅',
'洞' => '洞',
'暴' => '暴',
'輻' => '輻',
'行' => '行',
'降' => '降',
'見' => '見',
'廓' => '廓',
'兀' => '兀',
'嗀' => '嗀',
'塚' => '塚',
'晴' => '晴',
'凞' => '凞',
'猪' => '猪',
'益' => '益',
'礼' => '礼',
'神' => '神',
'祥' => '祥',
'福' => '福',
'靖' => '靖',
'精' => '精',
'羽' => '羽',
'蘒' => '蘒',
'諸' => '諸',
'逸' => '逸',
'都' => '都',
'飯' => '飯',
'飼' => '飼',
'館' => '館',
'鶴' => '鶴',
'郞' => '郞',
'隷' => '隷',
'侮' => '侮',
'僧' => '僧',
'免' => '免',
'勉' => '勉',
'勤' => '勤',
'卑' => '卑',
'喝' => '喝',
'嘆' => '嘆',
'器' => '器',
'塀' => '塀',
'墨' => '墨',
'層' => '層',
'屮' => '屮',
'悔' => '悔',
'慨' => '慨',
'憎' => '憎',
'懲' => '懲',
'敏' => '敏',
'既' => '既',
'暑' => '暑',
'梅' => '梅',
'海' => '海',
'渚' => '渚',
'漢' => '漢',
'煮' => '煮',
'爫' => '爫',
'琢' => '琢',
'碑' => '碑',
'社' => '社',
'祉' => '祉',
'祈' => '祈',
'祐' => '祐',
'祖' => '祖',
'祝' => '祝',
'禍' => '禍',
'禎' => '禎',
'穀' => '穀',
'突' => '突',
'節' => '節',
'練' => '練',
'縉' => '縉',
'繁' => '繁',
'署' => '署',
'者' => '者',
'臭' => '臭',
'艹' => '艹',
'艹' => '艹',
'著' => '著',
'褐' => '褐',
'視' => '視',
'謁' => '謁',
'謹' => '謹',
'賓' => '賓',
'贈' => '贈',
'辶' => '辶',
'逸' => '逸',
'難' => '難',
'響' => '響',
'頻' => '頻',
'恵' => '恵',
'𤋮' => '𤋮',
'舘' => '舘',
'並' => '並',
'况' => '况',
'全' => '全',
'侀' => '侀',
'充' => '充',
'冀' => '冀',
'勇' => '勇',
'勺' => '勺',
'喝' => '喝',
'啕' => '啕',
'喙' => '喙',
'嗢' => '嗢',
'塚' => '塚',
'墳' => '墳',
'奄' => '奄',
'奔' => '奔',
'婢' => '婢',
'嬨' => '嬨',
'廒' => '廒',
'廙' => '廙',
'彩' => '彩',
'徭' => '徭',
'惘' => '惘',
'慎' => '慎',
'愈' => '愈',
'憎' => '憎',
'慠' => '慠',
'懲' => '懲',
'戴' => '戴',
'揄' => '揄',
'搜' => '搜',
'摒' => '摒',
'敖' => '敖',
'晴' => '晴',
'朗' => '朗',
'望' => '望',
'杖' => '杖',
'歹' => '歹',
'殺' => '殺',
'流' => '流',
'滛' => '滛',
'滋' => '滋',
'漢' => '漢',
'瀞' => '瀞',
'煮' => '煮',
'瞧' => '瞧',
'爵' => '爵',
'犯' => '犯',
'猪' => '猪',
'瑱' => '瑱',
'甆' => '甆',
'画' => '画',
'瘝' => '瘝',
'瘟' => '瘟',
'益' => '益',
'盛' => '盛',
'直' => '直',
'睊' => '睊',
'着' => '着',
'磌' => '磌',
'窱' => '窱',
'節' => '節',
'类' => '类',
'絛' => '絛',
'練' => '練',
'缾' => '缾',
'者' => '者',
'荒' => '荒',
'華' => '華',
'蝹' => '蝹',
'襁' => '襁',
'覆' => '覆',
'視' => '視',
'調' => '調',
'諸' => '諸',
'請' => '請',
'謁' => '謁',
'諾' => '諾',
'諭' => '諭',
'謹' => '謹',
'變' => '變',
'贈' => '贈',
'輸' => '輸',
'遲' => '遲',
'醙' => '醙',
'鉶' => '鉶',
'陼' => '陼',
'難' => '難',
'靖' => '靖',
'韛' => '韛',
'響' => '響',
'頋' => '頋',
'頻' => '頻',
'鬒' => '鬒',
'龜' => '龜',
'𢡊' => '𢡊',
'𢡄' => '𢡄',
'𣏕' => '𣏕',
'㮝' => '㮝',
'䀘' => '䀘',
'䀹' => '䀹',
'𥉉' => '𥉉',
'𥳐' => '𥳐',
'𧻓' => '𧻓',
'齃' => '齃',
'龎' => '龎',
'יִ' => 'יִ',
'ײַ' => 'ײַ',
'שׁ' => 'שׁ',
'שׂ' => 'שׂ',
'שּׁ' => 'שּׁ',
'שּׂ' => 'שּׂ',
'אַ' => 'אַ',
'אָ' => 'אָ',
'אּ' => 'אּ',
'בּ' => 'בּ',
'גּ' => 'גּ',
'דּ' => 'דּ',
'הּ' => 'הּ',
'וּ' => 'וּ',
'זּ' => 'זּ',
'טּ' => 'טּ',
'יּ' => 'יּ',
'ךּ' => 'ךּ',
'כּ' => 'כּ',
'לּ' => 'לּ',
'מּ' => 'מּ',
'נּ' => 'נּ',
'סּ' => 'סּ',
'ףּ' => 'ףּ',
'פּ' => 'פּ',
'צּ' => 'צּ',
'קּ' => 'קּ',
'רּ' => 'רּ',
'שּ' => 'שּ',
'תּ' => 'תּ',
'וֹ' => 'וֹ',
'בֿ' => 'בֿ',
'כֿ' => 'כֿ',
'פֿ' => 'פֿ',
'𑂚' => '𑂚',
'𑂜' => '𑂜',
'𑂫' => '𑂫',
'𑄮' => '𑄮',
'𑄯' => '𑄯',
'𑍋' => '𑍋',
'𑍌' => '𑍌',
'𑒻' => '𑒻',
'𑒼' => '𑒼',
'𑒾' => '𑒾',
'𑖺' => '𑖺',
'𑖻' => '𑖻',
'𑤸' => '𑤸',
'𝅗𝅥' => '𝅗𝅥',
'𝅘𝅥' => '𝅘𝅥',
'𝅘𝅥𝅮' => '𝅘𝅥𝅮',
'𝅘𝅥𝅯' => '𝅘𝅥𝅯',
'𝅘𝅥𝅰' => '𝅘𝅥𝅰',
'𝅘𝅥𝅱' => '𝅘𝅥𝅱',
'𝅘𝅥𝅲' => '𝅘𝅥𝅲',
'𝆹𝅥' => '𝆹𝅥',
'𝆺𝅥' => '𝆺𝅥',
'𝆹𝅥𝅮' => '𝆹𝅥𝅮',
'𝆺𝅥𝅮' => '𝆺𝅥𝅮',
'𝆹𝅥𝅯' => '𝆹𝅥𝅯',
'𝆺𝅥𝅯' => '𝆺𝅥𝅯',
'丽' => '丽',
'丸' => '丸',
'乁' => '乁',
'𠄢' => '𠄢',
'你' => '你',
'侮' => '侮',
'侻' => '侻',
'倂' => '倂',
'偺' => '偺',
'備' => '備',
'僧' => '僧',
'像' => '像',
'㒞' => '㒞',
'𠘺' => '𠘺',
'免' => '免',
'兔' => '兔',
'兤' => '兤',
'具' => '具',
'𠔜' => '𠔜',
'㒹' => '㒹',
'內' => '內',
'再' => '再',
'𠕋' => '𠕋',
'冗' => '冗',
'冤' => '冤',
'仌' => '仌',
'冬' => '冬',
'况' => '况',
'𩇟' => '𩇟',
'凵' => '凵',
'刃' => '刃',
'㓟' => '㓟',
'刻' => '刻',
'剆' => '剆',
'割' => '割',
'剷' => '剷',
'㔕' => '㔕',
'勇' => '勇',
'勉' => '勉',
'勤' => '勤',
'勺' => '勺',
'包' => '包',
'匆' => '匆',
'北' => '北',
'卉' => '卉',
'卑' => '卑',
'博' => '博',
'即' => '即',
'卽' => '卽',
'卿' => '卿',
'卿' => '卿',
'卿' => '卿',
'𠨬' => '𠨬',
'灰' => '灰',
'及' => '及',
'叟' => '叟',
'𠭣' => '𠭣',
'叫' => '叫',
'叱' => '叱',
'吆' => '吆',
'咞' => '咞',
'吸' => '吸',
'呈' => '呈',
'周' => '周',
'咢' => '咢',
'哶' => '哶',
'唐' => '唐',
'啓' => '啓',
'啣' => '啣',
'善' => '善',
'善' => '善',
'喙' => '喙',
'喫' => '喫',
'喳' => '喳',
'嗂' => '嗂',
'圖' => '圖',
'嘆' => '嘆',
'圗' => '圗',
'噑' => '噑',
'噴' => '噴',
'切' => '切',
'壮' => '壮',
'城' => '城',
'埴' => '埴',
'堍' => '堍',
'型' => '型',
'堲' => '堲',
'報' => '報',
'墬' => '墬',
'𡓤' => '𡓤',
'売' => '売',
'壷' => '壷',
'夆' => '夆',
'多' => '多',
'夢' => '夢',
'奢' => '奢',
'𡚨' => '𡚨',
'𡛪' => '𡛪',
'姬' => '姬',
'娛' => '娛',
'娧' => '娧',
'姘' => '姘',
'婦' => '婦',
'㛮' => '㛮',
'㛼' => '㛼',
'嬈' => '嬈',
'嬾' => '嬾',
'嬾' => '嬾',
'𡧈' => '𡧈',
'寃' => '寃',
'寘' => '寘',
'寧' => '寧',
'寳' => '寳',
'𡬘' => '𡬘',
'寿' => '寿',
'将' => '将',
'当' => '当',
'尢' => '尢',
'㞁' => '㞁',
'屠' => '屠',
'屮' => '屮',
'峀' => '峀',
'岍' => '岍',
'𡷤' => '𡷤',
'嵃' => '嵃',
'𡷦' => '𡷦',
'嵮' => '嵮',
'嵫' => '嵫',
'嵼' => '嵼',
'巡' => '巡',
'巢' => '巢',
'㠯' => '㠯',
'巽' => '巽',
'帨' => '帨',
'帽' => '帽',
'幩' => '幩',
'㡢' => '㡢',
'𢆃' => '𢆃',
'㡼' => '㡼',
'庰' => '庰',
'庳' => '庳',
'庶' => '庶',
'廊' => '廊',
'𪎒' => '𪎒',
'廾' => '廾',
'𢌱' => '𢌱',
'𢌱' => '𢌱',
'舁' => '舁',
'弢' => '弢',
'弢' => '弢',
'㣇' => '㣇',
'𣊸' => '𣊸',
'𦇚' => '𦇚',
'形' => '形',
'彫' => '彫',
'㣣' => '㣣',
'徚' => '徚',
'忍' => '忍',
'志' => '志',
'忹' => '忹',
'悁' => '悁',
'㤺' => '㤺',
'㤜' => '㤜',
'悔' => '悔',
'𢛔' => '𢛔',
'惇' => '惇',
'慈' => '慈',
'慌' => '慌',
'慎' => '慎',
'慌' => '慌',
'慺' => '慺',
'憎' => '憎',
'憲' => '憲',
'憤' => '憤',
'憯' => '憯',
'懞' => '懞',
'懲' => '懲',
'懶' => '懶',
'成' => '成',
'戛' => '戛',
'扝' => '扝',
'抱' => '抱',
'拔' => '拔',
'捐' => '捐',
'𢬌' => '𢬌',
'挽' => '挽',
'拼' => '拼',
'捨' => '捨',
'掃' => '掃',
'揤' => '揤',
'𢯱' => '𢯱',
'搢' => '搢',
'揅' => '揅',
'掩' => '掩',
'㨮' => '㨮',
'摩' => '摩',
'摾' => '摾',
'撝' => '撝',
'摷' => '摷',
'㩬' => '㩬',
'敏' => '敏',
'敬' => '敬',
'𣀊' => '𣀊',
'旣' => '旣',
'書' => '書',
'晉' => '晉',
'㬙' => '㬙',
'暑' => '暑',
'㬈' => '㬈',
'㫤' => '㫤',
'冒' => '冒',
'冕' => '冕',
'最' => '最',
'暜' => '暜',
'肭' => '肭',
'䏙' => '䏙',
'朗' => '朗',
'望' => '望',
'朡' => '朡',
'杞' => '杞',
'杓' => '杓',
'𣏃' => '𣏃',
'㭉' => '㭉',
'柺' => '柺',
'枅' => '枅',
'桒' => '桒',
'梅' => '梅',
'𣑭' => '𣑭',
'梎' => '梎',
'栟' => '栟',
'椔' => '椔',
'㮝' => '㮝',
'楂' => '楂',
'榣' => '榣',
'槪' => '槪',
'檨' => '檨',
'𣚣' => '𣚣',
'櫛' => '櫛',
'㰘' => '㰘',
'次' => '次',
'𣢧' => '𣢧',
'歔' => '歔',
'㱎' => '㱎',
'歲' => '歲',
'殟' => '殟',
'殺' => '殺',
'殻' => '殻',
'𣪍' => '𣪍',
'𡴋' => '𡴋',
'𣫺' => '𣫺',
'汎' => '汎',
'𣲼' => '𣲼',
'沿' => '沿',
'泍' => '泍',
'汧' => '汧',
'洖' => '洖',
'派' => '派',
'海' => '海',
'流' => '流',
'浩' => '浩',
'浸' => '浸',
'涅' => '涅',
'𣴞' => '𣴞',
'洴' => '洴',
'港' => '港',
'湮' => '湮',
'㴳' => '㴳',
'滋' => '滋',
'滇' => '滇',
'𣻑' => '𣻑',
'淹' => '淹',
'潮' => '潮',
'𣽞' => '𣽞',
'𣾎' => '𣾎',
'濆' => '濆',
'瀹' => '瀹',
'瀞' => '瀞',
'瀛' => '瀛',
'㶖' => '㶖',
'灊' => '灊',
'災' => '災',
'灷' => '灷',
'炭' => '炭',
'𠔥' => '𠔥',
'煅' => '煅',
'𤉣' => '𤉣',
'熜' => '熜',
'𤎫' => '𤎫',
'爨' => '爨',
'爵' => '爵',
'牐' => '牐',
'𤘈' => '𤘈',
'犀' => '犀',
'犕' => '犕',
'𤜵' => '𤜵',
'𤠔' => '𤠔',
'獺' => '獺',
'王' => '王',
'㺬' => '㺬',
'玥' => '玥',
'㺸' => '㺸',
'㺸' => '㺸',
'瑇' => '瑇',
'瑜' => '瑜',
'瑱' => '瑱',
'璅' => '璅',
'瓊' => '瓊',
'㼛' => '㼛',
'甤' => '甤',
'𤰶' => '𤰶',
'甾' => '甾',
'𤲒' => '𤲒',
'異' => '異',
'𢆟' => '𢆟',
'瘐' => '瘐',
'𤾡' => '𤾡',
'𤾸' => '𤾸',
'𥁄' => '𥁄',
'㿼' => '㿼',
'䀈' => '䀈',
'直' => '直',
'𥃳' => '𥃳',
'𥃲' => '𥃲',
'𥄙' => '𥄙',
'𥄳' => '𥄳',
'眞' => '眞',
'真' => '真',
'真' => '真',
'睊' => '睊',
'䀹' => '䀹',
'瞋' => '瞋',
'䁆' => '䁆',
'䂖' => '䂖',
'𥐝' => '𥐝',
'硎' => '硎',
'碌' => '碌',
'磌' => '磌',
'䃣' => '䃣',
'𥘦' => '𥘦',
'祖' => '祖',
'𥚚' => '𥚚',
'𥛅' => '𥛅',
'福' => '福',
'秫' => '秫',
'䄯' => '䄯',
'穀' => '穀',
'穊' => '穊',
'穏' => '穏',
'𥥼' => '𥥼',
'𥪧' => '𥪧',
'𥪧' => '𥪧',
'竮' => '竮',
'䈂' => '䈂',
'𥮫' => '𥮫',
'篆' => '篆',
'築' => '築',
'䈧' => '䈧',
'𥲀' => '𥲀',
'糒' => '糒',
'䊠' => '䊠',
'糨' => '糨',
'糣' => '糣',
'紀' => '紀',
'𥾆' => '𥾆',
'絣' => '絣',
'䌁' => '䌁',
'緇' => '緇',
'縂' => '縂',
'繅' => '繅',
'䌴' => '䌴',
'𦈨' => '𦈨',
'𦉇' => '𦉇',
'䍙' => '䍙',
'𦋙' => '𦋙',
'罺' => '罺',
'𦌾' => '𦌾',
'羕' => '羕',
'翺' => '翺',
'者' => '者',
'𦓚' => '𦓚',
'𦔣' => '𦔣',
'聠' => '聠',
'𦖨' => '𦖨',
'聰' => '聰',
'𣍟' => '𣍟',
'䏕' => '䏕',
'育' => '育',
'脃' => '脃',
'䐋' => '䐋',
'脾' => '脾',
'媵' => '媵',
'𦞧' => '𦞧',
'𦞵' => '𦞵',
'𣎓' => '𣎓',
'𣎜' => '𣎜',
'舁' => '舁',
'舄' => '舄',
'辞' => '辞',
'䑫' => '䑫',
'芑' => '芑',
'芋' => '芋',
'芝' => '芝',
'劳' => '劳',
'花' => '花',
'芳' => '芳',
'芽' => '芽',
'苦' => '苦',
'𦬼' => '𦬼',
'若' => '若',
'茝' => '茝',
'荣' => '荣',
'莭' => '莭',
'茣' => '茣',
'莽' => '莽',
'菧' => '菧',
'著' => '著',
'荓' => '荓',
'菊' => '菊',
'菌' => '菌',
'菜' => '菜',
'𦰶' => '𦰶',
'𦵫' => '𦵫',
'𦳕' => '𦳕',
'䔫' => '䔫',
'蓱' => '蓱',
'蓳' => '蓳',
'蔖' => '蔖',
'𧏊' => '𧏊',
'蕤' => '蕤',
'𦼬' => '𦼬',
'䕝' => '䕝',
'䕡' => '䕡',
'𦾱' => '𦾱',
'𧃒' => '𧃒',
'䕫' => '䕫',
'虐' => '虐',
'虜' => '虜',
'虧' => '虧',
'虩' => '虩',
'蚩' => '蚩',
'蚈' => '蚈',
'蜎' => '蜎',
'蛢' => '蛢',
'蝹' => '蝹',
'蜨' => '蜨',
'蝫' => '蝫',
'螆' => '螆',
'䗗' => '䗗',
'蟡' => '蟡',
'蠁' => '蠁',
'䗹' => '䗹',
'衠' => '衠',
'衣' => '衣',
'𧙧' => '𧙧',
'裗' => '裗',
'裞' => '裞',
'䘵' => '䘵',
'裺' => '裺',
'㒻' => '㒻',
'𧢮' => '𧢮',
'𧥦' => '𧥦',
'䚾' => '䚾',
'䛇' => '䛇',
'誠' => '誠',
'諭' => '諭',
'變' => '變',
'豕' => '豕',
'𧲨' => '𧲨',
'貫' => '貫',
'賁' => '賁',
'贛' => '贛',
'起' => '起',
'𧼯' => '𧼯',
'𠠄' => '𠠄',
'跋' => '跋',
'趼' => '趼',
'跰' => '跰',
'𠣞' => '𠣞',
'軔' => '軔',
'輸' => '輸',
'𨗒' => '𨗒',
'𨗭' => '𨗭',
'邔' => '邔',
'郱' => '郱',
'鄑' => '鄑',
'𨜮' => '𨜮',
'鄛' => '鄛',
'鈸' => '鈸',
'鋗' => '鋗',
'鋘' => '鋘',
'鉼' => '鉼',
'鏹' => '鏹',
'鐕' => '鐕',
'𨯺' => '𨯺',
'開' => '開',
'䦕' => '䦕',
'閷' => '閷',
'𨵷' => '𨵷',
'䧦' => '䧦',
'雃' => '雃',
'嶲' => '嶲',
'霣' => '霣',
'𩅅' => '𩅅',
'𩈚' => '𩈚',
'䩮' => '䩮',
'䩶' => '䩶',
'韠' => '韠',
'𩐊' => '𩐊',
'䪲' => '䪲',
'𩒖' => '𩒖',
'頋' => '頋',
'頋' => '頋',
'頩' => '頩',
'𩖶' => '𩖶',
'飢' => '飢',
'䬳' => '䬳',
'餩' => '餩',
'馧' => '馧',
'駂' => '駂',
'駾' => '駾',
'䯎' => '䯎',
'𩬰' => '𩬰',
'鬒' => '鬒',
'鱀' => '鱀',
'鳽' => '鳽',
'䳎' => '䳎',
'䳭' => '䳭',
'鵧' => '鵧',
'𪃎' => '𪃎',
'䳸' => '䳸',
'𪄅' => '𪄅',
'𪈎' => '𪈎',
'𪊑' => '𪊑',
'麻' => '麻',
'䵖' => '䵖',
'黹' => '黹',
'黾' => '黾',
'鼅' => '鼅',
'鼏' => '鼏',
'鼖' => '鼖',
'鼻' => '鼻',
'𪘀' => '𪘀',
);
<?php

return array (
'̀' => 230,
'́' => 230,
'̂' => 230,
'̃' => 230,
'̄' => 230,
'̅' => 230,
'̆' => 230,
'̇' => 230,
'̈' => 230,
'̉' => 230,
'̊' => 230,
'̋' => 230,
'̌' => 230,
'̍' => 230,
'̎' => 230,
'̏' => 230,
'̐' => 230,
'̑' => 230,
'̒' => 230,
'̓' => 230,
'̔' => 230,
'̕' => 232,
'̖' => 220,
'̗' => 220,
'̘' => 220,
'̙' => 220,
'̚' => 232,
'̛' => 216,
'̜' => 220,
'̝' => 220,
'̞' => 220,
'̟' => 220,
'̠' => 220,
'̡' => 202,
'̢' => 202,
'̣' => 220,
'̤' => 220,
'̥' => 220,
'̦' => 220,
'̧' => 202,
'̨' => 202,
'̩' => 220,
'̪' => 220,
'̫' => 220,
'̬' => 220,
'̭' => 220,
'̮' => 220,
'̯' => 220,
'̰' => 220,
'̱' => 220,
'̲' => 220,
'̳' => 220,
'̴' => 1,
'̵' => 1,
'̶' => 1,
'̷' => 1,
'̸' => 1,
'̹' => 220,
'̺' => 220,
'̻' => 220,
'̼' => 220,
'̽' => 230,
'̾' => 230,
'̿' => 230,
'̀' => 230,
'́' => 230,
'͂' => 230,
'̓' => 230,
'̈́' => 230,
'ͅ' => 240,
'͆' => 230,
'͇' => 220,
'͈' => 220,
'͉' => 220,
'͊' => 230,
'͋' => 230,
'͌' => 230,
'͍' => 220,
'͎' => 220,
'͐' => 230,
'͑' => 230,
'͒' => 230,
'͓' => 220,
'͔' => 220,
'͕' => 220,
'͖' => 220,
'͗' => 230,
'͘' => 232,
'͙' => 220,
'͚' => 220,
'͛' => 230,
'͜' => 233,
'͝' => 234,
'͞' => 234,
'͟' => 233,
'͠' => 234,
'͡' => 234,
'͢' => 233,
'ͣ' => 230,
'ͤ' => 230,
'ͥ' => 230,
'ͦ' => 230,
'ͧ' => 230,
'ͨ' => 230,
'ͩ' => 230,
'ͪ' => 230,
'ͫ' => 230,
'ͬ' => 230,
'ͭ' => 230,
'ͮ' => 230,
'ͯ' => 230,
'҃' => 230,
'҄' => 230,
'҅' => 230,
'҆' => 230,
'҇' => 230,
'֑' => 220,
'֒' => 230,
'֓' => 230,
'֔' => 230,
'֕' => 230,
'֖' => 220,
'֗' => 230,
'֘' => 230,
'֙' => 230,
'֚' => 222,
'֛' => 220,
'֜' => 230,
'֝' => 230,
'֞' => 230,
'֟' => 230,
'֠' => 230,
'֡' => 230,
'֢' => 220,
'֣' => 220,
'֤' => 220,
'֥' => 220,
'֦' => 220,
'֧' => 220,
'֨' => 230,
'֩' => 230,
'֪' => 220,
'֫' => 230,
'֬' => 230,
'֭' => 222,
'֮' => 228,
'֯' => 230,
'ְ' => 10,
'ֱ' => 11,
'ֲ' => 12,
'ֳ' => 13,
'ִ' => 14,
'ֵ' => 15,
'ֶ' => 16,
'ַ' => 17,
'ָ' => 18,
'ֹ' => 19,
'ֺ' => 19,
'ֻ' => 20,
'ּ' => 21,
'ֽ' => 22,
'ֿ' => 23,
'ׁ' => 24,
'ׂ' => 25,
'ׄ' => 230,
'ׅ' => 220,
'ׇ' => 18,
'ؐ' => 230,
'ؑ' => 230,
'ؒ' => 230,
'ؓ' => 230,
'ؔ' => 230,
'ؕ' => 230,
'ؖ' => 230,
'ؗ' => 230,
'ؘ' => 30,
'ؙ' => 31,
'ؚ' => 32,
'ً' => 27,
'ٌ' => 28,
'ٍ' => 29,
'َ' => 30,
'ُ' => 31,
'ِ' => 32,
'ّ' => 33,
'ْ' => 34,
'ٓ' => 230,
'ٔ' => 230,
'ٕ' => 220,
'ٖ' => 220,
'ٗ' => 230,
'٘' => 230,
'ٙ' => 230,
'ٚ' => 230,
'ٛ' => 230,
'ٜ' => 220,
'ٝ' => 230,
'ٞ' => 230,
'ٟ' => 220,
'ٰ' => 35,
'ۖ' => 230,
'ۗ' => 230,
'ۘ' => 230,
'ۙ' => 230,
'ۚ' => 230,
'ۛ' => 230,
'ۜ' => 230,
'۟' => 230,
'۠' => 230,
'ۡ' => 230,
'ۢ' => 230,
'ۣ' => 220,
'ۤ' => 230,
'ۧ' => 230,
'ۨ' => 230,
'۪' => 220,
'۫' => 230,
'۬' => 230,
'ۭ' => 220,
'ܑ' => 36,
'ܰ' => 230,
'ܱ' => 220,
'ܲ' => 230,
'ܳ' => 230,
'ܴ' => 220,
'ܵ' => 230,
'ܶ' => 230,
'ܷ' => 220,
'ܸ' => 220,
'ܹ' => 220,
'ܺ' => 230,
'ܻ' => 220,
'ܼ' => 220,
'ܽ' => 230,
'ܾ' => 220,
'ܿ' => 230,
'݀' => 230,
'݁' => 230,
'݂' => 220,
'݃' => 230,
'݄' => 220,
'݅' => 230,
'݆' => 220,
'݇' => 230,
'݈' => 220,
'݉' => 230,
'݊' => 230,
'߫' => 230,
'߬' => 230,
'߭' => 230,
'߮' => 230,
'߯' => 230,
'߰' => 230,
'߱' => 230,
'߲' => 220,
'߳' => 230,
'߽' => 220,
'ࠖ' => 230,
'ࠗ' => 230,
'࠘' => 230,
'࠙' => 230,
'ࠛ' => 230,
'ࠜ' => 230,
'ࠝ' => 230,
'ࠞ' => 230,
'ࠟ' => 230,
'ࠠ' => 230,
'ࠡ' => 230,
'ࠢ' => 230,
'ࠣ' => 230,
'ࠥ' => 230,
'ࠦ' => 230,
'ࠧ' => 230,
'ࠩ' => 230,
'ࠪ' => 230,
'ࠫ' => 230,
'ࠬ' => 230,
'࠭' => 230,
'࡙' => 220,
'࡚' => 220,
'࡛' => 220,
'࣓' => 220,
'ࣔ' => 230,
'ࣕ' => 230,
'ࣖ' => 230,
'ࣗ' => 230,
'ࣘ' => 230,
'ࣙ' => 230,
'ࣚ' => 230,
'ࣛ' => 230,
'ࣜ' => 230,
'ࣝ' => 230,
'ࣞ' => 230,
'ࣟ' => 230,
'࣠' => 230,
'࣡' => 230,
'ࣣ' => 220,
'ࣤ' => 230,
'ࣥ' => 230,
'ࣦ' => 220,
'ࣧ' => 230,
'ࣨ' => 230,
'ࣩ' => 220,
'࣪' => 230,
'࣫' => 230,
'࣬' => 230,
'࣭' => 220,
'࣮' => 220,
'࣯' => 220,
'ࣰ' => 27,
'ࣱ' => 28,
'ࣲ' => 29,
'ࣳ' => 230,
'ࣴ' => 230,
'ࣵ' => 230,
'ࣶ' => 220,
'ࣷ' => 230,
'ࣸ' => 230,
'ࣹ' => 220,
'ࣺ' => 220,
'ࣻ' => 230,
'ࣼ' => 230,
'ࣽ' => 230,
'ࣾ' => 230,
'ࣿ' => 230,
'़' => 7,
'्' => 9,
'॑' => 230,
'॒' => 220,
'॓' => 230,
'॔' => 230,
'়' => 7,
'্' => 9,
'৾' => 230,
'਼' => 7,
'੍' => 9,
'઼' => 7,
'્' => 9,
'଼' => 7,
'୍' => 9,
'்' => 9,
'్' => 9,
'ౕ' => 84,
'ౖ' => 91,
'಼' => 7,
'್' => 9,
'഻' => 9,
'഼' => 9,
'്' => 9,
'්' => 9,
'ุ' => 103,
'ู' => 103,
'ฺ' => 9,
'่' => 107,
'้' => 107,
'๊' => 107,
'๋' => 107,
'ຸ' => 118,
'ູ' => 118,
'຺' => 9,
'່' => 122,
'້' => 122,
'໊' => 122,
'໋' => 122,
'༘' => 220,
'༙' => 220,
'༵' => 220,
'༷' => 220,
'༹' => 216,
'ཱ' => 129,
'ི' => 130,
'ུ' => 132,
'ེ' => 130,
'ཻ' => 130,
'ོ' => 130,
'ཽ' => 130,
'ྀ' => 130,
'ྂ' => 230,
'ྃ' => 230,
'྄' => 9,
'྆' => 230,
'྇' => 230,
'࿆' => 220,
'့' => 7,
'္' => 9,
'်' => 9,
'ႍ' => 220,
'፝' => 230,
'፞' => 230,
'፟' => 230,
'᜔' => 9,
'᜴' => 9,
'្' => 9,
'៝' => 230,
'ᢩ' => 228,
'᤹' => 222,
'᤺' => 230,
'᤻' => 220,
'ᨗ' => 230,
'ᨘ' => 220,
'᩠' => 9,
'᩵' => 230,
'᩶' => 230,
'᩷' => 230,
'᩸' => 230,
'᩹' => 230,
'᩺' => 230,
'᩻' => 230,
'᩼' => 230,
'᩿' => 220,
'᪰' => 230,
'᪱' => 230,
'᪲' => 230,
'᪳' => 230,
'᪴' => 230,
'᪵' => 220,
'᪶' => 220,
'᪷' => 220,
'᪸' => 220,
'᪹' => 220,
'᪺' => 220,
'᪻' => 230,
'᪼' => 230,
'᪽' => 220,
'ᪿ' => 220,
'ᫀ' => 220,
'᬴' => 7,
'᭄' => 9,
'᭫' => 230,
'᭬' => 220,
'᭭' => 230,
'᭮' => 230,
'᭯' => 230,
'᭰' => 230,
'᭱' => 230,
'᭲' => 230,
'᭳' => 230,
'᮪' => 9,
'᮫' => 9,
'᯦' => 7,
'᯲' => 9,
'᯳' => 9,
'᰷' => 7,
'᳐' => 230,
'᳑' => 230,
'᳒' => 230,
'᳔' => 1,
'᳕' => 220,
'᳖' => 220,
'᳗' => 220,
'᳘' => 220,
'᳙' => 220,
'᳚' => 230,
'᳛' => 230,
'᳜' => 220,
'᳝' => 220,
'᳞' => 220,
'᳟' => 220,
'᳠' => 230,
'᳢' => 1,
'᳣' => 1,
'᳤' => 1,
'᳥' => 1,
'᳦' => 1,
'᳧' => 1,
'᳨' => 1,
'᳭' => 220,
'᳴' => 230,
'᳸' => 230,
'᳹' => 230,
'᷀' => 230,
'᷁' => 230,
'᷂' => 220,
'᷃' => 230,
'᷄' => 230,
'᷅' => 230,
'᷆' => 230,
'᷇' => 230,
'᷈' => 230,
'᷉' => 230,
'᷊' => 220,
'᷋' => 230,
'᷌' => 230,
'᷍' => 234,
'᷎' => 214,
'᷏' => 220,
'᷐' => 202,
'᷑' => 230,
'᷒' => 230,
'ᷓ' => 230,
'ᷔ' => 230,
'ᷕ' => 230,
'ᷖ' => 230,
'ᷗ' => 230,
'ᷘ' => 230,
'ᷙ' => 230,
'ᷚ' => 230,
'ᷛ' => 230,
'ᷜ' => 230,
'ᷝ' => 230,
'ᷞ' => 230,
'ᷟ' => 230,
'ᷠ' => 230,
'ᷡ' => 230,
'ᷢ' => 230,
'ᷣ' => 230,
'ᷤ' => 230,
'ᷥ' => 230,
'ᷦ' => 230,
'ᷧ' => 230,
'ᷨ' => 230,
'ᷩ' => 230,
'ᷪ' => 230,
'ᷫ' => 230,
'ᷬ' => 230,
'ᷭ' => 230,
'ᷮ' => 230,
'ᷯ' => 230,
'ᷰ' => 230,
'ᷱ' => 230,
'ᷲ' => 230,
'ᷳ' => 230,
'ᷴ' => 230,
'᷵' => 230,
'᷶' => 232,
'᷷' => 228,
'᷸' => 228,
'᷹' => 220,
'᷻' => 230,
'᷼' => 233,
'᷽' => 220,
'᷾' => 230,
'᷿' => 220,
'⃐' => 230,
'⃑' => 230,
'⃒' => 1,
'⃓' => 1,
'⃔' => 230,
'⃕' => 230,
'⃖' => 230,
'⃗' => 230,
'⃘' => 1,
'⃙' => 1,
'⃚' => 1,
'⃛' => 230,
'⃜' => 230,
'⃡' => 230,
'⃥' => 1,
'⃦' => 1,
'⃧' => 230,
'⃨' => 220,
'⃩' => 230,
'⃪' => 1,
'⃫' => 1,
'⃬' => 220,
'⃭' => 220,
'⃮' => 220,
'⃯' => 220,
'⃰' => 230,
'⳯' => 230,
'⳰' => 230,
'⳱' => 230,
'⵿' => 9,
'ⷠ' => 230,
'ⷡ' => 230,
'ⷢ' => 230,
'ⷣ' => 230,
'ⷤ' => 230,
'ⷥ' => 230,
'ⷦ' => 230,
'ⷧ' => 230,
'ⷨ' => 230,
'ⷩ' => 230,
'ⷪ' => 230,
'ⷫ' => 230,
'ⷬ' => 230,
'ⷭ' => 230,
'ⷮ' => 230,
'ⷯ' => 230,
'ⷰ' => 230,
'ⷱ' => 230,
'ⷲ' => 230,
'ⷳ' => 230,
'ⷴ' => 230,
'ⷵ' => 230,
'ⷶ' => 230,
'ⷷ' => 230,
'ⷸ' => 230,
'ⷹ' => 230,
'ⷺ' => 230,
'ⷻ' => 230,
'ⷼ' => 230,
'ⷽ' => 230,
'ⷾ' => 230,
'ⷿ' => 230,
'〪' => 218,
'〫' => 228,
'〬' => 232,
'〭' => 222,
'〮' => 224,
'〯' => 224,
'゙' => 8,
'゚' => 8,
'꙯' => 230,
'ꙴ' => 230,
'ꙵ' => 230,
'ꙶ' => 230,
'ꙷ' => 230,
'ꙸ' => 230,
'ꙹ' => 230,
'ꙺ' => 230,
'ꙻ' => 230,
'꙼' => 230,
'꙽' => 230,
'ꚞ' => 230,
'ꚟ' => 230,
'꛰' => 230,
'꛱' => 230,
'꠆' => 9,
'꠬' => 9,
'꣄' => 9,
'꣠' => 230,
'꣡' => 230,
'꣢' => 230,
'꣣' => 230,
'꣤' => 230,
'꣥' => 230,
'꣦' => 230,
'꣧' => 230,
'꣨' => 230,
'꣩' => 230,
'꣪' => 230,
'꣫' => 230,
'꣬' => 230,
'꣭' => 230,
'꣮' => 230,
'꣯' => 230,
'꣰' => 230,
'꣱' => 230,
'꤫' => 220,
'꤬' => 220,
'꤭' => 220,
'꥓' => 9,
'꦳' => 7,
'꧀' => 9,
'ꪰ' => 230,
'ꪲ' => 230,
'ꪳ' => 230,
'ꪴ' => 220,
'ꪷ' => 230,
'ꪸ' => 230,
'ꪾ' => 230,
'꪿' => 230,
'꫁' => 230,
'꫶' => 9,
'꯭' => 9,
'ﬞ' => 26,
'︠' => 230,
'︡' => 230,
'︢' => 230,
'︣' => 230,
'︤' => 230,
'︥' => 230,
'︦' => 230,
'︧' => 220,
'︨' => 220,
'︩' => 220,
'︪' => 220,
'︫' => 220,
'︬' => 220,
'︭' => 220,
'︮' => 230,
'︯' => 230,
'𐇽' => 220,
'𐋠' => 220,
'𐍶' => 230,
'𐍷' => 230,
'𐍸' => 230,
'𐍹' => 230,
'𐍺' => 230,
'𐨍' => 220,
'𐨏' => 230,
'𐨸' => 230,
'𐨹' => 1,
'𐨺' => 220,
'𐨿' => 9,
'𐫥' => 230,
'𐫦' => 220,
'𐴤' => 230,
'𐴥' => 230,
'𐴦' => 230,
'𐴧' => 230,
'𐺫' => 230,
'𐺬' => 230,
'𐽆' => 220,
'𐽇' => 220,
'𐽈' => 230,
'𐽉' => 230,
'𐽊' => 230,
'𐽋' => 220,
'𐽌' => 230,
'𐽍' => 220,
'𐽎' => 220,
'𐽏' => 220,
'𐽐' => 220,
'𑁆' => 9,
'𑁿' => 9,
'𑂹' => 9,
'𑂺' => 7,
'𑄀' => 230,
'𑄁' => 230,
'𑄂' => 230,
'𑄳' => 9,
'𑄴' => 9,
'𑅳' => 7,
'𑇀' => 9,
'𑇊' => 7,
'𑈵' => 9,
'𑈶' => 7,
'𑋩' => 7,
'𑋪' => 9,
'𑌻' => 7,
'𑌼' => 7,
'𑍍' => 9,
'𑍦' => 230,
'𑍧' => 230,
'𑍨' => 230,
'𑍩' => 230,
'𑍪' => 230,
'𑍫' => 230,
'𑍬' => 230,
'𑍰' => 230,
'𑍱' => 230,
'𑍲' => 230,
'𑍳' => 230,
'𑍴' => 230,
'𑑂' => 9,
'𑑆' => 7,
'𑑞' => 230,
'𑓂' => 9,
'𑓃' => 7,
'𑖿' => 9,
'𑗀' => 7,
'𑘿' => 9,
'𑚶' => 9,
'𑚷' => 7,
'𑜫' => 9,
'𑠹' => 9,
'𑠺' => 7,
'𑤽' => 9,
'𑤾' => 9,
'𑥃' => 7,
'𑧠' => 9,
'𑨴' => 9,
'𑩇' => 9,
'𑪙' => 9,
'𑰿' => 9,
'𑵂' => 7,
'𑵄' => 9,
'𑵅' => 9,
'𑶗' => 9,
'𖫰' => 1,
'𖫱' => 1,
'𖫲' => 1,
'𖫳' => 1,
'𖫴' => 1,
'𖬰' => 230,
'𖬱' => 230,
'𖬲' => 230,
'𖬳' => 230,
'𖬴' => 230,
'𖬵' => 230,
'𖬶' => 230,
'𖿰' => 6,
'𖿱' => 6,
'𛲞' => 1,
'𝅥' => 216,
'𝅦' => 216,
'𝅧' => 1,
'𝅨' => 1,
'𝅩' => 1,
'𝅭' => 226,
'𝅮' => 216,
'𝅯' => 216,
'𝅰' => 216,
'𝅱' => 216,
'𝅲' => 216,
'𝅻' => 220,
'𝅼' => 220,
'𝅽' => 220,
'𝅾' => 220,
'𝅿' => 220,
'𝆀' => 220,
'𝆁' => 220,
'𝆂' => 220,
'𝆅' => 230,
'𝆆' => 230,
'𝆇' => 230,
'𝆈' => 230,
'𝆉' => 230,
'𝆊' => 220,
'𝆋' => 220,
'𝆪' => 230,
'𝆫' => 230,
'𝆬' => 230,
'𝆭' => 230,
'𝉂' => 230,
'𝉃' => 230,
'𝉄' => 230,
'𞀀' => 230,
'𞀁' => 230,
'𞀂' => 230,
'𞀃' => 230,
'𞀄' => 230,
'𞀅' => 230,
'𞀆' => 230,
'𞀈' => 230,
'𞀉' => 230,
'𞀊' => 230,
'𞀋' => 230,
'𞀌' => 230,
'𞀍' => 230,
'𞀎' => 230,
'𞀏' => 230,
'𞀐' => 230,
'𞀑' => 230,
'𞀒' => 230,
'𞀓' => 230,
'𞀔' => 230,
'𞀕' => 230,
'𞀖' => 230,
'𞀗' => 230,
'𞀘' => 230,
'𞀛' => 230,
'𞀜' => 230,
'𞀝' => 230,
'𞀞' => 230,
'𞀟' => 230,
'𞀠' => 230,
'𞀡' => 230,
'𞀣' => 230,
'𞀤' => 230,
'𞀦' => 230,
'𞀧' => 230,
'𞀨' => 230,
'𞀩' => 230,
'𞀪' => 230,
'𞄰' => 230,
'𞄱' => 230,
'𞄲' => 230,
'𞄳' => 230,
'𞄴' => 230,
'𞄵' => 230,
'𞄶' => 230,
'𞋬' => 230,
'𞋭' => 230,
'𞋮' => 230,
'𞋯' => 230,
'𞣐' => 220,
'𞣑' => 220,
'𞣒' => 220,
'𞣓' => 220,
'𞣔' => 220,
'𞣕' => 220,
'𞣖' => 220,
'𞥄' => 230,
'𞥅' => 230,
'𞥆' => 230,
'𞥇' => 230,
'𞥈' => 230,
'𞥉' => 230,
'𞥊' => 7,
);
<?php

return array (
' ' => ' ',
'¨' => ' ̈',
'ª' => 'a',
'¯' => ' ̄',
'²' => '2',
'³' => '3',
'´' => ' ́',
'µ' => 'μ',
'¸' => ' ̧',
'¹' => '1',
'º' => 'o',
'¼' => '1⁄4',
'½' => '1⁄2',
'¾' => '3⁄4',
'Ĳ' => 'IJ',
'ĳ' => 'ij',
'Ŀ' => 'L·',
'ŀ' => 'l·',
'ŉ' => 'ʼn',
'ſ' => 's',
'Ǆ' => 'DŽ',
'ǅ' => 'Dž',
'ǆ' => 'dž',
'Ǉ' => 'LJ',
'ǈ' => 'Lj',
'ǉ' => 'lj',
'Ǌ' => 'NJ',
'ǋ' => 'Nj',
'ǌ' => 'nj',
'Ǳ' => 'DZ',
'ǲ' => 'Dz',
'ǳ' => 'dz',
'ʰ' => 'h',
'ʱ' => 'ɦ',
'ʲ' => 'j',
'ʳ' => 'r',
'ʴ' => 'ɹ',
'ʵ' => 'ɻ',
'ʶ' => 'ʁ',
'ʷ' => 'w',
'ʸ' => 'y',
'˘' => ' ̆',
'˙' => ' ̇',
'˚' => ' ̊',
'˛' => ' ̨',
'˜' => ' ̃',
'˝' => ' ̋',
'ˠ' => 'ɣ',
'ˡ' => 'l',
'ˢ' => 's',
'ˣ' => 'x',
'ˤ' => 'ʕ',
'ͺ' => ' ͅ',
'΄' => ' ́',
'΅' => ' ̈́',
'ϐ' => 'β',
'ϑ' => 'θ',
'ϒ' => 'Υ',
'ϓ' => 'Ύ',
'ϔ' => 'Ϋ',
'ϕ' => 'φ',
'ϖ' => 'π',
'ϰ' => 'κ',
'ϱ' => 'ρ',
'ϲ' => 'ς',
'ϴ' => 'Θ',
'ϵ' => 'ε',
'Ϲ' => 'Σ',
'և' => 'եւ',
'ٵ' => 'اٴ',
'ٶ' => 'وٴ',
'ٷ' => 'ۇٴ',
'ٸ' => 'يٴ',
'ำ' => 'ํา',
'ຳ' => 'ໍາ',
'ໜ' => 'ຫນ',
'ໝ' => 'ຫມ',
'༌' => '་',
'ཷ' => 'ྲཱྀ',
'ཹ' => 'ླཱྀ',
'ჼ' => 'ნ',
'ᴬ' => 'A',
'ᴭ' => 'Æ',
'ᴮ' => 'B',
'ᴰ' => 'D',
'ᴱ' => 'E',
'ᴲ' => 'Ǝ',
'ᴳ' => 'G',
'ᴴ' => 'H',
'ᴵ' => 'I',
'ᴶ' => 'J',
'ᴷ' => 'K',
'ᴸ' => 'L',
'ᴹ' => 'M',
'ᴺ' => 'N',
'ᴼ' => 'O',
'ᴽ' => 'Ȣ',
'ᴾ' => 'P',
'ᴿ' => 'R',
'ᵀ' => 'T',
'ᵁ' => 'U',
'ᵂ' => 'W',
'ᵃ' => 'a',
'ᵄ' => 'ɐ',
'ᵅ' => 'ɑ',
'ᵆ' => 'ᴂ',
'ᵇ' => 'b',
'ᵈ' => 'd',
'ᵉ' => 'e',
'ᵊ' => 'ə',
'ᵋ' => 'ɛ',
'ᵌ' => 'ɜ',
'ᵍ' => 'g',
'ᵏ' => 'k',
'ᵐ' => 'm',
'ᵑ' => 'ŋ',
'ᵒ' => 'o',
'ᵓ' => 'ɔ',
'ᵔ' => 'ᴖ',
'ᵕ' => 'ᴗ',
'ᵖ' => 'p',
'ᵗ' => 't',
'ᵘ' => 'u',
'ᵙ' => 'ᴝ',
'ᵚ' => 'ɯ',
'ᵛ' => 'v',
'ᵜ' => 'ᴥ',
'ᵝ' => 'β',
'ᵞ' => 'γ',
'ᵟ' => 'δ',
'ᵠ' => 'φ',
'ᵡ' => 'χ',
'ᵢ' => 'i',
'ᵣ' => 'r',
'ᵤ' => 'u',
'ᵥ' => 'v',
'ᵦ' => 'β',
'ᵧ' => 'γ',
'ᵨ' => 'ρ',
'ᵩ' => 'φ',
'ᵪ' => 'χ',
'ᵸ' => 'н',
'ᶛ' => 'ɒ',
'ᶜ' => 'c',
'ᶝ' => 'ɕ',
'ᶞ' => 'ð',
'ᶟ' => 'ɜ',
'ᶠ' => 'f',
'ᶡ' => 'ɟ',
'ᶢ' => 'ɡ',
'ᶣ' => 'ɥ',
'ᶤ' => 'ɨ',
'ᶥ' => 'ɩ',
'ᶦ' => 'ɪ',
'ᶧ' => 'ᵻ',
'ᶨ' => 'ʝ',
'ᶩ' => 'ɭ',
'ᶪ' => 'ᶅ',
'ᶫ' => 'ʟ',
'ᶬ' => 'ɱ',
'ᶭ' => 'ɰ',
'ᶮ' => 'ɲ',
'ᶯ' => 'ɳ',
'ᶰ' => 'ɴ',
'ᶱ' => 'ɵ',
'ᶲ' => 'ɸ',
'ᶳ' => 'ʂ',
'ᶴ' => 'ʃ',
'ᶵ' => 'ƫ',
'ᶶ' => 'ʉ',
'ᶷ' => 'ʊ',
'ᶸ' => 'ᴜ',
'ᶹ' => 'ʋ',
'ᶺ' => 'ʌ',
'ᶻ' => 'z',
'ᶼ' => 'ʐ',
'ᶽ' => 'ʑ',
'ᶾ' => 'ʒ',
'ᶿ' => 'θ',
'ẚ' => 'aʾ',
'ẛ' => 'ṡ',
'᾽' => ' ̓',
'᾿' => ' ̓',
'῀' => ' ͂',
'῁' => ' ̈͂',
'῍' => ' ̓̀',
'῎' => ' ̓́',
'῏' => ' ̓͂',
'῝' => ' ̔̀',
'῞' => ' ̔́',
'῟' => ' ̔͂',
'῭' => ' ̈̀',
'΅' => ' ̈́',
'´' => ' ́',
'῾' => ' ̔',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
'‑' => '‐',
'‗' => ' ̳',
'․' => '.',
'‥' => '..',
'…' => '...',
' ' => ' ',
'″' => '′′',
'‴' => '′′′',
'‶' => '‵‵',
'‷' => '‵‵‵',
'‼' => '!!',
'‾' => ' ̅',
'⁇' => '??',
'⁈' => '?!',
'⁉' => '!?',
'⁗' => '′′′′',
' ' => ' ',
'⁰' => '0',
'ⁱ' => 'i',
'⁴' => '4',
'⁵' => '5',
'⁶' => '6',
'⁷' => '7',
'⁸' => '8',
'⁹' => '9',
'⁺' => '+',
'⁻' => '−',
'⁼' => '=',
'⁽' => '(',
'⁾' => ')',
'ⁿ' => 'n',
'₀' => '0',
'₁' => '1',
'₂' => '2',
'₃' => '3',
'₄' => '4',
'₅' => '5',
'₆' => '6',
'₇' => '7',
'₈' => '8',
'₉' => '9',
'₊' => '+',
'₋' => '−',
'₌' => '=',
'₍' => '(',
'₎' => ')',
'ₐ' => 'a',
'ₑ' => 'e',
'ₒ' => 'o',
'ₓ' => 'x',
'ₔ' => 'ə',
'ₕ' => 'h',
'ₖ' => 'k',
'ₗ' => 'l',
'ₘ' => 'm',
'ₙ' => 'n',
'ₚ' => 'p',
'ₛ' => 's',
'ₜ' => 't',
'₨' => 'Rs',
'℀' => 'a/c',
'℁' => 'a/s',
'ℂ' => 'C',
'℃' => '°C',
'℅' => 'c/o',
'℆' => 'c/u',
'ℇ' => 'Ɛ',
'℉' => '°F',
'ℊ' => 'g',
'ℋ' => 'H',
'ℌ' => 'H',
'ℍ' => 'H',
'ℎ' => 'h',
'ℏ' => 'ħ',
'ℐ' => 'I',
'ℑ' => 'I',
'ℒ' => 'L',
'ℓ' => 'l',
'ℕ' => 'N',
'№' => 'No',
'ℙ' => 'P',
'ℚ' => 'Q',
'ℛ' => 'R',
'ℜ' => 'R',
'ℝ' => 'R',
'℠' => 'SM',
'℡' => 'TEL',
'™' => 'TM',
'ℤ' => 'Z',
'ℨ' => 'Z',
'ℬ' => 'B',
'ℭ' => 'C',
'ℯ' => 'e',
'ℰ' => 'E',
'ℱ' => 'F',
'ℳ' => 'M',
'ℴ' => 'o',
'ℵ' => 'א',
'ℶ' => 'ב',
'ℷ' => 'ג',
'ℸ' => 'ד',
'ℹ' => 'i',
'℻' => 'FAX',
'ℼ' => 'π',
'ℽ' => 'γ',
'ℾ' => 'Γ',
'ℿ' => 'Π',
'⅀' => '∑',
'ⅅ' => 'D',
'ⅆ' => 'd',
'ⅇ' => 'e',
'ⅈ' => 'i',
'ⅉ' => 'j',
'⅐' => '1⁄7',
'⅑' => '1⁄9',
'⅒' => '1⁄10',
'⅓' => '1⁄3',
'⅔' => '2⁄3',
'⅕' => '1⁄5',
'⅖' => '2⁄5',
'⅗' => '3⁄5',
'⅘' => '4⁄5',
'⅙' => '1⁄6',
'⅚' => '5⁄6',
'⅛' => '1⁄8',
'⅜' => '3⁄8',
'⅝' => '5⁄8',
'⅞' => '7⁄8',
'⅟' => '1⁄',
'Ⅰ' => 'I',
'Ⅱ' => 'II',
'Ⅲ' => 'III',
'Ⅳ' => 'IV',
'Ⅴ' => 'V',
'Ⅵ' => 'VI',
'Ⅶ' => 'VII',
'Ⅷ' => 'VIII',
'Ⅸ' => 'IX',
'Ⅹ' => 'X',
'Ⅺ' => 'XI',
'Ⅻ' => 'XII',
'Ⅼ' => 'L',
'Ⅽ' => 'C',
'Ⅾ' => 'D',
'Ⅿ' => 'M',
'ⅰ' => 'i',
'ⅱ' => 'ii',
'ⅲ' => 'iii',
'ⅳ' => 'iv',
'ⅴ' => 'v',
'ⅵ' => 'vi',
'ⅶ' => 'vii',
'ⅷ' => 'viii',
'ⅸ' => 'ix',
'ⅹ' => 'x',
'ⅺ' => 'xi',
'ⅻ' => 'xii',
'ⅼ' => 'l',
'ⅽ' => 'c',
'ⅾ' => 'd',
'ⅿ' => 'm',
'↉' => '0⁄3',
'∬' => '∫∫',
'∭' => '∫∫∫',
'∯' => '∮∮',
'∰' => '∮∮∮',
'①' => '1',
'②' => '2',
'③' => '3',
'④' => '4',
'⑤' => '5',
'⑥' => '6',
'⑦' => '7',
'⑧' => '8',
'⑨' => '9',
'⑩' => '10',
'⑪' => '11',
'⑫' => '12',
'⑬' => '13',
'⑭' => '14',
'⑮' => '15',
'⑯' => '16',
'⑰' => '17',
'⑱' => '18',
'⑲' => '19',
'⑳' => '20',
'⑴' => '(1)',
'⑵' => '(2)',
'⑶' => '(3)',
'⑷' => '(4)',
'⑸' => '(5)',
'⑹' => '(6)',
'⑺' => '(7)',
'⑻' => '(8)',
'⑼' => '(9)',
'⑽' => '(10)',
'⑾' => '(11)',
'⑿' => '(12)',
'⒀' => '(13)',
'⒁' => '(14)',
'⒂' => '(15)',
'⒃' => '(16)',
'⒄' => '(17)',
'⒅' => '(18)',
'⒆' => '(19)',
'⒇' => '(20)',
'⒈' => '1.',
'⒉' => '2.',
'⒊' => '3.',
'⒋' => '4.',
'⒌' => '5.',
'⒍' => '6.',
'⒎' => '7.',
'⒏' => '8.',
'⒐' => '9.',
'⒑' => '10.',
'⒒' => '11.',
'⒓' => '12.',
'⒔' => '13.',
'⒕' => '14.',
'⒖' => '15.',
'⒗' => '16.',
'⒘' => '17.',
'⒙' => '18.',
'⒚' => '19.',
'⒛' => '20.',
'⒜' => '(a)',
'⒝' => '(b)',
'⒞' => '(c)',
'⒟' => '(d)',
'⒠' => '(e)',
'⒡' => '(f)',
'⒢' => '(g)',
'⒣' => '(h)',
'⒤' => '(i)',
'⒥' => '(j)',
'⒦' => '(k)',
'⒧' => '(l)',
'⒨' => '(m)',
'⒩' => '(n)',
'⒪' => '(o)',
'⒫' => '(p)',
'⒬' => '(q)',
'⒭' => '(r)',
'⒮' => '(s)',
'⒯' => '(t)',
'⒰' => '(u)',
'⒱' => '(v)',
'⒲' => '(w)',
'⒳' => '(x)',
'⒴' => '(y)',
'⒵' => '(z)',
'Ⓐ' => 'A',
'Ⓑ' => 'B',
'Ⓒ' => 'C',
'Ⓓ' => 'D',
'Ⓔ' => 'E',
'Ⓕ' => 'F',
'Ⓖ' => 'G',
'Ⓗ' => 'H',
'Ⓘ' => 'I',
'Ⓙ' => 'J',
'Ⓚ' => 'K',
'Ⓛ' => 'L',
'Ⓜ' => 'M',
'Ⓝ' => 'N',
'Ⓞ' => 'O',
'Ⓟ' => 'P',
'Ⓠ' => 'Q',
'Ⓡ' => 'R',
'Ⓢ' => 'S',
'Ⓣ' => 'T',
'Ⓤ' => 'U',
'Ⓥ' => 'V',
'Ⓦ' => 'W',
'Ⓧ' => 'X',
'Ⓨ' => 'Y',
'Ⓩ' => 'Z',
'ⓐ' => 'a',
'ⓑ' => 'b',
'ⓒ' => 'c',
'ⓓ' => 'd',
'ⓔ' => 'e',
'ⓕ' => 'f',
'ⓖ' => 'g',
'ⓗ' => 'h',
'ⓘ' => 'i',
'ⓙ' => 'j',
'ⓚ' => 'k',
'ⓛ' => 'l',
'ⓜ' => 'm',
'ⓝ' => 'n',
'ⓞ' => 'o',
'ⓟ' => 'p',
'ⓠ' => 'q',
'ⓡ' => 'r',
'ⓢ' => 's',
'ⓣ' => 't',
'ⓤ' => 'u',
'ⓥ' => 'v',
'ⓦ' => 'w',
'ⓧ' => 'x',
'ⓨ' => 'y',
'ⓩ' => 'z',
'⓪' => '0',
'⨌' => '∫∫∫∫',
'⩴' => '::=',
'⩵' => '==',
'⩶' => '===',
'ⱼ' => 'j',
'ⱽ' => 'V',
'ⵯ' => 'ⵡ',
'⺟' => '母',
'⻳' => '龟',
'⼀' => '一',
'⼁' => '丨',
'⼂' => '丶',
'⼃' => '丿',
'⼄' => '乙',
'⼅' => '亅',
'⼆' => '二',
'⼇' => '亠',
'⼈' => '人',
'⼉' => '儿',
'⼊' => '入',
'⼋' => '八',
'⼌' => '冂',
'⼍' => '冖',
'⼎' => '冫',
'⼏' => '几',
'⼐' => '凵',
'⼑' => '刀',
'⼒' => '力',
'⼓' => '勹',
'⼔' => '匕',
'⼕' => '匚',
'⼖' => '匸',
'⼗' => '十',
'⼘' => '卜',
'⼙' => '卩',
'⼚' => '厂',
'⼛' => '厶',
'⼜' => '又',
'⼝' => '口',
'⼞' => '囗',
'⼟' => '土',
'⼠' => '士',
'⼡' => '夂',
'⼢' => '夊',
'⼣' => '夕',
'⼤' => '大',
'⼥' => '女',
'⼦' => '子',
'⼧' => '宀',
'⼨' => '寸',
'⼩' => '小',
'⼪' => '尢',
'⼫' => '尸',
'⼬' => '屮',
'⼭' => '山',
'⼮' => '巛',
'⼯' => '工',
'⼰' => '己',
'⼱' => '巾',
'⼲' => '干',
'⼳' => '幺',
'⼴' => '广',
'⼵' => '廴',
'⼶' => '廾',
'⼷' => '弋',
'⼸' => '弓',
'⼹' => '彐',
'⼺' => '彡',
'⼻' => '彳',
'⼼' => '心',
'⼽' => '戈',
'⼾' => '戶',
'⼿' => '手',
'⽀' => '支',
'⽁' => '攴',
'⽂' => '文',
'⽃' => '斗',
'⽄' => '斤',
'⽅' => '方',
'⽆' => '无',
'⽇' => '日',
'⽈' => '曰',
'⽉' => '月',
'⽊' => '木',
'⽋' => '欠',
'⽌' => '止',
'⽍' => '歹',
'⽎' => '殳',
'⽏' => '毋',
'⽐' => '比',
'⽑' => '毛',
'⽒' => '氏',
'⽓' => '气',
'⽔' => '水',
'⽕' => '火',
'⽖' => '爪',
'⽗' => '父',
'⽘' => '爻',
'⽙' => '爿',
'⽚' => '片',
'⽛' => '牙',
'⽜' => '牛',
'⽝' => '犬',
'⽞' => '玄',
'⽟' => '玉',
'⽠' => '瓜',
'⽡' => '瓦',
'⽢' => '甘',
'⽣' => '生',
'⽤' => '用',
'⽥' => '田',
'⽦' => '疋',
'⽧' => '疒',
'⽨' => '癶',
'⽩' => '白',
'⽪' => '皮',
'⽫' => '皿',
'⽬' => '目',
'⽭' => '矛',
'⽮' => '矢',
'⽯' => '石',
'⽰' => '示',
'⽱' => '禸',
'⽲' => '禾',
'⽳' => '穴',
'⽴' => '立',
'⽵' => '竹',
'⽶' => '米',
'⽷' => '糸',
'⽸' => '缶',
'⽹' => '网',
'⽺' => '羊',
'⽻' => '羽',
'⽼' => '老',
'⽽' => '而',
'⽾' => '耒',
'⽿' => '耳',
'⾀' => '聿',
'⾁' => '肉',
'⾂' => '臣',
'⾃' => '自',
'⾄' => '至',
'⾅' => '臼',
'⾆' => '舌',
'⾇' => '舛',
'⾈' => '舟',
'⾉' => '艮',
'⾊' => '色',
'⾋' => '艸',
'⾌' => '虍',
'⾍' => '虫',
'⾎' => '血',
'⾏' => '行',
'⾐' => '衣',
'⾑' => '襾',
'⾒' => '見',
'⾓' => '角',
'⾔' => '言',
'⾕' => '谷',
'⾖' => '豆',
'⾗' => '豕',
'⾘' => '豸',
'⾙' => '貝',
'⾚' => '赤',
'⾛' => '走',
'⾜' => '足',
'⾝' => '身',
'⾞' => '車',
'⾟' => '辛',
'⾠' => '辰',
'⾡' => '辵',
'⾢' => '邑',
'⾣' => '酉',
'⾤' => '釆',
'⾥' => '里',
'⾦' => '金',
'⾧' => '長',
'⾨' => '門',
'⾩' => '阜',
'⾪' => '隶',
'⾫' => '隹',
'⾬' => '雨',
'⾭' => '靑',
'⾮' => '非',
'⾯' => '面',
'⾰' => '革',
'⾱' => '韋',
'⾲' => '韭',
'⾳' => '音',
'⾴' => '頁',
'⾵' => '風',
'⾶' => '飛',
'⾷' => '食',
'⾸' => '首',
'⾹' => '香',
'⾺' => '馬',
'⾻' => '骨',
'⾼' => '高',
'⾽' => '髟',
'⾾' => '鬥',
'⾿' => '鬯',
'⿀' => '鬲',
'⿁' => '鬼',
'⿂' => '魚',
'⿃' => '鳥',
'⿄' => '鹵',
'⿅' => '鹿',
'⿆' => '麥',
'⿇' => '麻',
'⿈' => '黃',
'⿉' => '黍',
'⿊' => '黑',
'⿋' => '黹',
'⿌' => '黽',
'⿍' => '鼎',
'⿎' => '鼓',
'⿏' => '鼠',
'⿐' => '鼻',
'⿑' => '齊',
'⿒' => '齒',
'⿓' => '龍',
'⿔' => '龜',
'⿕' => '龠',
'　' => ' ',
'〶' => '〒',
'〸' => '十',
'〹' => '卄',
'〺' => '卅',
'゛' => ' ゙',
'゜' => ' ゚',
'ゟ' => 'より',
'ヿ' => 'コト',
'ㄱ' => 'ᄀ',
'ㄲ' => 'ᄁ',
'ㄳ' => 'ᆪ',
'ㄴ' => 'ᄂ',
'ㄵ' => 'ᆬ',
'ㄶ' => 'ᆭ',
'ㄷ' => 'ᄃ',
'ㄸ' => 'ᄄ',
'ㄹ' => 'ᄅ',
'ㄺ' => 'ᆰ',
'ㄻ' => 'ᆱ',
'ㄼ' => 'ᆲ',
'ㄽ' => 'ᆳ',
'ㄾ' => 'ᆴ',
'ㄿ' => 'ᆵ',
'ㅀ' => 'ᄚ',
'ㅁ' => 'ᄆ',
'ㅂ' => 'ᄇ',
'ㅃ' => 'ᄈ',
'ㅄ' => 'ᄡ',
'ㅅ' => 'ᄉ',
'ㅆ' => 'ᄊ',
'ㅇ' => 'ᄋ',
'ㅈ' => 'ᄌ',
'ㅉ' => 'ᄍ',
'ㅊ' => 'ᄎ',
'ㅋ' => 'ᄏ',
'ㅌ' => 'ᄐ',
'ㅍ' => 'ᄑ',
'ㅎ' => 'ᄒ',
'ㅏ' => 'ᅡ',
'ㅐ' => 'ᅢ',
'ㅑ' => 'ᅣ',
'ㅒ' => 'ᅤ',
'ㅓ' => 'ᅥ',
'ㅔ' => 'ᅦ',
'ㅕ' => 'ᅧ',
'ㅖ' => 'ᅨ',
'ㅗ' => 'ᅩ',
'ㅘ' => 'ᅪ',
'ㅙ' => 'ᅫ',
'ㅚ' => 'ᅬ',
'ㅛ' => 'ᅭ',
'ㅜ' => 'ᅮ',
'ㅝ' => 'ᅯ',
'ㅞ' => 'ᅰ',
'ㅟ' => 'ᅱ',
'ㅠ' => 'ᅲ',
'ㅡ' => 'ᅳ',
'ㅢ' => 'ᅴ',
'ㅣ' => 'ᅵ',
'ㅤ' => 'ᅠ',
'ㅥ' => 'ᄔ',
'ㅦ' => 'ᄕ',
'ㅧ' => 'ᇇ',
'ㅨ' => 'ᇈ',
'ㅩ' => 'ᇌ',
'ㅪ' => 'ᇎ',
'ㅫ' => 'ᇓ',
'ㅬ' => 'ᇗ',
'ㅭ' => 'ᇙ',
'ㅮ' => 'ᄜ',
'ㅯ' => 'ᇝ',
'ㅰ' => 'ᇟ',
'ㅱ' => 'ᄝ',
'ㅲ' => 'ᄞ',
'ㅳ' => 'ᄠ',
'ㅴ' => 'ᄢ',
'ㅵ' => 'ᄣ',
'ㅶ' => 'ᄧ',
'ㅷ' => 'ᄩ',
'ㅸ' => 'ᄫ',
'ㅹ' => 'ᄬ',
'ㅺ' => 'ᄭ',
'ㅻ' => 'ᄮ',
'ㅼ' => 'ᄯ',
'ㅽ' => 'ᄲ',
'ㅾ' => 'ᄶ',
'ㅿ' => 'ᅀ',
'ㆀ' => 'ᅇ',
'ㆁ' => 'ᅌ',
'ㆂ' => 'ᇱ',
'ㆃ' => 'ᇲ',
'ㆄ' => 'ᅗ',
'ㆅ' => 'ᅘ',
'ㆆ' => 'ᅙ',
'ㆇ' => 'ᆄ',
'ㆈ' => 'ᆅ',
'ㆉ' => 'ᆈ',
'ㆊ' => 'ᆑ',
'ㆋ' => 'ᆒ',
'ㆌ' => 'ᆔ',
'ㆍ' => 'ᆞ',
'ㆎ' => 'ᆡ',
'㆒' => '一',
'㆓' => '二',
'㆔' => '三',
'㆕' => '四',
'㆖' => '上',
'㆗' => '中',
'㆘' => '下',
'㆙' => '甲',
'㆚' => '乙',
'㆛' => '丙',
'㆜' => '丁',
'㆝' => '天',
'㆞' => '地',
'㆟' => '人',
'㈀' => '(ᄀ)',
'㈁' => '(ᄂ)',
'㈂' => '(ᄃ)',
'㈃' => '(ᄅ)',
'㈄' => '(ᄆ)',
'㈅' => '(ᄇ)',
'㈆' => '(ᄉ)',
'㈇' => '(ᄋ)',
'㈈' => '(ᄌ)',
'㈉' => '(ᄎ)',
'㈊' => '(ᄏ)',
'㈋' => '(ᄐ)',
'㈌' => '(ᄑ)',
'㈍' => '(ᄒ)',
'㈎' => '(가)',
'㈏' => '(나)',
'㈐' => '(다)',
'㈑' => '(라)',
'㈒' => '(마)',
'㈓' => '(바)',
'㈔' => '(사)',
'㈕' => '(아)',
'㈖' => '(자)',
'㈗' => '(차)',
'㈘' => '(카)',
'㈙' => '(타)',
'㈚' => '(파)',
'㈛' => '(하)',
'㈜' => '(주)',
'㈝' => '(오전)',
'㈞' => '(오후)',
'㈠' => '(一)',
'㈡' => '(二)',
'㈢' => '(三)',
'㈣' => '(四)',
'㈤' => '(五)',
'㈥' => '(六)',
'㈦' => '(七)',
'㈧' => '(八)',
'㈨' => '(九)',
'㈩' => '(十)',
'㈪' => '(月)',
'㈫' => '(火)',
'㈬' => '(水)',
'㈭' => '(木)',
'㈮' => '(金)',
'㈯' => '(土)',
'㈰' => '(日)',
'㈱' => '(株)',
'㈲' => '(有)',
'㈳' => '(社)',
'㈴' => '(名)',
'㈵' => '(特)',
'㈶' => '(財)',
'㈷' => '(祝)',
'㈸' => '(労)',
'㈹' => '(代)',
'㈺' => '(呼)',
'㈻' => '(学)',
'㈼' => '(監)',
'㈽' => '(企)',
'㈾' => '(資)',
'㈿' => '(協)',
'㉀' => '(祭)',
'㉁' => '(休)',
'㉂' => '(自)',
'㉃' => '(至)',
'㉄' => '問',
'㉅' => '幼',
'㉆' => '文',
'㉇' => '箏',
'㉐' => 'PTE',
'㉑' => '21',
'㉒' => '22',
'㉓' => '23',
'㉔' => '24',
'㉕' => '25',
'㉖' => '26',
'㉗' => '27',
'㉘' => '28',
'㉙' => '29',
'㉚' => '30',
'㉛' => '31',
'㉜' => '32',
'㉝' => '33',
'㉞' => '34',
'㉟' => '35',
'㉠' => 'ᄀ',
'㉡' => 'ᄂ',
'㉢' => 'ᄃ',
'㉣' => 'ᄅ',
'㉤' => 'ᄆ',
'㉥' => 'ᄇ',
'㉦' => 'ᄉ',
'㉧' => 'ᄋ',
'㉨' => 'ᄌ',
'㉩' => 'ᄎ',
'㉪' => 'ᄏ',
'㉫' => 'ᄐ',
'㉬' => 'ᄑ',
'㉭' => 'ᄒ',
'㉮' => '가',
'㉯' => '나',
'㉰' => '다',
'㉱' => '라',
'㉲' => '마',
'㉳' => '바',
'㉴' => '사',
'㉵' => '아',
'㉶' => '자',
'㉷' => '차',
'㉸' => '카',
'㉹' => '타',
'㉺' => '파',
'㉻' => '하',
'㉼' => '참고',
'㉽' => '주의',
'㉾' => '우',
'㊀' => '一',
'㊁' => '二',
'㊂' => '三',
'㊃' => '四',
'㊄' => '五',
'㊅' => '六',
'㊆' => '七',
'㊇' => '八',
'㊈' => '九',
'㊉' => '十',
'㊊' => '月',
'㊋' => '火',
'㊌' => '水',
'㊍' => '木',
'㊎' => '金',
'㊏' => '土',
'㊐' => '日',
'㊑' => '株',
'㊒' => '有',
'㊓' => '社',
'㊔' => '名',
'㊕' => '特',
'㊖' => '財',
'㊗' => '祝',
'㊘' => '労',
'㊙' => '秘',
'㊚' => '男',
'㊛' => '女',
'㊜' => '適',
'㊝' => '優',
'㊞' => '印',
'㊟' => '注',
'㊠' => '項',
'㊡' => '休',
'㊢' => '写',
'㊣' => '正',
'㊤' => '上',
'㊥' => '中',
'㊦' => '下',
'㊧' => '左',
'㊨' => '右',
'㊩' => '医',
'㊪' => '宗',
'㊫' => '学',
'㊬' => '監',
'㊭' => '企',
'㊮' => '資',
'㊯' => '協',
'㊰' => '夜',
'㊱' => '36',
'㊲' => '37',
'㊳' => '38',
'㊴' => '39',
'㊵' => '40',
'㊶' => '41',
'㊷' => '42',
'㊸' => '43',
'㊹' => '44',
'㊺' => '45',
'㊻' => '46',
'㊼' => '47',
'㊽' => '48',
'㊾' => '49',
'㊿' => '50',
'㋀' => '1月',
'㋁' => '2月',
'㋂' => '3月',
'㋃' => '4月',
'㋄' => '5月',
'㋅' => '6月',
'㋆' => '7月',
'㋇' => '8月',
'㋈' => '9月',
'㋉' => '10月',
'㋊' => '11月',
'㋋' => '12月',
'㋌' => 'Hg',
'㋍' => 'erg',
'㋎' => 'eV',
'㋏' => 'LTD',
'㋐' => 'ア',
'㋑' => 'イ',
'㋒' => 'ウ',
'㋓' => 'エ',
'㋔' => 'オ',
'㋕' => 'カ',
'㋖' => 'キ',
'㋗' => 'ク',
'㋘' => 'ケ',
'㋙' => 'コ',
'㋚' => 'サ',
'㋛' => 'シ',
'㋜' => 'ス',
'㋝' => 'セ',
'㋞' => 'ソ',
'㋟' => 'タ',
'㋠' => 'チ',
'㋡' => 'ツ',
'㋢' => 'テ',
'㋣' => 'ト',
'㋤' => 'ナ',
'㋥' => 'ニ',
'㋦' => 'ヌ',
'㋧' => 'ネ',
'㋨' => 'ノ',
'㋩' => 'ハ',
'㋪' => 'ヒ',
'㋫' => 'フ',
'㋬' => 'ヘ',
'㋭' => 'ホ',
'㋮' => 'マ',
'㋯' => 'ミ',
'㋰' => 'ム',
'㋱' => 'メ',
'㋲' => 'モ',
'㋳' => 'ヤ',
'㋴' => 'ユ',
'㋵' => 'ヨ',
'㋶' => 'ラ',
'㋷' => 'リ',
'㋸' => 'ル',
'㋹' => 'レ',
'㋺' => 'ロ',
'㋻' => 'ワ',
'㋼' => 'ヰ',
'㋽' => 'ヱ',
'㋾' => 'ヲ',
'㋿' => '令和',
'㌀' => 'アパート',
'㌁' => 'アルファ',
'㌂' => 'アンペア',
'㌃' => 'アール',
'㌄' => 'イニング',
'㌅' => 'インチ',
'㌆' => 'ウォン',
'㌇' => 'エスクード',
'㌈' => 'エーカー',
'㌉' => 'オンス',
'㌊' => 'オーム',
'㌋' => 'カイリ',
'㌌' => 'カラット',
'㌍' => 'カロリー',
'㌎' => 'ガロン',
'㌏' => 'ガンマ',
'㌐' => 'ギガ',
'㌑' => 'ギニー',
'㌒' => 'キュリー',
'㌓' => 'ギルダー',
'㌔' => 'キロ',
'㌕' => 'キログラム',
'㌖' => 'キロメートル',
'㌗' => 'キロワット',
'㌘' => 'グラム',
'㌙' => 'グラムトン',
'㌚' => 'クルゼイロ',
'㌛' => 'クローネ',
'㌜' => 'ケース',
'㌝' => 'コルナ',
'㌞' => 'コーポ',
'㌟' => 'サイクル',
'㌠' => 'サンチーム',
'㌡' => 'シリング',
'㌢' => 'センチ',
'㌣' => 'セント',
'㌤' => 'ダース',
'㌥' => 'デシ',
'㌦' => 'ドル',
'㌧' => 'トン',
'㌨' => 'ナノ',
'㌩' => 'ノット',
'㌪' => 'ハイツ',
'㌫' => 'パーセント',
'㌬' => 'パーツ',
'㌭' => 'バーレル',
'㌮' => 'ピアストル',
'㌯' => 'ピクル',
'㌰' => 'ピコ',
'㌱' => 'ビル',
'㌲' => 'ファラッド',
'㌳' => 'フィート',
'㌴' => 'ブッシェル',
'㌵' => 'フラン',
'㌶' => 'ヘクタール',
'㌷' => 'ペソ',
'㌸' => 'ペニヒ',
'㌹' => 'ヘルツ',
'㌺' => 'ペンス',
'㌻' => 'ページ',
'㌼' => 'ベータ',
'㌽' => 'ポイント',
'㌾' => 'ボルト',
'㌿' => 'ホン',
'㍀' => 'ポンド',
'㍁' => 'ホール',
'㍂' => 'ホーン',
'㍃' => 'マイクロ',
'㍄' => 'マイル',
'㍅' => 'マッハ',
'㍆' => 'マルク',
'㍇' => 'マンション',
'㍈' => 'ミクロン',
'㍉' => 'ミリ',
'㍊' => 'ミリバール',
'㍋' => 'メガ',
'㍌' => 'メガトン',
'㍍' => 'メートル',
'㍎' => 'ヤード',
'㍏' => 'ヤール',
'㍐' => 'ユアン',
'㍑' => 'リットル',
'㍒' => 'リラ',
'㍓' => 'ルピー',
'㍔' => 'ルーブル',
'㍕' => 'レム',
'㍖' => 'レントゲン',
'㍗' => 'ワット',
'㍘' => '0点',
'㍙' => '1点',
'㍚' => '2点',
'㍛' => '3点',
'㍜' => '4点',
'㍝' => '5点',
'㍞' => '6点',
'㍟' => '7点',
'㍠' => '8点',
'㍡' => '9点',
'㍢' => '10点',
'㍣' => '11点',
'㍤' => '12点',
'㍥' => '13点',
'㍦' => '14点',
'㍧' => '15点',
'㍨' => '16点',
'㍩' => '17点',
'㍪' => '18点',
'㍫' => '19点',
'㍬' => '20点',
'㍭' => '21点',
'㍮' => '22点',
'㍯' => '23点',
'㍰' => '24点',
'㍱' => 'hPa',
'㍲' => 'da',
'㍳' => 'AU',
'㍴' => 'bar',
'㍵' => 'oV',
'㍶' => 'pc',
'㍷' => 'dm',
'㍸' => 'dm2',
'㍹' => 'dm3',
'㍺' => 'IU',
'㍻' => '平成',
'㍼' => '昭和',
'㍽' => '大正',
'㍾' => '明治',
'㍿' => '株式会社',
'㎀' => 'pA',
'㎁' => 'nA',
'㎂' => 'μA',
'㎃' => 'mA',
'㎄' => 'kA',
'㎅' => 'KB',
'㎆' => 'MB',
'㎇' => 'GB',
'㎈' => 'cal',
'㎉' => 'kcal',
'㎊' => 'pF',
'㎋' => 'nF',
'㎌' => 'μF',
'㎍' => 'μg',
'㎎' => 'mg',
'㎏' => 'kg',
'㎐' => 'Hz',
'㎑' => 'kHz',
'㎒' => 'MHz',
'㎓' => 'GHz',
'㎔' => 'THz',
'㎕' => 'μl',
'㎖' => 'ml',
'㎗' => 'dl',
'㎘' => 'kl',
'㎙' => 'fm',
'㎚' => 'nm',
'㎛' => 'μm',
'㎜' => 'mm',
'㎝' => 'cm',
'㎞' => 'km',
'㎟' => 'mm2',
'㎠' => 'cm2',
'㎡' => 'm2',
'㎢' => 'km2',
'㎣' => 'mm3',
'㎤' => 'cm3',
'㎥' => 'm3',
'㎦' => 'km3',
'㎧' => 'm∕s',
'㎨' => 'm∕s2',
'㎩' => 'Pa',
'㎪' => 'kPa',
'㎫' => 'MPa',
'㎬' => 'GPa',
'㎭' => 'rad',
'㎮' => 'rad∕s',
'㎯' => 'rad∕s2',
'㎰' => 'ps',
'㎱' => 'ns',
'㎲' => 'μs',
'㎳' => 'ms',
'㎴' => 'pV',
'㎵' => 'nV',
'㎶' => 'μV',
'㎷' => 'mV',
'㎸' => 'kV',
'㎹' => 'MV',
'㎺' => 'pW',
'㎻' => 'nW',
'㎼' => 'μW',
'㎽' => 'mW',
'㎾' => 'kW',
'㎿' => 'MW',
'㏀' => 'kΩ',
'㏁' => 'MΩ',
'㏂' => 'a.m.',
'㏃' => 'Bq',
'㏄' => 'cc',
'㏅' => 'cd',
'㏆' => 'C∕kg',
'㏇' => 'Co.',
'㏈' => 'dB',
'㏉' => 'Gy',
'㏊' => 'ha',
'㏋' => 'HP',
'㏌' => 'in',
'㏍' => 'KK',
'㏎' => 'KM',
'㏏' => 'kt',
'㏐' => 'lm',
'㏑' => 'ln',
'㏒' => 'log',
'㏓' => 'lx',
'㏔' => 'mb',
'㏕' => 'mil',
'㏖' => 'mol',
'㏗' => 'PH',
'㏘' => 'p.m.',
'㏙' => 'PPM',
'㏚' => 'PR',
'㏛' => 'sr',
'㏜' => 'Sv',
'㏝' => 'Wb',
'㏞' => 'V∕m',
'㏟' => 'A∕m',
'㏠' => '1日',
'㏡' => '2日',
'㏢' => '3日',
'㏣' => '4日',
'㏤' => '5日',
'㏥' => '6日',
'㏦' => '7日',
'㏧' => '8日',
'㏨' => '9日',
'㏩' => '10日',
'㏪' => '11日',
'㏫' => '12日',
'㏬' => '13日',
'㏭' => '14日',
'㏮' => '15日',
'㏯' => '16日',
'㏰' => '17日',
'㏱' => '18日',
'㏲' => '19日',
'㏳' => '20日',
'㏴' => '21日',
'㏵' => '22日',
'㏶' => '23日',
'㏷' => '24日',
'㏸' => '25日',
'㏹' => '26日',
'㏺' => '27日',
'㏻' => '28日',
'㏼' => '29日',
'㏽' => '30日',
'㏾' => '31日',
'㏿' => 'gal',
'ꚜ' => 'ъ',
'ꚝ' => 'ь',
'ꝰ' => 'ꝯ',
'ꟸ' => 'Ħ',
'ꟹ' => 'œ',
'ꭜ' => 'ꜧ',
'ꭝ' => 'ꬷ',
'ꭞ' => 'ɫ',
'ꭟ' => 'ꭒ',
'ꭩ' => 'ʍ',
'ﬀ' => 'ff',
'ﬁ' => 'fi',
'ﬂ' => 'fl',
'ﬃ' => 'ffi',
'ﬄ' => 'ffl',
'ﬅ' => 'st',
'ﬆ' => 'st',
'ﬓ' => 'մն',
'ﬔ' => 'մե',
'ﬕ' => 'մի',
'ﬖ' => 'վն',
'ﬗ' => 'մխ',
'ﬠ' => 'ע',
'ﬡ' => 'א',
'ﬢ' => 'ד',
'ﬣ' => 'ה',
'ﬤ' => 'כ',
'ﬥ' => 'ל',
'ﬦ' => 'ם',
'ﬧ' => 'ר',
'ﬨ' => 'ת',
'﬩' => '+',
'ﭏ' => 'אל',
'ﭐ' => 'ٱ',
'ﭑ' => 'ٱ',
'ﭒ' => 'ٻ',
'ﭓ' => 'ٻ',
'ﭔ' => 'ٻ',
'ﭕ' => 'ٻ',
'ﭖ' => 'پ',
'ﭗ' => 'پ',
'ﭘ' => 'پ',
'ﭙ' => 'پ',
'ﭚ' => 'ڀ',
'ﭛ' => 'ڀ',
'ﭜ' => 'ڀ',
'ﭝ' => 'ڀ',
'ﭞ' => 'ٺ',
'ﭟ' => 'ٺ',
'ﭠ' => 'ٺ',
'ﭡ' => 'ٺ',
'ﭢ' => 'ٿ',
'ﭣ' => 'ٿ',
'ﭤ' => 'ٿ',
'ﭥ' => 'ٿ',
'ﭦ' => 'ٹ',
'ﭧ' => 'ٹ',
'ﭨ' => 'ٹ',
'ﭩ' => 'ٹ',
'ﭪ' => 'ڤ',
'ﭫ' => 'ڤ',
'ﭬ' => 'ڤ',
'ﭭ' => 'ڤ',
'ﭮ' => 'ڦ',
'ﭯ' => 'ڦ',
'ﭰ' => 'ڦ',
'ﭱ' => 'ڦ',
'ﭲ' => 'ڄ',
'ﭳ' => 'ڄ',
'ﭴ' => 'ڄ',
'ﭵ' => 'ڄ',
'ﭶ' => 'ڃ',
'ﭷ' => 'ڃ',
'ﭸ' => 'ڃ',
'ﭹ' => 'ڃ',
'ﭺ' => 'چ',
'ﭻ' => 'چ',
'ﭼ' => 'چ',
'ﭽ' => 'چ',
'ﭾ' => 'ڇ',
'ﭿ' => 'ڇ',
'ﮀ' => 'ڇ',
'ﮁ' => 'ڇ',
'ﮂ' => 'ڍ',
'ﮃ' => 'ڍ',
'ﮄ' => 'ڌ',
'ﮅ' => 'ڌ',
'ﮆ' => 'ڎ',
'ﮇ' => 'ڎ',
'ﮈ' => 'ڈ',
'ﮉ' => 'ڈ',
'ﮊ' => 'ژ',
'ﮋ' => 'ژ',
'ﮌ' => 'ڑ',
'ﮍ' => 'ڑ',
'ﮎ' => 'ک',
'ﮏ' => 'ک',
'ﮐ' => 'ک',
'ﮑ' => 'ک',
'ﮒ' => 'گ',
'ﮓ' => 'گ',
'ﮔ' => 'گ',
'ﮕ' => 'گ',
'ﮖ' => 'ڳ',
'ﮗ' => 'ڳ',
'ﮘ' => 'ڳ',
'ﮙ' => 'ڳ',
'ﮚ' => 'ڱ',
'ﮛ' => 'ڱ',
'ﮜ' => 'ڱ',
'ﮝ' => 'ڱ',
'ﮞ' => 'ں',
'ﮟ' => 'ں',
'ﮠ' => 'ڻ',
'ﮡ' => 'ڻ',
'ﮢ' => 'ڻ',
'ﮣ' => 'ڻ',
'ﮤ' => 'ۀ',
'ﮥ' => 'ۀ',
'ﮦ' => 'ہ',
'ﮧ' => 'ہ',
'ﮨ' => 'ہ',
'ﮩ' => 'ہ',
'ﮪ' => 'ھ',
'ﮫ' => 'ھ',
'ﮬ' => 'ھ',
'ﮭ' => 'ھ',
'ﮮ' => 'ے',
'ﮯ' => 'ے',
'ﮰ' => 'ۓ',
'ﮱ' => 'ۓ',
'ﯓ' => 'ڭ',
'ﯔ' => 'ڭ',
'ﯕ' => 'ڭ',
'ﯖ' => 'ڭ',
'ﯗ' => 'ۇ',
'ﯘ' => 'ۇ',
'ﯙ' => 'ۆ',
'ﯚ' => 'ۆ',
'ﯛ' => 'ۈ',
'ﯜ' => 'ۈ',
'ﯝ' => 'ۇٴ',
'ﯞ' => 'ۋ',
'ﯟ' => 'ۋ',
'ﯠ' => 'ۅ',
'ﯡ' => 'ۅ',
'ﯢ' => 'ۉ',
'ﯣ' => 'ۉ',
'ﯤ' => 'ې',
'ﯥ' => 'ې',
'ﯦ' => 'ې',
'ﯧ' => 'ې',
'ﯨ' => 'ى',
'ﯩ' => 'ى',
'ﯪ' => 'ئا',
'ﯫ' => 'ئا',
'ﯬ' => 'ئە',
'ﯭ' => 'ئە',
'ﯮ' => 'ئو',
'ﯯ' => 'ئو',
'ﯰ' => 'ئۇ',
'ﯱ' => 'ئۇ',
'ﯲ' => 'ئۆ',
'ﯳ' => 'ئۆ',
'ﯴ' => 'ئۈ',
'ﯵ' => 'ئۈ',
'ﯶ' => 'ئې',
'ﯷ' => 'ئې',
'ﯸ' => 'ئې',
'ﯹ' => 'ئى',
'ﯺ' => 'ئى',
'ﯻ' => 'ئى',
'ﯼ' => 'ی',
'ﯽ' => 'ی',
'ﯾ' => 'ی',
'ﯿ' => 'ی',
'ﰀ' => 'ئج',
'ﰁ' => 'ئح',
'ﰂ' => 'ئم',
'ﰃ' => 'ئى',
'ﰄ' => 'ئي',
'ﰅ' => 'بج',
'ﰆ' => 'بح',
'ﰇ' => 'بخ',
'ﰈ' => 'بم',
'ﰉ' => 'بى',
'ﰊ' => 'بي',
'ﰋ' => 'تج',
'ﰌ' => 'تح',
'ﰍ' => 'تخ',
'ﰎ' => 'تم',
'ﰏ' => 'تى',
'ﰐ' => 'تي',
'ﰑ' => 'ثج',
'ﰒ' => 'ثم',
'ﰓ' => 'ثى',
'ﰔ' => 'ثي',
'ﰕ' => 'جح',
'ﰖ' => 'جم',
'ﰗ' => 'حج',
'ﰘ' => 'حم',
'ﰙ' => 'خج',
'ﰚ' => 'خح',
'ﰛ' => 'خم',
'ﰜ' => 'سج',
'ﰝ' => 'سح',
'ﰞ' => 'سخ',
'ﰟ' => 'سم',
'ﰠ' => 'صح',
'ﰡ' => 'صم',
'ﰢ' => 'ضج',
'ﰣ' => 'ضح',
'ﰤ' => 'ضخ',
'ﰥ' => 'ضم',
'ﰦ' => 'طح',
'ﰧ' => 'طم',
'ﰨ' => 'ظم',
'ﰩ' => 'عج',
'ﰪ' => 'عم',
'ﰫ' => 'غج',
'ﰬ' => 'غم',
'ﰭ' => 'فج',
'ﰮ' => 'فح',
'ﰯ' => 'فخ',
'ﰰ' => 'فم',
'ﰱ' => 'فى',
'ﰲ' => 'في',
'ﰳ' => 'قح',
'ﰴ' => 'قم',
'ﰵ' => 'قى',
'ﰶ' => 'قي',
'ﰷ' => 'كا',
'ﰸ' => 'كج',
'ﰹ' => 'كح',
'ﰺ' => 'كخ',
'ﰻ' => 'كل',
'ﰼ' => 'كم',
'ﰽ' => 'كى',
'ﰾ' => 'كي',
'ﰿ' => 'لج',
'ﱀ' => 'لح',
'ﱁ' => 'لخ',
'ﱂ' => 'لم',
'ﱃ' => 'لى',
'ﱄ' => 'لي',
'ﱅ' => 'مج',
'ﱆ' => 'مح',
'ﱇ' => 'مخ',
'ﱈ' => 'مم',
'ﱉ' => 'مى',
'ﱊ' => 'مي',
'ﱋ' => 'نج',
'ﱌ' => 'نح',
'ﱍ' => 'نخ',
'ﱎ' => 'نم',
'ﱏ' => 'نى',
'ﱐ' => 'ني',
'ﱑ' => 'هج',
'ﱒ' => 'هم',
'ﱓ' => 'هى',
'ﱔ' => 'هي',
'ﱕ' => 'يج',
'ﱖ' => 'يح',
'ﱗ' => 'يخ',
'ﱘ' => 'يم',
'ﱙ' => 'يى',
'ﱚ' => 'يي',
'ﱛ' => 'ذٰ',
'ﱜ' => 'رٰ',
'ﱝ' => 'ىٰ',
'ﱞ' => ' ٌّ',
'ﱟ' => ' ٍّ',
'ﱠ' => ' َّ',
'ﱡ' => ' ُّ',
'ﱢ' => ' ِّ',
'ﱣ' => ' ّٰ',
'ﱤ' => 'ئر',
'ﱥ' => 'ئز',
'ﱦ' => 'ئم',
'ﱧ' => 'ئن',
'ﱨ' => 'ئى',
'ﱩ' => 'ئي',
'ﱪ' => 'بر',
'ﱫ' => 'بز',
'ﱬ' => 'بم',
'ﱭ' => 'بن',
'ﱮ' => 'بى',
'ﱯ' => 'بي',
'ﱰ' => 'تر',
'ﱱ' => 'تز',
'ﱲ' => 'تم',
'ﱳ' => 'تن',
'ﱴ' => 'تى',
'ﱵ' => 'تي',
'ﱶ' => 'ثر',
'ﱷ' => 'ثز',
'ﱸ' => 'ثم',
'ﱹ' => 'ثن',
'ﱺ' => 'ثى',
'ﱻ' => 'ثي',
'ﱼ' => 'فى',
'ﱽ' => 'في',
'ﱾ' => 'قى',
'ﱿ' => 'قي',
'ﲀ' => 'كا',
'ﲁ' => 'كل',
'ﲂ' => 'كم',
'ﲃ' => 'كى',
'ﲄ' => 'كي',
'ﲅ' => 'لم',
'ﲆ' => 'لى',
'ﲇ' => 'لي',
'ﲈ' => 'ما',
'ﲉ' => 'مم',
'ﲊ' => 'نر',
'ﲋ' => 'نز',
'ﲌ' => 'نم',
'ﲍ' => 'نن',
'ﲎ' => 'نى',
'ﲏ' => 'ني',
'ﲐ' => 'ىٰ',
'ﲑ' => 'ير',
'ﲒ' => 'يز',
'ﲓ' => 'يم',
'ﲔ' => 'ين',
'ﲕ' => 'يى',
'ﲖ' => 'يي',
'ﲗ' => 'ئج',
'ﲘ' => 'ئح',
'ﲙ' => 'ئخ',
'ﲚ' => 'ئم',
'ﲛ' => 'ئه',
'ﲜ' => 'بج',
'ﲝ' => 'بح',
'ﲞ' => 'بخ',
'ﲟ' => 'بم',
'ﲠ' => 'به',
'ﲡ' => 'تج',
'ﲢ' => 'تح',
'ﲣ' => 'تخ',
'ﲤ' => 'تم',
'ﲥ' => 'ته',
'ﲦ' => 'ثم',
'ﲧ' => 'جح',
'ﲨ' => 'جم',
'ﲩ' => 'حج',
'ﲪ' => 'حم',
'ﲫ' => 'خج',
'ﲬ' => 'خم',
'ﲭ' => 'سج',
'ﲮ' => 'سح',
'ﲯ' => 'سخ',
'ﲰ' => 'سم',
'ﲱ' => 'صح',
'ﲲ' => 'صخ',
'ﲳ' => 'صم',
'ﲴ' => 'ضج',
'ﲵ' => 'ضح',
'ﲶ' => 'ضخ',
'ﲷ' => 'ضم',
'ﲸ' => 'طح',
'ﲹ' => 'ظم',
'ﲺ' => 'عج',
'ﲻ' => 'عم',
'ﲼ' => 'غج',
'ﲽ' => 'غم',
'ﲾ' => 'فج',
'ﲿ' => 'فح',
'ﳀ' => 'فخ',
'ﳁ' => 'فم',
'ﳂ' => 'قح',
'ﳃ' => 'قم',
'ﳄ' => 'كج',
'ﳅ' => 'كح',
'ﳆ' => 'كخ',
'ﳇ' => 'كل',
'ﳈ' => 'كم',
'ﳉ' => 'لج',
'ﳊ' => 'لح',
'ﳋ' => 'لخ',
'ﳌ' => 'لم',
'ﳍ' => 'له',
'ﳎ' => 'مج',
'ﳏ' => 'مح',
'ﳐ' => 'مخ',
'ﳑ' => 'مم',
'ﳒ' => 'نج',
'ﳓ' => 'نح',
'ﳔ' => 'نخ',
'ﳕ' => 'نم',
'ﳖ' => 'نه',
'ﳗ' => 'هج',
'ﳘ' => 'هم',
'ﳙ' => 'هٰ',
'ﳚ' => 'يج',
'ﳛ' => 'يح',
'ﳜ' => 'يخ',
'ﳝ' => 'يم',
'ﳞ' => 'يه',
'ﳟ' => 'ئم',
'ﳠ' => 'ئه',
'ﳡ' => 'بم',
'ﳢ' => 'به',
'ﳣ' => 'تم',
'ﳤ' => 'ته',
'ﳥ' => 'ثم',
'ﳦ' => 'ثه',
'ﳧ' => 'سم',
'ﳨ' => 'سه',
'ﳩ' => 'شم',
'ﳪ' => 'شه',
'ﳫ' => 'كل',
'ﳬ' => 'كم',
'ﳭ' => 'لم',
'ﳮ' => 'نم',
'ﳯ' => 'نه',
'ﳰ' => 'يم',
'ﳱ' => 'يه',
'ﳲ' => 'ـَّ',
'ﳳ' => 'ـُّ',
'ﳴ' => 'ـِّ',
'ﳵ' => 'طى',
'ﳶ' => 'طي',
'ﳷ' => 'عى',
'ﳸ' => 'عي',
'ﳹ' => 'غى',
'ﳺ' => 'غي',
'ﳻ' => 'سى',
'ﳼ' => 'سي',
'ﳽ' => 'شى',
'ﳾ' => 'شي',
'ﳿ' => 'حى',
'ﴀ' => 'حي',
'ﴁ' => 'جى',
'ﴂ' => 'جي',
'ﴃ' => 'خى',
'ﴄ' => 'خي',
'ﴅ' => 'صى',
'ﴆ' => 'صي',
'ﴇ' => 'ضى',
'ﴈ' => 'ضي',
'ﴉ' => 'شج',
'ﴊ' => 'شح',
'ﴋ' => 'شخ',
'ﴌ' => 'شم',
'ﴍ' => 'شر',
'ﴎ' => 'سر',
'ﴏ' => 'صر',
'ﴐ' => 'ضر',
'ﴑ' => 'طى',
'ﴒ' => 'طي',
'ﴓ' => 'عى',
'ﴔ' => 'عي',
'ﴕ' => 'غى',
'ﴖ' => 'غي',
'ﴗ' => 'سى',
'ﴘ' => 'سي',
'ﴙ' => 'شى',
'ﴚ' => 'شي',
'ﴛ' => 'حى',
'ﴜ' => 'حي',
'ﴝ' => 'جى',
'ﴞ' => 'جي',
'ﴟ' => 'خى',
'ﴠ' => 'خي',
'ﴡ' => 'صى',
'ﴢ' => 'صي',
'ﴣ' => 'ضى',
'ﴤ' => 'ضي',
'ﴥ' => 'شج',
'ﴦ' => 'شح',
'ﴧ' => 'شخ',
'ﴨ' => 'شم',
'ﴩ' => 'شر',
'ﴪ' => 'سر',
'ﴫ' => 'صر',
'ﴬ' => 'ضر',
'ﴭ' => 'شج',
'ﴮ' => 'شح',
'ﴯ' => 'شخ',
'ﴰ' => 'شم',
'ﴱ' => 'سه',
'ﴲ' => 'شه',
'ﴳ' => 'طم',
'ﴴ' => 'سج',
'ﴵ' => 'سح',
'ﴶ' => 'سخ',
'ﴷ' => 'شج',
'ﴸ' => 'شح',
'ﴹ' => 'شخ',
'ﴺ' => 'طم',
'ﴻ' => 'ظم',
'ﴼ' => 'اً',
'ﴽ' => 'اً',
'ﵐ' => 'تجم',
'ﵑ' => 'تحج',
'ﵒ' => 'تحج',
'ﵓ' => 'تحم',
'ﵔ' => 'تخم',
'ﵕ' => 'تمج',
'ﵖ' => 'تمح',
'ﵗ' => 'تمخ',
'ﵘ' => 'جمح',
'ﵙ' => 'جمح',
'ﵚ' => 'حمي',
'ﵛ' => 'حمى',
'ﵜ' => 'سحج',
'ﵝ' => 'سجح',
'ﵞ' => 'سجى',
'ﵟ' => 'سمح',
'ﵠ' => 'سمح',
'ﵡ' => 'سمج',
'ﵢ' => 'سمم',
'ﵣ' => 'سمم',
'ﵤ' => 'صحح',
'ﵥ' => 'صحح',
'ﵦ' => 'صمم',
'ﵧ' => 'شحم',
'ﵨ' => 'شحم',
'ﵩ' => 'شجي',
'ﵪ' => 'شمخ',
'ﵫ' => 'شمخ',
'ﵬ' => 'شمم',
'ﵭ' => 'شمم',
'ﵮ' => 'ضحى',
'ﵯ' => 'ضخم',
'ﵰ' => 'ضخم',
'ﵱ' => 'طمح',
'ﵲ' => 'طمح',
'ﵳ' => 'طمم',
'ﵴ' => 'طمي',
'ﵵ' => 'عجم',
'ﵶ' => 'عمم',
'ﵷ' => 'عمم',
'ﵸ' => 'عمى',
'ﵹ' => 'غمم',
'ﵺ' => 'غمي',
'ﵻ' => 'غمى',
'ﵼ' => 'فخم',
'ﵽ' => 'فخم',
'ﵾ' => 'قمح',
'ﵿ' => 'قمم',
'ﶀ' => 'لحم',
'ﶁ' => 'لحي',
'ﶂ' => 'لحى',
'ﶃ' => 'لجج',
'ﶄ' => 'لجج',
'ﶅ' => 'لخم',
'ﶆ' => 'لخم',
'ﶇ' => 'لمح',
'ﶈ' => 'لمح',
'ﶉ' => 'محج',
'ﶊ' => 'محم',
'ﶋ' => 'محي',
'ﶌ' => 'مجح',
'ﶍ' => 'مجم',
'ﶎ' => 'مخج',
'ﶏ' => 'مخم',
'ﶒ' => 'مجخ',
'ﶓ' => 'همج',
'ﶔ' => 'همم',
'ﶕ' => 'نحم',
'ﶖ' => 'نحى',
'ﶗ' => 'نجم',
'ﶘ' => 'نجم',
'ﶙ' => 'نجى',
'ﶚ' => 'نمي',
'ﶛ' => 'نمى',
'ﶜ' => 'يمم',
'ﶝ' => 'يمم',
'ﶞ' => 'بخي',
'ﶟ' => 'تجي',
'ﶠ' => 'تجى',
'ﶡ' => 'تخي',
'ﶢ' => 'تخى',
'ﶣ' => 'تمي',
'ﶤ' => 'تمى',
'ﶥ' => 'جمي',
'ﶦ' => 'جحى',
'ﶧ' => 'جمى',
'ﶨ' => 'سخى',
'ﶩ' => 'صحي',
'ﶪ' => 'شحي',
'ﶫ' => 'ضحي',
'ﶬ' => 'لجي',
'ﶭ' => 'لمي',
'ﶮ' => 'يحي',
'ﶯ' => 'يجي',
'ﶰ' => 'يمي',
'ﶱ' => 'ممي',
'ﶲ' => 'قمي',
'ﶳ' => 'نحي',
'ﶴ' => 'قمح',
'ﶵ' => 'لحم',
'ﶶ' => 'عمي',
'ﶷ' => 'كمي',
'ﶸ' => 'نجح',
'ﶹ' => 'مخي',
'ﶺ' => 'لجم',
'ﶻ' => 'كمم',
'ﶼ' => 'لجم',
'ﶽ' => 'نجح',
'ﶾ' => 'جحي',
'ﶿ' => 'حجي',
'ﷀ' => 'مجي',
'ﷁ' => 'فمي',
'ﷂ' => 'بحي',
'ﷃ' => 'كمم',
'ﷄ' => 'عجم',
'ﷅ' => 'صمم',
'ﷆ' => 'سخي',
'ﷇ' => 'نجي',
'ﷰ' => 'صلے',
'ﷱ' => 'قلے',
'ﷲ' => 'الله',
'ﷳ' => 'اكبر',
'ﷴ' => 'محمد',
'ﷵ' => 'صلعم',
'ﷶ' => 'رسول',
'ﷷ' => 'عليه',
'ﷸ' => 'وسلم',
'ﷹ' => 'صلى',
'ﷺ' => 'صلى الله عليه وسلم',
'ﷻ' => 'جل جلاله',
'﷼' => 'ریال',
'︐' => ',',
'︑' => '、',
'︒' => '。',
'︓' => ':',
'︔' => ';',
'︕' => '!',
'︖' => '?',
'︗' => '〖',
'︘' => '〗',
'︙' => '...',
'︰' => '..',
'︱' => '—',
'︲' => '–',
'︳' => '_',
'︴' => '_',
'︵' => '(',
'︶' => ')',
'︷' => '{',
'︸' => '}',
'︹' => '〔',
'︺' => '〕',
'︻' => '【',
'︼' => '】',
'︽' => '《',
'︾' => '》',
'︿' => '〈',
'﹀' => '〉',
'﹁' => '「',
'﹂' => '」',
'﹃' => '『',
'﹄' => '』',
'﹇' => '[',
'﹈' => ']',
'﹉' => ' ̅',
'﹊' => ' ̅',
'﹋' => ' ̅',
'﹌' => ' ̅',
'﹍' => '_',
'﹎' => '_',
'﹏' => '_',
'﹐' => ',',
'﹑' => '、',
'﹒' => '.',
'﹔' => ';',
'﹕' => ':',
'﹖' => '?',
'﹗' => '!',
'﹘' => '—',
'﹙' => '(',
'﹚' => ')',
'﹛' => '{',
'﹜' => '}',
'﹝' => '〔',
'﹞' => '〕',
'﹟' => '#',
'﹠' => '&',
'﹡' => '*',
'﹢' => '+',
'﹣' => '-',
'﹤' => '<',
'﹥' => '>',
'﹦' => '=',
'﹨' => '\\',
'﹩' => '$',
'﹪' => '%',
'﹫' => '@',
'ﹰ' => ' ً',
'ﹱ' => 'ـً',
'ﹲ' => ' ٌ',
'ﹴ' => ' ٍ',
'ﹶ' => ' َ',
'ﹷ' => 'ـَ',
'ﹸ' => ' ُ',
'ﹹ' => 'ـُ',
'ﹺ' => ' ِ',
'ﹻ' => 'ـِ',
'ﹼ' => ' ّ',
'ﹽ' => 'ـّ',
'ﹾ' => ' ْ',
'ﹿ' => 'ـْ',
'ﺀ' => 'ء',
'ﺁ' => 'آ',
'ﺂ' => 'آ',
'ﺃ' => 'أ',
'ﺄ' => 'أ',
'ﺅ' => 'ؤ',
'ﺆ' => 'ؤ',
'ﺇ' => 'إ',
'ﺈ' => 'إ',
'ﺉ' => 'ئ',
'ﺊ' => 'ئ',
'ﺋ' => 'ئ',
'ﺌ' => 'ئ',
'ﺍ' => 'ا',
'ﺎ' => 'ا',
'ﺏ' => 'ب',
'ﺐ' => 'ب',
'ﺑ' => 'ب',
'ﺒ' => 'ب',
'ﺓ' => 'ة',
'ﺔ' => 'ة',
'ﺕ' => 'ت',
'ﺖ' => 'ت',
'ﺗ' => 'ت',
'ﺘ' => 'ت',
'ﺙ' => 'ث',
'ﺚ' => 'ث',
'ﺛ' => 'ث',
'ﺜ' => 'ث',
'ﺝ' => 'ج',
'ﺞ' => 'ج',
'ﺟ' => 'ج',
'ﺠ' => 'ج',
'ﺡ' => 'ح',
'ﺢ' => 'ح',
'ﺣ' => 'ح',
'ﺤ' => 'ح',
'ﺥ' => 'خ',
'ﺦ' => 'خ',
'ﺧ' => 'خ',
'ﺨ' => 'خ',
'ﺩ' => 'د',
'ﺪ' => 'د',
'ﺫ' => 'ذ',
'ﺬ' => 'ذ',
'ﺭ' => 'ر',
'ﺮ' => 'ر',
'ﺯ' => 'ز',
'ﺰ' => 'ز',
'ﺱ' => 'س',
'ﺲ' => 'س',
'ﺳ' => 'س',
'ﺴ' => 'س',
'ﺵ' => 'ش',
'ﺶ' => 'ش',
'ﺷ' => 'ش',
'ﺸ' => 'ش',
'ﺹ' => 'ص',
'ﺺ' => 'ص',
'ﺻ' => 'ص',
'ﺼ' => 'ص',
'ﺽ' => 'ض',
'ﺾ' => 'ض',
'ﺿ' => 'ض',
'ﻀ' => 'ض',
'ﻁ' => 'ط',
'ﻂ' => 'ط',
'ﻃ' => 'ط',
'ﻄ' => 'ط',
'ﻅ' => 'ظ',
'ﻆ' => 'ظ',
'ﻇ' => 'ظ',
'ﻈ' => 'ظ',
'ﻉ' => 'ع',
'ﻊ' => 'ع',
'ﻋ' => 'ع',
'ﻌ' => 'ع',
'ﻍ' => 'غ',
'ﻎ' => 'غ',
'ﻏ' => 'غ',
'ﻐ' => 'غ',
'ﻑ' => 'ف',
'ﻒ' => 'ف',
'ﻓ' => 'ف',
'ﻔ' => 'ف',
'ﻕ' => 'ق',
'ﻖ' => 'ق',
'ﻗ' => 'ق',
'ﻘ' => 'ق',
'ﻙ' => 'ك',
'ﻚ' => 'ك',
'ﻛ' => 'ك',
'ﻜ' => 'ك',
'ﻝ' => 'ل',
'ﻞ' => 'ل',
'ﻟ' => 'ل',
'ﻠ' => 'ل',
'ﻡ' => 'م',
'ﻢ' => 'م',
'ﻣ' => 'م',
'ﻤ' => 'م',
'ﻥ' => 'ن',
'ﻦ' => 'ن',
'ﻧ' => 'ن',
'ﻨ' => 'ن',
'ﻩ' => 'ه',
'ﻪ' => 'ه',
'ﻫ' => 'ه',
'ﻬ' => 'ه',
'ﻭ' => 'و',
'ﻮ' => 'و',
'ﻯ' => 'ى',
'ﻰ' => 'ى',
'ﻱ' => 'ي',
'ﻲ' => 'ي',
'ﻳ' => 'ي',
'ﻴ' => 'ي',
'ﻵ' => 'لآ',
'ﻶ' => 'لآ',
'ﻷ' => 'لأ',
'ﻸ' => 'لأ',
'ﻹ' => 'لإ',
'ﻺ' => 'لإ',
'ﻻ' => 'لا',
'ﻼ' => 'لا',
'！' => '!',
'＂' => '"',
'＃' => '#',
'＄' => '$',
'％' => '%',
'＆' => '&',
'＇' => '\'',
'（' => '(',
'）' => ')',
'＊' => '*',
'＋' => '+',
'，' => ',',
'－' => '-',
'．' => '.',
'／' => '/',
'０' => '0',
'１' => '1',
'２' => '2',
'３' => '3',
'４' => '4',
'５' => '5',
'６' => '6',
'７' => '7',
'８' => '8',
'９' => '9',
'：' => ':',
'；' => ';',
'＜' => '<',
'＝' => '=',
'＞' => '>',
'？' => '?',
'＠' => '@',
'Ａ' => 'A',
'Ｂ' => 'B',
'Ｃ' => 'C',
'Ｄ' => 'D',
'Ｅ' => 'E',
'Ｆ' => 'F',
'Ｇ' => 'G',
'Ｈ' => 'H',
'Ｉ' => 'I',
'Ｊ' => 'J',
'Ｋ' => 'K',
'Ｌ' => 'L',
'Ｍ' => 'M',
'Ｎ' => 'N',
'Ｏ' => 'O',
'Ｐ' => 'P',
'Ｑ' => 'Q',
'Ｒ' => 'R',
'Ｓ' => 'S',
'Ｔ' => 'T',
'Ｕ' => 'U',
'Ｖ' => 'V',
'Ｗ' => 'W',
'Ｘ' => 'X',
'Ｙ' => 'Y',
'Ｚ' => 'Z',
'［' => '[',
'＼' => '\\',
'］' => ']',
'＾' => '^',
'＿' => '_',
'｀' => '`',
'ａ' => 'a',
'ｂ' => 'b',
'ｃ' => 'c',
'ｄ' => 'd',
'ｅ' => 'e',
'ｆ' => 'f',
'ｇ' => 'g',
'ｈ' => 'h',
'ｉ' => 'i',
'ｊ' => 'j',
'ｋ' => 'k',
'ｌ' => 'l',
'ｍ' => 'm',
'ｎ' => 'n',
'ｏ' => 'o',
'ｐ' => 'p',
'ｑ' => 'q',
'ｒ' => 'r',
'ｓ' => 's',
'ｔ' => 't',
'ｕ' => 'u',
'ｖ' => 'v',
'ｗ' => 'w',
'ｘ' => 'x',
'ｙ' => 'y',
'ｚ' => 'z',
'｛' => '{',
'｜' => '|',
'｝' => '}',
'～' => '~',
'｟' => '⦅',
'｠' => '⦆',
'｡' => '。',
'｢' => '「',
'｣' => '」',
'､' => '、',
'･' => '・',
'ｦ' => 'ヲ',
'ｧ' => 'ァ',
'ｨ' => 'ィ',
'ｩ' => 'ゥ',
'ｪ' => 'ェ',
'ｫ' => 'ォ',
'ｬ' => 'ャ',
'ｭ' => 'ュ',
'ｮ' => 'ョ',
'ｯ' => 'ッ',
'ｰ' => 'ー',
'ｱ' => 'ア',
'ｲ' => 'イ',
'ｳ' => 'ウ',
'ｴ' => 'エ',
'ｵ' => 'オ',
'ｶ' => 'カ',
'ｷ' => 'キ',
'ｸ' => 'ク',
'ｹ' => 'ケ',
'ｺ' => 'コ',
'ｻ' => 'サ',
'ｼ' => 'シ',
'ｽ' => 'ス',
'ｾ' => 'セ',
'ｿ' => 'ソ',
'ﾀ' => 'タ',
'ﾁ' => 'チ',
'ﾂ' => 'ツ',
'ﾃ' => 'テ',
'ﾄ' => 'ト',
'ﾅ' => 'ナ',
'ﾆ' => 'ニ',
'ﾇ' => 'ヌ',
'ﾈ' => 'ネ',
'ﾉ' => 'ノ',
'ﾊ' => 'ハ',
'ﾋ' => 'ヒ',
'ﾌ' => 'フ',
'ﾍ' => 'ヘ',
'ﾎ' => 'ホ',
'ﾏ' => 'マ',
'ﾐ' => 'ミ',
'ﾑ' => 'ム',
'ﾒ' => 'メ',
'ﾓ' => 'モ',
'ﾔ' => 'ヤ',
'ﾕ' => 'ユ',
'ﾖ' => 'ヨ',
'ﾗ' => 'ラ',
'ﾘ' => 'リ',
'ﾙ' => 'ル',
'ﾚ' => 'レ',
'ﾛ' => 'ロ',
'ﾜ' => 'ワ',
'ﾝ' => 'ン',
'ﾞ' => '゙',
'ﾟ' => '゚',
'ﾠ' => 'ᅠ',
'ﾡ' => 'ᄀ',
'ﾢ' => 'ᄁ',
'ﾣ' => 'ᆪ',
'ﾤ' => 'ᄂ',
'ﾥ' => 'ᆬ',
'ﾦ' => 'ᆭ',
'ﾧ' => 'ᄃ',
'ﾨ' => 'ᄄ',
'ﾩ' => 'ᄅ',
'ﾪ' => 'ᆰ',
'ﾫ' => 'ᆱ',
'ﾬ' => 'ᆲ',
'ﾭ' => 'ᆳ',
'ﾮ' => 'ᆴ',
'ﾯ' => 'ᆵ',
'ﾰ' => 'ᄚ',
'ﾱ' => 'ᄆ',
'ﾲ' => 'ᄇ',
'ﾳ' => 'ᄈ',
'ﾴ' => 'ᄡ',
'ﾵ' => 'ᄉ',
'ﾶ' => 'ᄊ',
'ﾷ' => 'ᄋ',
'ﾸ' => 'ᄌ',
'ﾹ' => 'ᄍ',
'ﾺ' => 'ᄎ',
'ﾻ' => 'ᄏ',
'ﾼ' => 'ᄐ',
'ﾽ' => 'ᄑ',
'ﾾ' => 'ᄒ',
'ￂ' => 'ᅡ',
'ￃ' => 'ᅢ',
'ￄ' => 'ᅣ',
'ￅ' => 'ᅤ',
'ￆ' => 'ᅥ',
'ￇ' => 'ᅦ',
'ￊ' => 'ᅧ',
'ￋ' => 'ᅨ',
'ￌ' => 'ᅩ',
'ￍ' => 'ᅪ',
'ￎ' => 'ᅫ',
'ￏ' => 'ᅬ',
'ￒ' => 'ᅭ',
'ￓ' => 'ᅮ',
'ￔ' => 'ᅯ',
'ￕ' => 'ᅰ',
'ￖ' => 'ᅱ',
'ￗ' => 'ᅲ',
'ￚ' => 'ᅳ',
'ￛ' => 'ᅴ',
'ￜ' => 'ᅵ',
'￠' => '¢',
'￡' => '£',
'￢' => '¬',
'￣' => ' ̄',
'￤' => '¦',
'￥' => '¥',
'￦' => '₩',
'￨' => '│',
'￩' => '←',
'￪' => '↑',
'￫' => '→',
'￬' => '↓',
'￭' => '■',
'￮' => '○',
'𝐀' => 'A',
'𝐁' => 'B',
'𝐂' => 'C',
'𝐃' => 'D',
'𝐄' => 'E',
'𝐅' => 'F',
'𝐆' => 'G',
'𝐇' => 'H',
'𝐈' => 'I',
'𝐉' => 'J',
'𝐊' => 'K',
'𝐋' => 'L',
'𝐌' => 'M',
'𝐍' => 'N',
'𝐎' => 'O',
'𝐏' => 'P',
'𝐐' => 'Q',
'𝐑' => 'R',
'𝐒' => 'S',
'𝐓' => 'T',
'𝐔' => 'U',
'𝐕' => 'V',
'𝐖' => 'W',
'𝐗' => 'X',
'𝐘' => 'Y',
'𝐙' => 'Z',
'𝐚' => 'a',
'𝐛' => 'b',
'𝐜' => 'c',
'𝐝' => 'd',
'𝐞' => 'e',
'𝐟' => 'f',
'𝐠' => 'g',
'𝐡' => 'h',
'𝐢' => 'i',
'𝐣' => 'j',
'𝐤' => 'k',
'𝐥' => 'l',
'𝐦' => 'm',
'𝐧' => 'n',
'𝐨' => 'o',
'𝐩' => 'p',
'𝐪' => 'q',
'𝐫' => 'r',
'𝐬' => 's',
'𝐭' => 't',
'𝐮' => 'u',
'𝐯' => 'v',
'𝐰' => 'w',
'𝐱' => 'x',
'𝐲' => 'y',
'𝐳' => 'z',
'𝐴' => 'A',
'𝐵' => 'B',
'𝐶' => 'C',
'𝐷' => 'D',
'𝐸' => 'E',
'𝐹' => 'F',
'𝐺' => 'G',
'𝐻' => 'H',
'𝐼' => 'I',
'𝐽' => 'J',
'𝐾' => 'K',
'𝐿' => 'L',
'𝑀' => 'M',
'𝑁' => 'N',
'𝑂' => 'O',
'𝑃' => 'P',
'𝑄' => 'Q',
'𝑅' => 'R',
'𝑆' => 'S',
'𝑇' => 'T',
'𝑈' => 'U',
'𝑉' => 'V',
'𝑊' => 'W',
'𝑋' => 'X',
'𝑌' => 'Y',
'𝑍' => 'Z',
'𝑎' => 'a',
'𝑏' => 'b',
'𝑐' => 'c',
'𝑑' => 'd',
'𝑒' => 'e',
'𝑓' => 'f',
'𝑔' => 'g',
'𝑖' => 'i',
'𝑗' => 'j',
'𝑘' => 'k',
'𝑙' => 'l',
'𝑚' => 'm',
'𝑛' => 'n',
'𝑜' => 'o',
'𝑝' => 'p',
'𝑞' => 'q',
'𝑟' => 'r',
'𝑠' => 's',
'𝑡' => 't',
'𝑢' => 'u',
'𝑣' => 'v',
'𝑤' => 'w',
'𝑥' => 'x',
'𝑦' => 'y',
'𝑧' => 'z',
'𝑨' => 'A',
'𝑩' => 'B',
'𝑪' => 'C',
'𝑫' => 'D',
'𝑬' => 'E',
'𝑭' => 'F',
'𝑮' => 'G',
'𝑯' => 'H',
'𝑰' => 'I',
'𝑱' => 'J',
'𝑲' => 'K',
'𝑳' => 'L',
'𝑴' => 'M',
'𝑵' => 'N',
'𝑶' => 'O',
'𝑷' => 'P',
'𝑸' => 'Q',
'𝑹' => 'R',
'𝑺' => 'S',
'𝑻' => 'T',
'𝑼' => 'U',
'𝑽' => 'V',
'𝑾' => 'W',
'𝑿' => 'X',
'𝒀' => 'Y',
'𝒁' => 'Z',
'𝒂' => 'a',
'𝒃' => 'b',
'𝒄' => 'c',
'𝒅' => 'd',
'𝒆' => 'e',
'𝒇' => 'f',
'𝒈' => 'g',
'𝒉' => 'h',
'𝒊' => 'i',
'𝒋' => 'j',
'𝒌' => 'k',
'𝒍' => 'l',
'𝒎' => 'm',
'𝒏' => 'n',
'𝒐' => 'o',
'𝒑' => 'p',
'𝒒' => 'q',
'𝒓' => 'r',
'𝒔' => 's',
'𝒕' => 't',
'𝒖' => 'u',
'𝒗' => 'v',
'𝒘' => 'w',
'𝒙' => 'x',
'𝒚' => 'y',
'𝒛' => 'z',
'𝒜' => 'A',
'𝒞' => 'C',
'𝒟' => 'D',
'𝒢' => 'G',
'𝒥' => 'J',
'𝒦' => 'K',
'𝒩' => 'N',
'𝒪' => 'O',
'𝒫' => 'P',
'𝒬' => 'Q',
'𝒮' => 'S',
'𝒯' => 'T',
'𝒰' => 'U',
'𝒱' => 'V',
'𝒲' => 'W',
'𝒳' => 'X',
'𝒴' => 'Y',
'𝒵' => 'Z',
'𝒶' => 'a',
'𝒷' => 'b',
'𝒸' => 'c',
'𝒹' => 'd',
'𝒻' => 'f',
'𝒽' => 'h',
'𝒾' => 'i',
'𝒿' => 'j',
'𝓀' => 'k',
'𝓁' => 'l',
'𝓂' => 'm',
'𝓃' => 'n',
'𝓅' => 'p',
'𝓆' => 'q',
'𝓇' => 'r',
'𝓈' => 's',
'𝓉' => 't',
'𝓊' => 'u',
'𝓋' => 'v',
'𝓌' => 'w',
'𝓍' => 'x',
'𝓎' => 'y',
'𝓏' => 'z',
'𝓐' => 'A',
'𝓑' => 'B',
'𝓒' => 'C',
'𝓓' => 'D',
'𝓔' => 'E',
'𝓕' => 'F',
'𝓖' => 'G',
'𝓗' => 'H',
'𝓘' => 'I',
'𝓙' => 'J',
'𝓚' => 'K',
'𝓛' => 'L',
'𝓜' => 'M',
'𝓝' => 'N',
'𝓞' => 'O',
'𝓟' => 'P',
'𝓠' => 'Q',
'𝓡' => 'R',
'𝓢' => 'S',
'𝓣' => 'T',
'𝓤' => 'U',
'𝓥' => 'V',
'𝓦' => 'W',
'𝓧' => 'X',
'𝓨' => 'Y',
'𝓩' => 'Z',
'𝓪' => 'a',
'𝓫' => 'b',
'𝓬' => 'c',
'𝓭' => 'd',
'𝓮' => 'e',
'𝓯' => 'f',
'𝓰' => 'g',
'𝓱' => 'h',
'𝓲' => 'i',
'𝓳' => 'j',
'𝓴' => 'k',
'𝓵' => 'l',
'𝓶' => 'm',
'𝓷' => 'n',
'𝓸' => 'o',
'𝓹' => 'p',
'𝓺' => 'q',
'𝓻' => 'r',
'𝓼' => 's',
'𝓽' => 't',
'𝓾' => 'u',
'𝓿' => 'v',
'𝔀' => 'w',
'𝔁' => 'x',
'𝔂' => 'y',
'𝔃' => 'z',
'𝔄' => 'A',
'𝔅' => 'B',
'𝔇' => 'D',
'𝔈' => 'E',
'𝔉' => 'F',
'𝔊' => 'G',
'𝔍' => 'J',
'𝔎' => 'K',
'𝔏' => 'L',
'𝔐' => 'M',
'𝔑' => 'N',
'𝔒' => 'O',
'𝔓' => 'P',
'𝔔' => 'Q',
'𝔖' => 'S',
'𝔗' => 'T',
'𝔘' => 'U',
'𝔙' => 'V',
'𝔚' => 'W',
'𝔛' => 'X',
'𝔜' => 'Y',
'𝔞' => 'a',
'𝔟' => 'b',
'𝔠' => 'c',
'𝔡' => 'd',
'𝔢' => 'e',
'𝔣' => 'f',
'𝔤' => 'g',
'𝔥' => 'h',
'𝔦' => 'i',
'𝔧' => 'j',
'𝔨' => 'k',
'𝔩' => 'l',
'𝔪' => 'm',
'𝔫' => 'n',
'𝔬' => 'o',
'𝔭' => 'p',
'𝔮' => 'q',
'𝔯' => 'r',
'𝔰' => 's',
'𝔱' => 't',
'𝔲' => 'u',
'𝔳' => 'v',
'𝔴' => 'w',
'𝔵' => 'x',
'𝔶' => 'y',
'𝔷' => 'z',
'𝔸' => 'A',
'𝔹' => 'B',
'𝔻' => 'D',
'𝔼' => 'E',
'𝔽' => 'F',
'𝔾' => 'G',
'𝕀' => 'I',
'𝕁' => 'J',
'𝕂' => 'K',
'𝕃' => 'L',
'𝕄' => 'M',
'𝕆' => 'O',
'𝕊' => 'S',
'𝕋' => 'T',
'𝕌' => 'U',
'𝕍' => 'V',
'𝕎' => 'W',
'𝕏' => 'X',
'𝕐' => 'Y',
'𝕒' => 'a',
'𝕓' => 'b',
'𝕔' => 'c',
'𝕕' => 'd',
'𝕖' => 'e',
'𝕗' => 'f',
'𝕘' => 'g',
'𝕙' => 'h',
'𝕚' => 'i',
'𝕛' => 'j',
'𝕜' => 'k',
'𝕝' => 'l',
'𝕞' => 'm',
'𝕟' => 'n',
'𝕠' => 'o',
'𝕡' => 'p',
'𝕢' => 'q',
'𝕣' => 'r',
'𝕤' => 's',
'𝕥' => 't',
'𝕦' => 'u',
'𝕧' => 'v',
'𝕨' => 'w',
'𝕩' => 'x',
'𝕪' => 'y',
'𝕫' => 'z',
'𝕬' => 'A',
'𝕭' => 'B',
'𝕮' => 'C',
'𝕯' => 'D',
'𝕰' => 'E',
'𝕱' => 'F',
'𝕲' => 'G',
'𝕳' => 'H',
'𝕴' => 'I',
'𝕵' => 'J',
'𝕶' => 'K',
'𝕷' => 'L',
'𝕸' => 'M',
'𝕹' => 'N',
'𝕺' => 'O',
'𝕻' => 'P',
'𝕼' => 'Q',
'𝕽' => 'R',
'𝕾' => 'S',
'𝕿' => 'T',
'𝖀' => 'U',
'𝖁' => 'V',
'𝖂' => 'W',
'𝖃' => 'X',
'𝖄' => 'Y',
'𝖅' => 'Z',
'𝖆' => 'a',
'𝖇' => 'b',
'𝖈' => 'c',
'𝖉' => 'd',
'𝖊' => 'e',
'𝖋' => 'f',
'𝖌' => 'g',
'𝖍' => 'h',
'𝖎' => 'i',
'𝖏' => 'j',
'𝖐' => 'k',
'𝖑' => 'l',
'𝖒' => 'm',
'𝖓' => 'n',
'𝖔' => 'o',
'𝖕' => 'p',
'𝖖' => 'q',
'𝖗' => 'r',
'𝖘' => 's',
'𝖙' => 't',
'𝖚' => 'u',
'𝖛' => 'v',
'𝖜' => 'w',
'𝖝' => 'x',
'𝖞' => 'y',
'𝖟' => 'z',
'𝖠' => 'A',
'𝖡' => 'B',
'𝖢' => 'C',
'𝖣' => 'D',
'𝖤' => 'E',
'𝖥' => 'F',
'𝖦' => 'G',
'𝖧' => 'H',
'𝖨' => 'I',
'𝖩' => 'J',
'𝖪' => 'K',
'𝖫' => 'L',
'𝖬' => 'M',
'𝖭' => 'N',
'𝖮' => 'O',
'𝖯' => 'P',
'𝖰' => 'Q',
'𝖱' => 'R',
'𝖲' => 'S',
'𝖳' => 'T',
'𝖴' => 'U',
'𝖵' => 'V',
'𝖶' => 'W',
'𝖷' => 'X',
'𝖸' => 'Y',
'𝖹' => 'Z',
'𝖺' => 'a',
'𝖻' => 'b',
'𝖼' => 'c',
'𝖽' => 'd',
'𝖾' => 'e',
'𝖿' => 'f',
'𝗀' => 'g',
'𝗁' => 'h',
'𝗂' => 'i',
'𝗃' => 'j',
'𝗄' => 'k',
'𝗅' => 'l',
'𝗆' => 'm',
'𝗇' => 'n',
'𝗈' => 'o',
'𝗉' => 'p',
'𝗊' => 'q',
'𝗋' => 'r',
'𝗌' => 's',
'𝗍' => 't',
'𝗎' => 'u',
'𝗏' => 'v',
'𝗐' => 'w',
'𝗑' => 'x',
'𝗒' => 'y',
'𝗓' => 'z',
'𝗔' => 'A',
'𝗕' => 'B',
'𝗖' => 'C',
'𝗗' => 'D',
'𝗘' => 'E',
'𝗙' => 'F',
'𝗚' => 'G',
'𝗛' => 'H',
'𝗜' => 'I',
'𝗝' => 'J',
'𝗞' => 'K',
'𝗟' => 'L',
'𝗠' => 'M',
'𝗡' => 'N',
'𝗢' => 'O',
'𝗣' => 'P',
'𝗤' => 'Q',
'𝗥' => 'R',
'𝗦' => 'S',
'𝗧' => 'T',
'𝗨' => 'U',
'𝗩' => 'V',
'𝗪' => 'W',
'𝗫' => 'X',
'𝗬' => 'Y',
'𝗭' => 'Z',
'𝗮' => 'a',
'𝗯' => 'b',
'𝗰' => 'c',
'𝗱' => 'd',
'𝗲' => 'e',
'𝗳' => 'f',
'𝗴' => 'g',
'𝗵' => 'h',
'𝗶' => 'i',
'𝗷' => 'j',
'𝗸' => 'k',
'𝗹' => 'l',
'𝗺' => 'm',
'𝗻' => 'n',
'𝗼' => 'o',
'𝗽' => 'p',
'𝗾' => 'q',
'𝗿' => 'r',
'𝘀' => 's',
'𝘁' => 't',
'𝘂' => 'u',
'𝘃' => 'v',
'𝘄' => 'w',
'𝘅' => 'x',
'𝘆' => 'y',
'𝘇' => 'z',
'𝘈' => 'A',
'𝘉' => 'B',
'𝘊' => 'C',
'𝘋' => 'D',
'𝘌' => 'E',
'𝘍' => 'F',
'𝘎' => 'G',
'𝘏' => 'H',
'𝘐' => 'I',
'𝘑' => 'J',
'𝘒' => 'K',
'𝘓' => 'L',
'𝘔' => 'M',
'𝘕' => 'N',
'𝘖' => 'O',
'𝘗' => 'P',
'𝘘' => 'Q',
'𝘙' => 'R',
'𝘚' => 'S',
'𝘛' => 'T',
'𝘜' => 'U',
'𝘝' => 'V',
'𝘞' => 'W',
'𝘟' => 'X',
'𝘠' => 'Y',
'𝘡' => 'Z',
'𝘢' => 'a',
'𝘣' => 'b',
'𝘤' => 'c',
'𝘥' => 'd',
'𝘦' => 'e',
'𝘧' => 'f',
'𝘨' => 'g',
'𝘩' => 'h',
'𝘪' => 'i',
'𝘫' => 'j',
'𝘬' => 'k',
'𝘭' => 'l',
'𝘮' => 'm',
'𝘯' => 'n',
'𝘰' => 'o',
'𝘱' => 'p',
'𝘲' => 'q',
'𝘳' => 'r',
'𝘴' => 's',
'𝘵' => 't',
'𝘶' => 'u',
'𝘷' => 'v',
'𝘸' => 'w',
'𝘹' => 'x',
'𝘺' => 'y',
'𝘻' => 'z',
'𝘼' => 'A',
'𝘽' => 'B',
'𝘾' => 'C',
'𝘿' => 'D',
'𝙀' => 'E',
'𝙁' => 'F',
'𝙂' => 'G',
'𝙃' => 'H',
'𝙄' => 'I',
'𝙅' => 'J',
'𝙆' => 'K',
'𝙇' => 'L',
'𝙈' => 'M',
'𝙉' => 'N',
'𝙊' => 'O',
'𝙋' => 'P',
'𝙌' => 'Q',
'𝙍' => 'R',
'𝙎' => 'S',
'𝙏' => 'T',
'𝙐' => 'U',
'𝙑' => 'V',
'𝙒' => 'W',
'𝙓' => 'X',
'𝙔' => 'Y',
'𝙕' => 'Z',
'𝙖' => 'a',
'𝙗' => 'b',
'𝙘' => 'c',
'𝙙' => 'd',
'𝙚' => 'e',
'𝙛' => 'f',
'𝙜' => 'g',
'𝙝' => 'h',
'𝙞' => 'i',
'𝙟' => 'j',
'𝙠' => 'k',
'𝙡' => 'l',
'𝙢' => 'm',
'𝙣' => 'n',
'𝙤' => 'o',
'𝙥' => 'p',
'𝙦' => 'q',
'𝙧' => 'r',
'𝙨' => 's',
'𝙩' => 't',
'𝙪' => 'u',
'𝙫' => 'v',
'𝙬' => 'w',
'𝙭' => 'x',
'𝙮' => 'y',
'𝙯' => 'z',
'𝙰' => 'A',
'𝙱' => 'B',
'𝙲' => 'C',
'𝙳' => 'D',
'𝙴' => 'E',
'𝙵' => 'F',
'𝙶' => 'G',
'𝙷' => 'H',
'𝙸' => 'I',
'𝙹' => 'J',
'𝙺' => 'K',
'𝙻' => 'L',
'𝙼' => 'M',
'𝙽' => 'N',
'𝙾' => 'O',
'𝙿' => 'P',
'𝚀' => 'Q',
'𝚁' => 'R',
'𝚂' => 'S',
'𝚃' => 'T',
'𝚄' => 'U',
'𝚅' => 'V',
'𝚆' => 'W',
'𝚇' => 'X',
'𝚈' => 'Y',
'𝚉' => 'Z',
'𝚊' => 'a',
'𝚋' => 'b',
'𝚌' => 'c',
'𝚍' => 'd',
'𝚎' => 'e',
'𝚏' => 'f',
'𝚐' => 'g',
'𝚑' => 'h',
'𝚒' => 'i',
'𝚓' => 'j',
'𝚔' => 'k',
'𝚕' => 'l',
'𝚖' => 'm',
'𝚗' => 'n',
'𝚘' => 'o',
'𝚙' => 'p',
'𝚚' => 'q',
'𝚛' => 'r',
'𝚜' => 's',
'𝚝' => 't',
'𝚞' => 'u',
'𝚟' => 'v',
'𝚠' => 'w',
'𝚡' => 'x',
'𝚢' => 'y',
'𝚣' => 'z',
'𝚤' => 'ı',
'𝚥' => 'ȷ',
'𝚨' => 'Α',
'𝚩' => 'Β',
'𝚪' => 'Γ',
'𝚫' => 'Δ',
'𝚬' => 'Ε',
'𝚭' => 'Ζ',
'𝚮' => 'Η',
'𝚯' => 'Θ',
'𝚰' => 'Ι',
'𝚱' => 'Κ',
'𝚲' => 'Λ',
'𝚳' => 'Μ',
'𝚴' => 'Ν',
'𝚵' => 'Ξ',
'𝚶' => 'Ο',
'𝚷' => 'Π',
'𝚸' => 'Ρ',
'𝚹' => 'Θ',
'𝚺' => 'Σ',
'𝚻' => 'Τ',
'𝚼' => 'Υ',
'𝚽' => 'Φ',
'𝚾' => 'Χ',
'𝚿' => 'Ψ',
'𝛀' => 'Ω',
'𝛁' => '∇',
'𝛂' => 'α',
'𝛃' => 'β',
'𝛄' => 'γ',
'𝛅' => 'δ',
'𝛆' => 'ε',
'𝛇' => 'ζ',
'𝛈' => 'η',
'𝛉' => 'θ',
'𝛊' => 'ι',
'𝛋' => 'κ',
'𝛌' => 'λ',
'𝛍' => 'μ',
'𝛎' => 'ν',
'𝛏' => 'ξ',
'𝛐' => 'ο',
'𝛑' => 'π',
'𝛒' => 'ρ',
'𝛓' => 'ς',
'𝛔' => 'σ',
'𝛕' => 'τ',
'𝛖' => 'υ',
'𝛗' => 'φ',
'𝛘' => 'χ',
'𝛙' => 'ψ',
'𝛚' => 'ω',
'𝛛' => '∂',
'𝛜' => 'ε',
'𝛝' => 'θ',
'𝛞' => 'κ',
'𝛟' => 'φ',
'𝛠' => 'ρ',
'𝛡' => 'π',
'𝛢' => 'Α',
'𝛣' => 'Β',
'𝛤' => 'Γ',
'𝛥' => 'Δ',
'𝛦' => 'Ε',
'𝛧' => 'Ζ',
'𝛨' => 'Η',
'𝛩' => 'Θ',
'𝛪' => 'Ι',
'𝛫' => 'Κ',
'𝛬' => 'Λ',
'𝛭' => 'Μ',
'𝛮' => 'Ν',
'𝛯' => 'Ξ',
'𝛰' => 'Ο',
'𝛱' => 'Π',
'𝛲' => 'Ρ',
'𝛳' => 'Θ',
'𝛴' => 'Σ',
'𝛵' => 'Τ',
'𝛶' => 'Υ',
'𝛷' => 'Φ',
'𝛸' => 'Χ',
'𝛹' => 'Ψ',
'𝛺' => 'Ω',
'𝛻' => '∇',
'𝛼' => 'α',
'𝛽' => 'β',
'𝛾' => 'γ',
'𝛿' => 'δ',
'𝜀' => 'ε',
'𝜁' => 'ζ',
'𝜂' => 'η',
'𝜃' => 'θ',
'𝜄' => 'ι',
'𝜅' => 'κ',
'𝜆' => 'λ',
'𝜇' => 'μ',
'𝜈' => 'ν',
'𝜉' => 'ξ',
'𝜊' => 'ο',
'𝜋' => 'π',
'𝜌' => 'ρ',
'𝜍' => 'ς',
'𝜎' => 'σ',
'𝜏' => 'τ',
'𝜐' => 'υ',
'𝜑' => 'φ',
'𝜒' => 'χ',
'𝜓' => 'ψ',
'𝜔' => 'ω',
'𝜕' => '∂',
'𝜖' => 'ε',
'𝜗' => 'θ',
'𝜘' => 'κ',
'𝜙' => 'φ',
'𝜚' => 'ρ',
'𝜛' => 'π',
'𝜜' => 'Α',
'𝜝' => 'Β',
'𝜞' => 'Γ',
'𝜟' => 'Δ',
'𝜠' => 'Ε',
'𝜡' => 'Ζ',
'𝜢' => 'Η',
'𝜣' => 'Θ',
'𝜤' => 'Ι',
'𝜥' => 'Κ',
'𝜦' => 'Λ',
'𝜧' => 'Μ',
'𝜨' => 'Ν',
'𝜩' => 'Ξ',
'𝜪' => 'Ο',
'𝜫' => 'Π',
'𝜬' => 'Ρ',
'𝜭' => 'Θ',
'𝜮' => 'Σ',
'𝜯' => 'Τ',
'𝜰' => 'Υ',
'𝜱' => 'Φ',
'𝜲' => 'Χ',
'𝜳' => 'Ψ',
'𝜴' => 'Ω',
'𝜵' => '∇',
'𝜶' => 'α',
'𝜷' => 'β',
'𝜸' => 'γ',
'𝜹' => 'δ',
'𝜺' => 'ε',
'𝜻' => 'ζ',
'𝜼' => 'η',
'𝜽' => 'θ',
'𝜾' => 'ι',
'𝜿' => 'κ',
'𝝀' => 'λ',
'𝝁' => 'μ',
'𝝂' => 'ν',
'𝝃' => 'ξ',
'𝝄' => 'ο',
'𝝅' => 'π',
'𝝆' => 'ρ',
'𝝇' => 'ς',
'𝝈' => 'σ',
'𝝉' => 'τ',
'𝝊' => 'υ',
'𝝋' => 'φ',
'𝝌' => 'χ',
'𝝍' => 'ψ',
'𝝎' => 'ω',
'𝝏' => '∂',
'𝝐' => 'ε',
'𝝑' => 'θ',
'𝝒' => 'κ',
'𝝓' => 'φ',
'𝝔' => 'ρ',
'𝝕' => 'π',
'𝝖' => 'Α',
'𝝗' => 'Β',
'𝝘' => 'Γ',
'𝝙' => 'Δ',
'𝝚' => 'Ε',
'𝝛' => 'Ζ',
'𝝜' => 'Η',
'𝝝' => 'Θ',
'𝝞' => 'Ι',
'𝝟' => 'Κ',
'𝝠' => 'Λ',
'𝝡' => 'Μ',
'𝝢' => 'Ν',
'𝝣' => 'Ξ',
'𝝤' => 'Ο',
'𝝥' => 'Π',
'𝝦' => 'Ρ',
'𝝧' => 'Θ',
'𝝨' => 'Σ',
'𝝩' => 'Τ',
'𝝪' => 'Υ',
'𝝫' => 'Φ',
'𝝬' => 'Χ',
'𝝭' => 'Ψ',
'𝝮' => 'Ω',
'𝝯' => '∇',
'𝝰' => 'α',
'𝝱' => 'β',
'𝝲' => 'γ',
'𝝳' => 'δ',
'𝝴' => 'ε',
'𝝵' => 'ζ',
'𝝶' => 'η',
'𝝷' => 'θ',
'𝝸' => 'ι',
'𝝹' => 'κ',
'𝝺' => 'λ',
'𝝻' => 'μ',
'𝝼' => 'ν',
'𝝽' => 'ξ',
'𝝾' => 'ο',
'𝝿' => 'π',
'𝞀' => 'ρ',
'𝞁' => 'ς',
'𝞂' => 'σ',
'𝞃' => 'τ',
'𝞄' => 'υ',
'𝞅' => 'φ',
'𝞆' => 'χ',
'𝞇' => 'ψ',
'𝞈' => 'ω',
'𝞉' => '∂',
'𝞊' => 'ε',
'𝞋' => 'θ',
'𝞌' => 'κ',
'𝞍' => 'φ',
'𝞎' => 'ρ',
'𝞏' => 'π',
'𝞐' => 'Α',
'𝞑' => 'Β',
'𝞒' => 'Γ',
'𝞓' => 'Δ',
'𝞔' => 'Ε',
'𝞕' => 'Ζ',
'𝞖' => 'Η',
'𝞗' => 'Θ',
'𝞘' => 'Ι',
'𝞙' => 'Κ',
'𝞚' => 'Λ',
'𝞛' => 'Μ',
'𝞜' => 'Ν',
'𝞝' => 'Ξ',
'𝞞' => 'Ο',
'𝞟' => 'Π',
'𝞠' => 'Ρ',
'𝞡' => 'Θ',
'𝞢' => 'Σ',
'𝞣' => 'Τ',
'𝞤' => 'Υ',
'𝞥' => 'Φ',
'𝞦' => 'Χ',
'𝞧' => 'Ψ',
'𝞨' => 'Ω',
'𝞩' => '∇',
'𝞪' => 'α',
'𝞫' => 'β',
'𝞬' => 'γ',
'𝞭' => 'δ',
'𝞮' => 'ε',
'𝞯' => 'ζ',
'𝞰' => 'η',
'𝞱' => 'θ',
'𝞲' => 'ι',
'𝞳' => 'κ',
'𝞴' => 'λ',
'𝞵' => 'μ',
'𝞶' => 'ν',
'𝞷' => 'ξ',
'𝞸' => 'ο',
'𝞹' => 'π',
'𝞺' => 'ρ',
'𝞻' => 'ς',
'𝞼' => 'σ',
'𝞽' => 'τ',
'𝞾' => 'υ',
'𝞿' => 'φ',
'𝟀' => 'χ',
'𝟁' => 'ψ',
'𝟂' => 'ω',
'𝟃' => '∂',
'𝟄' => 'ε',
'𝟅' => 'θ',
'𝟆' => 'κ',
'𝟇' => 'φ',
'𝟈' => 'ρ',
'𝟉' => 'π',
'𝟊' => 'Ϝ',
'𝟋' => 'ϝ',
'𝟎' => '0',
'𝟏' => '1',
'𝟐' => '2',
'𝟑' => '3',
'𝟒' => '4',
'𝟓' => '5',
'𝟔' => '6',
'𝟕' => '7',
'𝟖' => '8',
'𝟗' => '9',
'𝟘' => '0',
'𝟙' => '1',
'𝟚' => '2',
'𝟛' => '3',
'𝟜' => '4',
'𝟝' => '5',
'𝟞' => '6',
'𝟟' => '7',
'𝟠' => '8',
'𝟡' => '9',
'𝟢' => '0',
'𝟣' => '1',
'𝟤' => '2',
'𝟥' => '3',
'𝟦' => '4',
'𝟧' => '5',
'𝟨' => '6',
'𝟩' => '7',
'𝟪' => '8',
'𝟫' => '9',
'𝟬' => '0',
'𝟭' => '1',
'𝟮' => '2',
'𝟯' => '3',
'𝟰' => '4',
'𝟱' => '5',
'𝟲' => '6',
'𝟳' => '7',
'𝟴' => '8',
'𝟵' => '9',
'𝟶' => '0',
'𝟷' => '1',
'𝟸' => '2',
'𝟹' => '3',
'𝟺' => '4',
'𝟻' => '5',
'𝟼' => '6',
'𝟽' => '7',
'𝟾' => '8',
'𝟿' => '9',
'𞸀' => 'ا',
'𞸁' => 'ب',
'𞸂' => 'ج',
'𞸃' => 'د',
'𞸅' => 'و',
'𞸆' => 'ز',
'𞸇' => 'ح',
'𞸈' => 'ط',
'𞸉' => 'ي',
'𞸊' => 'ك',
'𞸋' => 'ل',
'𞸌' => 'م',
'𞸍' => 'ن',
'𞸎' => 'س',
'𞸏' => 'ع',
'𞸐' => 'ف',
'𞸑' => 'ص',
'𞸒' => 'ق',
'𞸓' => 'ر',
'𞸔' => 'ش',
'𞸕' => 'ت',
'𞸖' => 'ث',
'𞸗' => 'خ',
'𞸘' => 'ذ',
'𞸙' => 'ض',
'𞸚' => 'ظ',
'𞸛' => 'غ',
'𞸜' => 'ٮ',
'𞸝' => 'ں',
'𞸞' => 'ڡ',
'𞸟' => 'ٯ',
'𞸡' => 'ب',
'𞸢' => 'ج',
'𞸤' => 'ه',
'𞸧' => 'ح',
'𞸩' => 'ي',
'𞸪' => 'ك',
'𞸫' => 'ل',
'𞸬' => 'م',
'𞸭' => 'ن',
'𞸮' => 'س',
'𞸯' => 'ع',
'𞸰' => 'ف',
'𞸱' => 'ص',
'𞸲' => 'ق',
'𞸴' => 'ش',
'𞸵' => 'ت',
'𞸶' => 'ث',
'𞸷' => 'خ',
'𞸹' => 'ض',
'𞸻' => 'غ',
'𞹂' => 'ج',
'𞹇' => 'ح',
'𞹉' => 'ي',
'𞹋' => 'ل',
'𞹍' => 'ن',
'𞹎' => 'س',
'𞹏' => 'ع',
'𞹑' => 'ص',
'𞹒' => 'ق',
'𞹔' => 'ش',
'𞹗' => 'خ',
'𞹙' => 'ض',
'𞹛' => 'غ',
'𞹝' => 'ں',
'𞹟' => 'ٯ',
'𞹡' => 'ب',
'𞹢' => 'ج',
'𞹤' => 'ه',
'𞹧' => 'ح',
'𞹨' => 'ط',
'𞹩' => 'ي',
'𞹪' => 'ك',
'𞹬' => 'م',
'𞹭' => 'ن',
'𞹮' => 'س',
'𞹯' => 'ع',
'𞹰' => 'ف',
'𞹱' => 'ص',
'𞹲' => 'ق',
'𞹴' => 'ش',
'𞹵' => 'ت',
'𞹶' => 'ث',
'𞹷' => 'خ',
'𞹹' => 'ض',
'𞹺' => 'ظ',
'𞹻' => 'غ',
'𞹼' => 'ٮ',
'𞹾' => 'ڡ',
'𞺀' => 'ا',
'𞺁' => 'ب',
'𞺂' => 'ج',
'𞺃' => 'د',
'𞺄' => 'ه',
'𞺅' => 'و',
'𞺆' => 'ز',
'𞺇' => 'ح',
'𞺈' => 'ط',
'𞺉' => 'ي',
'𞺋' => 'ل',
'𞺌' => 'م',
'𞺍' => 'ن',
'𞺎' => 'س',
'𞺏' => 'ع',
'𞺐' => 'ف',
'𞺑' => 'ص',
'𞺒' => 'ق',
'𞺓' => 'ر',
'𞺔' => 'ش',
'𞺕' => 'ت',
'𞺖' => 'ث',
'𞺗' => 'خ',
'𞺘' => 'ذ',
'𞺙' => 'ض',
'𞺚' => 'ظ',
'𞺛' => 'غ',
'𞺡' => 'ب',
'𞺢' => 'ج',
'𞺣' => 'د',
'𞺥' => 'و',
'𞺦' => 'ز',
'𞺧' => 'ح',
'𞺨' => 'ط',
'𞺩' => 'ي',
'𞺫' => 'ل',
'𞺬' => 'م',
'𞺭' => 'ن',
'𞺮' => 'س',
'𞺯' => 'ع',
'𞺰' => 'ف',
'𞺱' => 'ص',
'𞺲' => 'ق',
'𞺳' => 'ر',
'𞺴' => 'ش',
'𞺵' => 'ت',
'𞺶' => 'ث',
'𞺷' => 'خ',
'𞺸' => 'ذ',
'𞺹' => 'ض',
'𞺺' => 'ظ',
'𞺻' => 'غ',
'🄀' => '0.',
'🄁' => '0,',
'🄂' => '1,',
'🄃' => '2,',
'🄄' => '3,',
'🄅' => '4,',
'🄆' => '5,',
'🄇' => '6,',
'🄈' => '7,',
'🄉' => '8,',
'🄊' => '9,',
'🄐' => '(A)',
'🄑' => '(B)',
'🄒' => '(C)',
'🄓' => '(D)',
'🄔' => '(E)',
'🄕' => '(F)',
'🄖' => '(G)',
'🄗' => '(H)',
'🄘' => '(I)',
'🄙' => '(J)',
'🄚' => '(K)',
'🄛' => '(L)',
'🄜' => '(M)',
'🄝' => '(N)',
'🄞' => '(O)',
'🄟' => '(P)',
'🄠' => '(Q)',
'🄡' => '(R)',
'🄢' => '(S)',
'🄣' => '(T)',
'🄤' => '(U)',
'🄥' => '(V)',
'🄦' => '(W)',
'🄧' => '(X)',
'🄨' => '(Y)',
'🄩' => '(Z)',
'🄪' => '〔S〕',
'🄫' => 'C',
'🄬' => 'R',
'🄭' => 'CD',
'🄮' => 'WZ',
'🄰' => 'A',
'🄱' => 'B',
'🄲' => 'C',
'🄳' => 'D',
'🄴' => 'E',
'🄵' => 'F',
'🄶' => 'G',
'🄷' => 'H',
'🄸' => 'I',
'🄹' => 'J',
'🄺' => 'K',
'🄻' => 'L',
'🄼' => 'M',
'🄽' => 'N',
'🄾' => 'O',
'🄿' => 'P',
'🅀' => 'Q',
'🅁' => 'R',
'🅂' => 'S',
'🅃' => 'T',
'🅄' => 'U',
'🅅' => 'V',
'🅆' => 'W',
'🅇' => 'X',
'🅈' => 'Y',
'🅉' => 'Z',
'🅊' => 'HV',
'🅋' => 'MV',
'🅌' => 'SD',
'🅍' => 'SS',
'🅎' => 'PPV',
'🅏' => 'WC',
'🅪' => 'MC',
'🅫' => 'MD',
'🅬' => 'MR',
'🆐' => 'DJ',
'🈀' => 'ほか',
'🈁' => 'ココ',
'🈂' => 'サ',
'🈐' => '手',
'🈑' => '字',
'🈒' => '双',
'🈓' => 'デ',
'🈔' => '二',
'🈕' => '多',
'🈖' => '解',
'🈗' => '天',
'🈘' => '交',
'🈙' => '映',
'🈚' => '無',
'🈛' => '料',
'🈜' => '前',
'🈝' => '後',
'🈞' => '再',
'🈟' => '新',
'🈠' => '初',
'🈡' => '終',
'🈢' => '生',
'🈣' => '販',
'🈤' => '声',
'🈥' => '吹',
'🈦' => '演',
'🈧' => '投',
'🈨' => '捕',
'🈩' => '一',
'🈪' => '三',
'🈫' => '遊',
'🈬' => '左',
'🈭' => '中',
'🈮' => '右',
'🈯' => '指',
'🈰' => '走',
'🈱' => '打',
'🈲' => '禁',
'🈳' => '空',
'🈴' => '合',
'🈵' => '満',
'🈶' => '有',
'🈷' => '月',
'🈸' => '申',
'🈹' => '割',
'🈺' => '営',
'🈻' => '配',
'🉀' => '〔本〕',
'🉁' => '〔三〕',
'🉂' => '〔二〕',
'🉃' => '〔安〕',
'🉄' => '〔点〕',
'🉅' => '〔打〕',
'🉆' => '〔盗〕',
'🉇' => '〔勝〕',
'🉈' => '〔敗〕',
'🉐' => '得',
'🉑' => '可',
'🯰' => '0',
'🯱' => '1',
'🯲' => '2',
'🯳' => '3',
'🯴' => '4',
'🯵' => '5',
'🯶' => '6',
'🯷' => '7',
'🯸' => '8',
'🯹' => '9',
);
<?php

return array (
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'ÿ' => 'ÿ',
'Ā' => 'Ā',
'ā' => 'ā',
'Ă' => 'Ă',
'ă' => 'ă',
'Ą' => 'Ą',
'ą' => 'ą',
'Ć' => 'Ć',
'ć' => 'ć',
'Ĉ' => 'Ĉ',
'ĉ' => 'ĉ',
'Ċ' => 'Ċ',
'ċ' => 'ċ',
'Č' => 'Č',
'č' => 'č',
'Ď' => 'Ď',
'ď' => 'ď',
'Ē' => 'Ē',
'ē' => 'ē',
'Ĕ' => 'Ĕ',
'ĕ' => 'ĕ',
'Ė' => 'Ė',
'ė' => 'ė',
'Ę' => 'Ę',
'ę' => 'ę',
'Ě' => 'Ě',
'ě' => 'ě',
'Ĝ' => 'Ĝ',
'ĝ' => 'ĝ',
'Ğ' => 'Ğ',
'ğ' => 'ğ',
'Ġ' => 'Ġ',
'ġ' => 'ġ',
'Ģ' => 'Ģ',
'ģ' => 'ģ',
'Ĥ' => 'Ĥ',
'ĥ' => 'ĥ',
'Ĩ' => 'Ĩ',
'ĩ' => 'ĩ',
'Ī' => 'Ī',
'ī' => 'ī',
'Ĭ' => 'Ĭ',
'ĭ' => 'ĭ',
'Į' => 'Į',
'į' => 'į',
'İ' => 'İ',
'Ĵ' => 'Ĵ',
'ĵ' => 'ĵ',
'Ķ' => 'Ķ',
'ķ' => 'ķ',
'Ĺ' => 'Ĺ',
'ĺ' => 'ĺ',
'Ļ' => 'Ļ',
'ļ' => 'ļ',
'Ľ' => 'Ľ',
'ľ' => 'ľ',
'Ń' => 'Ń',
'ń' => 'ń',
'Ņ' => 'Ņ',
'ņ' => 'ņ',
'Ň' => 'Ň',
'ň' => 'ň',
'Ō' => 'Ō',
'ō' => 'ō',
'Ŏ' => 'Ŏ',
'ŏ' => 'ŏ',
'Ő' => 'Ő',
'ő' => 'ő',
'Ŕ' => 'Ŕ',
'ŕ' => 'ŕ',
'Ŗ' => 'Ŗ',
'ŗ' => 'ŗ',
'Ř' => 'Ř',
'ř' => 'ř',
'Ś' => 'Ś',
'ś' => 'ś',
'Ŝ' => 'Ŝ',
'ŝ' => 'ŝ',
'Ş' => 'Ş',
'ş' => 'ş',
'Š' => 'Š',
'š' => 'š',
'Ţ' => 'Ţ',
'ţ' => 'ţ',
'Ť' => 'Ť',
'ť' => 'ť',
'Ũ' => 'Ũ',
'ũ' => 'ũ',
'Ū' => 'Ū',
'ū' => 'ū',
'Ŭ' => 'Ŭ',
'ŭ' => 'ŭ',
'Ů' => 'Ů',
'ů' => 'ů',
'Ű' => 'Ű',
'ű' => 'ű',
'Ų' => 'Ų',
'ų' => 'ų',
'Ŵ' => 'Ŵ',
'ŵ' => 'ŵ',
'Ŷ' => 'Ŷ',
'ŷ' => 'ŷ',
'Ÿ' => 'Ÿ',
'Ź' => 'Ź',
'ź' => 'ź',
'Ż' => 'Ż',
'ż' => 'ż',
'Ž' => 'Ž',
'ž' => 'ž',
'Ơ' => 'Ơ',
'ơ' => 'ơ',
'Ư' => 'Ư',
'ư' => 'ư',
'Ǎ' => 'Ǎ',
'ǎ' => 'ǎ',
'Ǐ' => 'Ǐ',
'ǐ' => 'ǐ',
'Ǒ' => 'Ǒ',
'ǒ' => 'ǒ',
'Ǔ' => 'Ǔ',
'ǔ' => 'ǔ',
'Ǖ' => 'Ǖ',
'ǖ' => 'ǖ',
'Ǘ' => 'Ǘ',
'ǘ' => 'ǘ',
'Ǚ' => 'Ǚ',
'ǚ' => 'ǚ',
'Ǜ' => 'Ǜ',
'ǜ' => 'ǜ',
'Ǟ' => 'Ǟ',
'ǟ' => 'ǟ',
'Ǡ' => 'Ǡ',
'ǡ' => 'ǡ',
'Ǣ' => 'Ǣ',
'ǣ' => 'ǣ',
'Ǧ' => 'Ǧ',
'ǧ' => 'ǧ',
'Ǩ' => 'Ǩ',
'ǩ' => 'ǩ',
'Ǫ' => 'Ǫ',
'ǫ' => 'ǫ',
'Ǭ' => 'Ǭ',
'ǭ' => 'ǭ',
'Ǯ' => 'Ǯ',
'ǯ' => 'ǯ',
'ǰ' => 'ǰ',
'Ǵ' => 'Ǵ',
'ǵ' => 'ǵ',
'Ǹ' => 'Ǹ',
'ǹ' => 'ǹ',
'Ǻ' => 'Ǻ',
'ǻ' => 'ǻ',
'Ǽ' => 'Ǽ',
'ǽ' => 'ǽ',
'Ǿ' => 'Ǿ',
'ǿ' => 'ǿ',
'Ȁ' => 'Ȁ',
'ȁ' => 'ȁ',
'Ȃ' => 'Ȃ',
'ȃ' => 'ȃ',
'Ȅ' => 'Ȅ',
'ȅ' => 'ȅ',
'Ȇ' => 'Ȇ',
'ȇ' => 'ȇ',
'Ȉ' => 'Ȉ',
'ȉ' => 'ȉ',
'Ȋ' => 'Ȋ',
'ȋ' => 'ȋ',
'Ȍ' => 'Ȍ',
'ȍ' => 'ȍ',
'Ȏ' => 'Ȏ',
'ȏ' => 'ȏ',
'Ȑ' => 'Ȑ',
'ȑ' => 'ȑ',
'Ȓ' => 'Ȓ',
'ȓ' => 'ȓ',
'Ȕ' => 'Ȕ',
'ȕ' => 'ȕ',
'Ȗ' => 'Ȗ',
'ȗ' => 'ȗ',
'Ș' => 'Ș',
'ș' => 'ș',
'Ț' => 'Ț',
'ț' => 'ț',
'Ȟ' => 'Ȟ',
'ȟ' => 'ȟ',
'Ȧ' => 'Ȧ',
'ȧ' => 'ȧ',
'Ȩ' => 'Ȩ',
'ȩ' => 'ȩ',
'Ȫ' => 'Ȫ',
'ȫ' => 'ȫ',
'Ȭ' => 'Ȭ',
'ȭ' => 'ȭ',
'Ȯ' => 'Ȯ',
'ȯ' => 'ȯ',
'Ȱ' => 'Ȱ',
'ȱ' => 'ȱ',
'Ȳ' => 'Ȳ',
'ȳ' => 'ȳ',
'΅' => '΅',
'Ά' => 'Ά',
'Έ' => 'Έ',
'Ή' => 'Ή',
'Ί' => 'Ί',
'Ό' => 'Ό',
'Ύ' => 'Ύ',
'Ώ' => 'Ώ',
'ΐ' => 'ΐ',
'Ϊ' => 'Ϊ',
'Ϋ' => 'Ϋ',
'ά' => 'ά',
'έ' => 'έ',
'ή' => 'ή',
'ί' => 'ί',
'ΰ' => 'ΰ',
'ϊ' => 'ϊ',
'ϋ' => 'ϋ',
'ό' => 'ό',
'ύ' => 'ύ',
'ώ' => 'ώ',
'ϓ' => 'ϓ',
'ϔ' => 'ϔ',
'Ѐ' => 'Ѐ',
'Ё' => 'Ё',
'Ѓ' => 'Ѓ',
'Ї' => 'Ї',
'Ќ' => 'Ќ',
'Ѝ' => 'Ѝ',
'Ў' => 'Ў',
'Й' => 'Й',
'й' => 'й',
'ѐ' => 'ѐ',
'ё' => 'ё',
'ѓ' => 'ѓ',
'ї' => 'ї',
'ќ' => 'ќ',
'ѝ' => 'ѝ',
'ў' => 'ў',
'Ѷ' => 'Ѷ',
'ѷ' => 'ѷ',
'Ӂ' => 'Ӂ',
'ӂ' => 'ӂ',
'Ӑ' => 'Ӑ',
'ӑ' => 'ӑ',
'Ӓ' => 'Ӓ',
'ӓ' => 'ӓ',
'Ӗ' => 'Ӗ',
'ӗ' => 'ӗ',
'Ӛ' => 'Ӛ',
'ӛ' => 'ӛ',
'Ӝ' => 'Ӝ',
'ӝ' => 'ӝ',
'Ӟ' => 'Ӟ',
'ӟ' => 'ӟ',
'Ӣ' => 'Ӣ',
'ӣ' => 'ӣ',
'Ӥ' => 'Ӥ',
'ӥ' => 'ӥ',
'Ӧ' => 'Ӧ',
'ӧ' => 'ӧ',
'Ӫ' => 'Ӫ',
'ӫ' => 'ӫ',
'Ӭ' => 'Ӭ',
'ӭ' => 'ӭ',
'Ӯ' => 'Ӯ',
'ӯ' => 'ӯ',
'Ӱ' => 'Ӱ',
'ӱ' => 'ӱ',
'Ӳ' => 'Ӳ',
'ӳ' => 'ӳ',
'Ӵ' => 'Ӵ',
'ӵ' => 'ӵ',
'Ӹ' => 'Ӹ',
'ӹ' => 'ӹ',
'آ' => 'آ',
'أ' => 'أ',
'ؤ' => 'ؤ',
'إ' => 'إ',
'ئ' => 'ئ',
'ۀ' => 'ۀ',
'ۂ' => 'ۂ',
'ۓ' => 'ۓ',
'ऩ' => 'ऩ',
'ऱ' => 'ऱ',
'ऴ' => 'ऴ',
'ো' => 'ো',
'ৌ' => 'ৌ',
'ୈ' => 'ୈ',
'ୋ' => 'ୋ',
'ୌ' => 'ୌ',
'ஔ' => 'ஔ',
'ொ' => 'ொ',
'ோ' => 'ோ',
'ௌ' => 'ௌ',
'ై' => 'ై',
'ೀ' => 'ೀ',
'ೇ' => 'ೇ',
'ೈ' => 'ೈ',
'ೊ' => 'ೊ',
'ೋ' => 'ೋ',
'ൊ' => 'ൊ',
'ോ' => 'ോ',
'ൌ' => 'ൌ',
'ේ' => 'ේ',
'ො' => 'ො',
'ෝ' => 'ෝ',
'ෞ' => 'ෞ',
'ဦ' => 'ဦ',
'ᬆ' => 'ᬆ',
'ᬈ' => 'ᬈ',
'ᬊ' => 'ᬊ',
'ᬌ' => 'ᬌ',
'ᬎ' => 'ᬎ',
'ᬒ' => 'ᬒ',
'ᬻ' => 'ᬻ',
'ᬽ' => 'ᬽ',
'ᭀ' => 'ᭀ',
'ᭁ' => 'ᭁ',
'ᭃ' => 'ᭃ',
'Ḁ' => 'Ḁ',
'ḁ' => 'ḁ',
'Ḃ' => 'Ḃ',
'ḃ' => 'ḃ',
'Ḅ' => 'Ḅ',
'ḅ' => 'ḅ',
'Ḇ' => 'Ḇ',
'ḇ' => 'ḇ',
'Ḉ' => 'Ḉ',
'ḉ' => 'ḉ',
'Ḋ' => 'Ḋ',
'ḋ' => 'ḋ',
'Ḍ' => 'Ḍ',
'ḍ' => 'ḍ',
'Ḏ' => 'Ḏ',
'ḏ' => 'ḏ',
'Ḑ' => 'Ḑ',
'ḑ' => 'ḑ',
'Ḓ' => 'Ḓ',
'ḓ' => 'ḓ',
'Ḕ' => 'Ḕ',
'ḕ' => 'ḕ',
'Ḗ' => 'Ḗ',
'ḗ' => 'ḗ',
'Ḙ' => 'Ḙ',
'ḙ' => 'ḙ',
'Ḛ' => 'Ḛ',
'ḛ' => 'ḛ',
'Ḝ' => 'Ḝ',
'ḝ' => 'ḝ',
'Ḟ' => 'Ḟ',
'ḟ' => 'ḟ',
'Ḡ' => 'Ḡ',
'ḡ' => 'ḡ',
'Ḣ' => 'Ḣ',
'ḣ' => 'ḣ',
'Ḥ' => 'Ḥ',
'ḥ' => 'ḥ',
'Ḧ' => 'Ḧ',
'ḧ' => 'ḧ',
'Ḩ' => 'Ḩ',
'ḩ' => 'ḩ',
'Ḫ' => 'Ḫ',
'ḫ' => 'ḫ',
'Ḭ' => 'Ḭ',
'ḭ' => 'ḭ',
'Ḯ' => 'Ḯ',
'ḯ' => 'ḯ',
'Ḱ' => 'Ḱ',
'ḱ' => 'ḱ',
'Ḳ' => 'Ḳ',
'ḳ' => 'ḳ',
'Ḵ' => 'Ḵ',
'ḵ' => 'ḵ',
'Ḷ' => 'Ḷ',
'ḷ' => 'ḷ',
'Ḹ' => 'Ḹ',
'ḹ' => 'ḹ',
'Ḻ' => 'Ḻ',
'ḻ' => 'ḻ',
'Ḽ' => 'Ḽ',
'ḽ' => 'ḽ',
'Ḿ' => 'Ḿ',
'ḿ' => 'ḿ',
'Ṁ' => 'Ṁ',
'ṁ' => 'ṁ',
'Ṃ' => 'Ṃ',
'ṃ' => 'ṃ',
'Ṅ' => 'Ṅ',
'ṅ' => 'ṅ',
'Ṇ' => 'Ṇ',
'ṇ' => 'ṇ',
'Ṉ' => 'Ṉ',
'ṉ' => 'ṉ',
'Ṋ' => 'Ṋ',
'ṋ' => 'ṋ',
'Ṍ' => 'Ṍ',
'ṍ' => 'ṍ',
'Ṏ' => 'Ṏ',
'ṏ' => 'ṏ',
'Ṑ' => 'Ṑ',
'ṑ' => 'ṑ',
'Ṓ' => 'Ṓ',
'ṓ' => 'ṓ',
'Ṕ' => 'Ṕ',
'ṕ' => 'ṕ',
'Ṗ' => 'Ṗ',
'ṗ' => 'ṗ',
'Ṙ' => 'Ṙ',
'ṙ' => 'ṙ',
'Ṛ' => 'Ṛ',
'ṛ' => 'ṛ',
'Ṝ' => 'Ṝ',
'ṝ' => 'ṝ',
'Ṟ' => 'Ṟ',
'ṟ' => 'ṟ',
'Ṡ' => 'Ṡ',
'ṡ' => 'ṡ',
'Ṣ' => 'Ṣ',
'ṣ' => 'ṣ',
'Ṥ' => 'Ṥ',
'ṥ' => 'ṥ',
'Ṧ' => 'Ṧ',
'ṧ' => 'ṧ',
'Ṩ' => 'Ṩ',
'ṩ' => 'ṩ',
'Ṫ' => 'Ṫ',
'ṫ' => 'ṫ',
'Ṭ' => 'Ṭ',
'ṭ' => 'ṭ',
'Ṯ' => 'Ṯ',
'ṯ' => 'ṯ',
'Ṱ' => 'Ṱ',
'ṱ' => 'ṱ',
'Ṳ' => 'Ṳ',
'ṳ' => 'ṳ',
'Ṵ' => 'Ṵ',
'ṵ' => 'ṵ',
'Ṷ' => 'Ṷ',
'ṷ' => 'ṷ',
'Ṹ' => 'Ṹ',
'ṹ' => 'ṹ',
'Ṻ' => 'Ṻ',
'ṻ' => 'ṻ',
'Ṽ' => 'Ṽ',
'ṽ' => 'ṽ',
'Ṿ' => 'Ṿ',
'ṿ' => 'ṿ',
'Ẁ' => 'Ẁ',
'ẁ' => 'ẁ',
'Ẃ' => 'Ẃ',
'ẃ' => 'ẃ',
'Ẅ' => 'Ẅ',
'ẅ' => 'ẅ',
'Ẇ' => 'Ẇ',
'ẇ' => 'ẇ',
'Ẉ' => 'Ẉ',
'ẉ' => 'ẉ',
'Ẋ' => 'Ẋ',
'ẋ' => 'ẋ',
'Ẍ' => 'Ẍ',
'ẍ' => 'ẍ',
'Ẏ' => 'Ẏ',
'ẏ' => 'ẏ',
'Ẑ' => 'Ẑ',
'ẑ' => 'ẑ',
'Ẓ' => 'Ẓ',
'ẓ' => 'ẓ',
'Ẕ' => 'Ẕ',
'ẕ' => 'ẕ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẛ' => 'ẛ',
'Ạ' => 'Ạ',
'ạ' => 'ạ',
'Ả' => 'Ả',
'ả' => 'ả',
'Ấ' => 'Ấ',
'ấ' => 'ấ',
'Ầ' => 'Ầ',
'ầ' => 'ầ',
'Ẩ' => 'Ẩ',
'ẩ' => 'ẩ',
'Ẫ' => 'Ẫ',
'ẫ' => 'ẫ',
'Ậ' => 'Ậ',
'ậ' => 'ậ',
'Ắ' => 'Ắ',
'ắ' => 'ắ',
'Ằ' => 'Ằ',
'ằ' => 'ằ',
'Ẳ' => 'Ẳ',
'ẳ' => 'ẳ',
'Ẵ' => 'Ẵ',
'ẵ' => 'ẵ',
'Ặ' => 'Ặ',
'ặ' => 'ặ',
'Ẹ' => 'Ẹ',
'ẹ' => 'ẹ',
'Ẻ' => 'Ẻ',
'ẻ' => 'ẻ',
'Ẽ' => 'Ẽ',
'ẽ' => 'ẽ',
'Ế' => 'Ế',
'ế' => 'ế',
'Ề' => 'Ề',
'ề' => 'ề',
'Ể' => 'Ể',
'ể' => 'ể',
'Ễ' => 'Ễ',
'ễ' => 'ễ',
'Ệ' => 'Ệ',
'ệ' => 'ệ',
'Ỉ' => 'Ỉ',
'ỉ' => 'ỉ',
'Ị' => 'Ị',
'ị' => 'ị',
'Ọ' => 'Ọ',
'ọ' => 'ọ',
'Ỏ' => 'Ỏ',
'ỏ' => 'ỏ',
'Ố' => 'Ố',
'ố' => 'ố',
'Ồ' => 'Ồ',
'ồ' => 'ồ',
'Ổ' => 'Ổ',
'ổ' => 'ổ',
'Ỗ' => 'Ỗ',
'ỗ' => 'ỗ',
'Ộ' => 'Ộ',
'ộ' => 'ộ',
'Ớ' => 'Ớ',
'ớ' => 'ớ',
'Ờ' => 'Ờ',
'ờ' => 'ờ',
'Ở' => 'Ở',
'ở' => 'ở',
'Ỡ' => 'Ỡ',
'ỡ' => 'ỡ',
'Ợ' => 'Ợ',
'ợ' => 'ợ',
'Ụ' => 'Ụ',
'ụ' => 'ụ',
'Ủ' => 'Ủ',
'ủ' => 'ủ',
'Ứ' => 'Ứ',
'ứ' => 'ứ',
'Ừ' => 'Ừ',
'ừ' => 'ừ',
'Ử' => 'Ử',
'ử' => 'ử',
'Ữ' => 'Ữ',
'ữ' => 'ữ',
'Ự' => 'Ự',
'ự' => 'ự',
'Ỳ' => 'Ỳ',
'ỳ' => 'ỳ',
'Ỵ' => 'Ỵ',
'ỵ' => 'ỵ',
'Ỷ' => 'Ỷ',
'ỷ' => 'ỷ',
'Ỹ' => 'Ỹ',
'ỹ' => 'ỹ',
'ἀ' => 'ἀ',
'ἁ' => 'ἁ',
'ἂ' => 'ἂ',
'ἃ' => 'ἃ',
'ἄ' => 'ἄ',
'ἅ' => 'ἅ',
'ἆ' => 'ἆ',
'ἇ' => 'ἇ',
'Ἀ' => 'Ἀ',
'Ἁ' => 'Ἁ',
'Ἂ' => 'Ἂ',
'Ἃ' => 'Ἃ',
'Ἄ' => 'Ἄ',
'Ἅ' => 'Ἅ',
'Ἆ' => 'Ἆ',
'Ἇ' => 'Ἇ',
'ἐ' => 'ἐ',
'ἑ' => 'ἑ',
'ἒ' => 'ἒ',
'ἓ' => 'ἓ',
'ἔ' => 'ἔ',
'ἕ' => 'ἕ',
'Ἐ' => 'Ἐ',
'Ἑ' => 'Ἑ',
'Ἒ' => 'Ἒ',
'Ἓ' => 'Ἓ',
'Ἔ' => 'Ἔ',
'Ἕ' => 'Ἕ',
'ἠ' => 'ἠ',
'ἡ' => 'ἡ',
'ἢ' => 'ἢ',
'ἣ' => 'ἣ',
'ἤ' => 'ἤ',
'ἥ' => 'ἥ',
'ἦ' => 'ἦ',
'ἧ' => 'ἧ',
'Ἠ' => 'Ἠ',
'Ἡ' => 'Ἡ',
'Ἢ' => 'Ἢ',
'Ἣ' => 'Ἣ',
'Ἤ' => 'Ἤ',
'Ἥ' => 'Ἥ',
'Ἦ' => 'Ἦ',
'Ἧ' => 'Ἧ',
'ἰ' => 'ἰ',
'ἱ' => 'ἱ',
'ἲ' => 'ἲ',
'ἳ' => 'ἳ',
'ἴ' => 'ἴ',
'ἵ' => 'ἵ',
'ἶ' => 'ἶ',
'ἷ' => 'ἷ',
'Ἰ' => 'Ἰ',
'Ἱ' => 'Ἱ',
'Ἲ' => 'Ἲ',
'Ἳ' => 'Ἳ',
'Ἴ' => 'Ἴ',
'Ἵ' => 'Ἵ',
'Ἶ' => 'Ἶ',
'Ἷ' => 'Ἷ',
'ὀ' => 'ὀ',
'ὁ' => 'ὁ',
'ὂ' => 'ὂ',
'ὃ' => 'ὃ',
'ὄ' => 'ὄ',
'ὅ' => 'ὅ',
'Ὀ' => 'Ὀ',
'Ὁ' => 'Ὁ',
'Ὂ' => 'Ὂ',
'Ὃ' => 'Ὃ',
'Ὄ' => 'Ὄ',
'Ὅ' => 'Ὅ',
'ὐ' => 'ὐ',
'ὑ' => 'ὑ',
'ὒ' => 'ὒ',
'ὓ' => 'ὓ',
'ὔ' => 'ὔ',
'ὕ' => 'ὕ',
'ὖ' => 'ὖ',
'ὗ' => 'ὗ',
'Ὑ' => 'Ὑ',
'Ὓ' => 'Ὓ',
'Ὕ' => 'Ὕ',
'Ὗ' => 'Ὗ',
'ὠ' => 'ὠ',
'ὡ' => 'ὡ',
'ὢ' => 'ὢ',
'ὣ' => 'ὣ',
'ὤ' => 'ὤ',
'ὥ' => 'ὥ',
'ὦ' => 'ὦ',
'ὧ' => 'ὧ',
'Ὠ' => 'Ὠ',
'Ὡ' => 'Ὡ',
'Ὢ' => 'Ὢ',
'Ὣ' => 'Ὣ',
'Ὤ' => 'Ὤ',
'Ὥ' => 'Ὥ',
'Ὦ' => 'Ὦ',
'Ὧ' => 'Ὧ',
'ὰ' => 'ὰ',
'ὲ' => 'ὲ',
'ὴ' => 'ὴ',
'ὶ' => 'ὶ',
'ὸ' => 'ὸ',
'ὺ' => 'ὺ',
'ὼ' => 'ὼ',
'ᾀ' => 'ᾀ',
'ᾁ' => 'ᾁ',
'ᾂ' => 'ᾂ',
'ᾃ' => 'ᾃ',
'ᾄ' => 'ᾄ',
'ᾅ' => 'ᾅ',
'ᾆ' => 'ᾆ',
'ᾇ' => 'ᾇ',
'ᾈ' => 'ᾈ',
'ᾉ' => 'ᾉ',
'ᾊ' => 'ᾊ',
'ᾋ' => 'ᾋ',
'ᾌ' => 'ᾌ',
'ᾍ' => 'ᾍ',
'ᾎ' => 'ᾎ',
'ᾏ' => 'ᾏ',
'ᾐ' => 'ᾐ',
'ᾑ' => 'ᾑ',
'ᾒ' => 'ᾒ',
'ᾓ' => 'ᾓ',
'ᾔ' => 'ᾔ',
'ᾕ' => 'ᾕ',
'ᾖ' => 'ᾖ',
'ᾗ' => 'ᾗ',
'ᾘ' => 'ᾘ',
'ᾙ' => 'ᾙ',
'ᾚ' => 'ᾚ',
'ᾛ' => 'ᾛ',
'ᾜ' => 'ᾜ',
'ᾝ' => 'ᾝ',
'ᾞ' => 'ᾞ',
'ᾟ' => 'ᾟ',
'ᾠ' => 'ᾠ',
'ᾡ' => 'ᾡ',
'ᾢ' => 'ᾢ',
'ᾣ' => 'ᾣ',
'ᾤ' => 'ᾤ',
'ᾥ' => 'ᾥ',
'ᾦ' => 'ᾦ',
'ᾧ' => 'ᾧ',
'ᾨ' => 'ᾨ',
'ᾩ' => 'ᾩ',
'ᾪ' => 'ᾪ',
'ᾫ' => 'ᾫ',
'ᾬ' => 'ᾬ',
'ᾭ' => 'ᾭ',
'ᾮ' => 'ᾮ',
'ᾯ' => 'ᾯ',
'ᾰ' => 'ᾰ',
'ᾱ' => 'ᾱ',
'ᾲ' => 'ᾲ',
'ᾳ' => 'ᾳ',
'ᾴ' => 'ᾴ',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾷ',
'Ᾰ' => 'Ᾰ',
'Ᾱ' => 'Ᾱ',
'Ὰ' => 'Ὰ',
'ᾼ' => 'ᾼ',
'῁' => '῁',
'ῂ' => 'ῂ',
'ῃ' => 'ῃ',
'ῄ' => 'ῄ',
'ῆ' => 'ῆ',
'ῇ' => 'ῇ',
'Ὲ' => 'Ὲ',
'Ὴ' => 'Ὴ',
'ῌ' => 'ῌ',
'῍' => '῍',
'῎' => '῎',
'῏' => '῏',
'ῐ' => 'ῐ',
'ῑ' => 'ῑ',
'ῒ' => 'ῒ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'Ῐ' => 'Ῐ',
'Ῑ' => 'Ῑ',
'Ὶ' => 'Ὶ',
'῝' => '῝',
'῞' => '῞',
'῟' => '῟',
'ῠ' => 'ῠ',
'ῡ' => 'ῡ',
'ῢ' => 'ῢ',
'ῤ' => 'ῤ',
'ῥ' => 'ῥ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'Ῠ' => 'Ῠ',
'Ῡ' => 'Ῡ',
'Ὺ' => 'Ὺ',
'Ῥ' => 'Ῥ',
'῭' => '῭',
'ῲ' => 'ῲ',
'ῳ' => 'ῳ',
'ῴ' => 'ῴ',
'ῶ' => 'ῶ',
'ῷ' => 'ῷ',
'Ὸ' => 'Ὸ',
'Ὼ' => 'Ὼ',
'ῼ' => 'ῼ',
'↚' => '↚',
'↛' => '↛',
'↮' => '↮',
'⇍' => '⇍',
'⇎' => '⇎',
'⇏' => '⇏',
'∄' => '∄',
'∉' => '∉',
'∌' => '∌',
'∤' => '∤',
'∦' => '∦',
'≁' => '≁',
'≄' => '≄',
'≇' => '≇',
'≉' => '≉',
'≠' => '≠',
'≢' => '≢',
'≭' => '≭',
'≮' => '≮',
'≯' => '≯',
'≰' => '≰',
'≱' => '≱',
'≴' => '≴',
'≵' => '≵',
'≸' => '≸',
'≹' => '≹',
'⊀' => '⊀',
'⊁' => '⊁',
'⊄' => '⊄',
'⊅' => '⊅',
'⊈' => '⊈',
'⊉' => '⊉',
'⊬' => '⊬',
'⊭' => '⊭',
'⊮' => '⊮',
'⊯' => '⊯',
'⋠' => '⋠',
'⋡' => '⋡',
'⋢' => '⋢',
'⋣' => '⋣',
'⋪' => '⋪',
'⋫' => '⋫',
'⋬' => '⋬',
'⋭' => '⋭',
'が' => 'が',
'ぎ' => 'ぎ',
'ぐ' => 'ぐ',
'げ' => 'げ',
'ご' => 'ご',
'ざ' => 'ざ',
'じ' => 'じ',
'ず' => 'ず',
'ぜ' => 'ぜ',
'ぞ' => 'ぞ',
'だ' => 'だ',
'ぢ' => 'ぢ',
'づ' => 'づ',
'で' => 'で',
'ど' => 'ど',
'ば' => 'ば',
'ぱ' => 'ぱ',
'び' => 'び',
'ぴ' => 'ぴ',
'ぶ' => 'ぶ',
'ぷ' => 'ぷ',
'べ' => 'べ',
'ぺ' => 'ぺ',
'ぼ' => 'ぼ',
'ぽ' => 'ぽ',
'ゔ' => 'ゔ',
'ゞ' => 'ゞ',
'ガ' => 'ガ',
'ギ' => 'ギ',
'グ' => 'グ',
'ゲ' => 'ゲ',
'ゴ' => 'ゴ',
'ザ' => 'ザ',
'ジ' => 'ジ',
'ズ' => 'ズ',
'ゼ' => 'ゼ',
'ゾ' => 'ゾ',
'ダ' => 'ダ',
'ヂ' => 'ヂ',
'ヅ' => 'ヅ',
'デ' => 'デ',
'ド' => 'ド',
'バ' => 'バ',
'パ' => 'パ',
'ビ' => 'ビ',
'ピ' => 'ピ',
'ブ' => 'ブ',
'プ' => 'プ',
'ベ' => 'ベ',
'ペ' => 'ペ',
'ボ' => 'ボ',
'ポ' => 'ポ',
'ヴ' => 'ヴ',
'ヷ' => 'ヷ',
'ヸ' => 'ヸ',
'ヹ' => 'ヹ',
'ヺ' => 'ヺ',
'ヾ' => 'ヾ',
'𑂚' => '𑂚',
'𑂜' => '𑂜',
'𑂫' => '𑂫',
'𑄮' => '𑄮',
'𑄯' => '𑄯',
'𑍋' => '𑍋',
'𑍌' => '𑍌',
'𑒻' => '𑒻',
'𑒼' => '𑒼',
'𑒾' => '𑒾',
'𑖺' => '𑖺',
'𑖻' => '𑖻',
'𑤸' => '𑤸',
);
<?php










use Symfony\Polyfill\Intl\Normalizer as p;

if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}

if (!function_exists('normalizer_is_normalized')) {
function normalizer_is_normalized($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::isNormalized($string, $form); }
}
if (!function_exists('normalizer_normalize')) {
function normalizer_normalize($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::normalize($string, $form); }
}
<?php










namespace Symfony\Polyfill\Intl\Normalizer;











class Normalizer
{
public const FORM_D = \Normalizer::FORM_D;
public const FORM_KD = \Normalizer::FORM_KD;
public const FORM_C = \Normalizer::FORM_C;
public const FORM_KC = \Normalizer::FORM_KC;
public const NFD = \Normalizer::NFD;
public const NFKD = \Normalizer::NFKD;
public const NFC = \Normalizer::NFC;
public const NFKC = \Normalizer::NFKC;

private static $C;
private static $D;
private static $KD;
private static $cC;
private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";

public static function isNormalized(string $s, int $form = self::FORM_C)
{
if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) {
return false;
}
if (!isset($s[strspn($s, self::$ASCII)])) {
return true;
}
if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) {
return true;
}

return self::normalize($s, $form) === $s;
}

public static function normalize(string $s, int $form = self::FORM_C)
{
if (!preg_match('//u', $s)) {
return false;
}

switch ($form) {
case self::NFC: $C = true; $K = false; break;
case self::NFD: $C = false; $K = false; break;
case self::NFKC: $C = true; $K = true; break;
case self::NFKD: $C = false; $K = true; break;
default:
if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) {
return $s;
}

if (80000 > \PHP_VERSION_ID) {
return false;
}

throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form');
}

if ('' === $s) {
return '';
}

if ($K && null === self::$KD) {
self::$KD = self::getData('compatibilityDecomposition');
}

if (null === self::$D) {
self::$D = self::getData('canonicalDecomposition');
self::$cC = self::getData('combiningClass');
}

if (null !== $mbEncoding = (2  & (int) \ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) {
mb_internal_encoding('8bit');
}

$r = self::decompose($s, $K);

if ($C) {
if (null === self::$C) {
self::$C = self::getData('canonicalComposition');
}

$r = self::recompose($r);
}
if (null !== $mbEncoding) {
mb_internal_encoding($mbEncoding);
}

return $r;
}

private static function recompose($s)
{
$ASCII = self::$ASCII;
$compMap = self::$C;
$combClass = self::$cC;
$ulenMask = self::$ulenMask;

$result = $tail = '';

$i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"];
$len = \strlen($s);

$lastUchr = substr($s, 0, $i);
$lastUcls = isset($combClass[$lastUchr]) ? 256 : 0;

while ($i < $len) {
if ($s[$i] < "\x80") {


if ($tail) {
$lastUchr .= $tail;
$tail = '';
}

if ($j = strspn($s, $ASCII, $i + 1)) {
$lastUchr .= substr($s, $i, $j);
$i += $j;
}

$result .= $lastUchr;
$lastUchr = $s[$i];
$lastUcls = 0;
++$i;
continue;
}

$ulen = $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);

if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr
|| $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr
|| $lastUcls) {


$ucls = $combClass[$uchr] ?? 0;

if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) {
$lastUchr = $compMap[$lastUchr.$uchr];
} elseif ($lastUcls = $ucls) {
$tail .= $uchr;
} else {
if ($tail) {
$lastUchr .= $tail;
$tail = '';
}

$result .= $lastUchr;
$lastUchr = $uchr;
}
} else {


$L = \ord($lastUchr[2]) - 0x80;
$V = \ord($uchr[2]) - 0xA1;
$T = 0;

$uchr = substr($s, $i + $ulen, 3);

if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") {
$T = \ord($uchr[2]) - 0xA7;
0 > $T && $T += 0x40;
$ulen += 3;
}

$L = 0xAC00 + ($L * 21 + $V) * 28 + $T;
$lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F);
}

$i += $ulen;
}

return $result.$lastUchr.$tail;
}

private static function decompose($s, $c)
{
$result = '';

$ASCII = self::$ASCII;
$decompMap = self::$D;
$combClass = self::$cC;
$ulenMask = self::$ulenMask;
if ($c) {
$compatMap = self::$KD;
}

$c = [];
$i = 0;
$len = \strlen($s);

while ($i < $len) {
if ($s[$i] < "\x80") {


if ($c) {
ksort($c);
$result .= implode('', $c);
$c = [];
}

$j = 1 + strspn($s, $ASCII, $i + 1);
$result .= substr($s, $i, $j);
$i += $j;
continue;
}

$ulen = $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;

if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) {


if ($uchr !== $j = $compatMap[$uchr] ?? ($decompMap[$uchr] ?? $uchr)) {
$uchr = $j;

$j = \strlen($uchr);
$ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"];

if ($ulen != $j) {


$j -= $ulen;
$i -= $j;

if (0 > $i) {
$s = str_repeat(' ', -$i).$s;
$len -= $i;
$i = 0;
}

while ($j--) {
$s[$i + $j] = $uchr[$ulen + $j];
}

$uchr = substr($uchr, 0, $ulen);
}
}
if (isset($combClass[$uchr])) {


if (!isset($c[$combClass[$uchr]])) {
$c[$combClass[$uchr]] = '';
}
$c[$combClass[$uchr]] .= $uchr;
continue;
}
} else {


$uchr = unpack('C*', $uchr);
$j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80;

$uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588))
."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28));

if ($j %= 28) {
$uchr .= $j < 25
? ("\xE1\x86".\chr(0xA7 + $j))
: ("\xE1\x87".\chr(0x67 + $j));
}
}
if ($c) {
ksort($c);
$result .= implode('', $c);
$c = [];
}

$result .= $uchr;
}

if ($c) {
ksort($c);
$result .= implode('', $c);
}

return $result;
}

private static function getData($file)
{
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
return require $file;
}

return false;
}
}
<?php










use Symfony\Polyfill\Intl\Normalizer as p;

if (!function_exists('normalizer_is_normalized')) {
function normalizer_is_normalized(?string $string, ?int $form = p\Normalizer::FORM_C): bool { return p\Normalizer::isNormalized((string) $string, (int) $form); }
}
if (!function_exists('normalizer_normalize')) {
function normalizer_normalize(?string $string, ?int $form = p\Normalizer::FORM_C): string|false { return p\Normalizer::normalize((string) $string, (int) $form); }
}
<?php










namespace Symfony\Component\BrowserKit\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\BrowserKit\AbstractBrowser;

final class BrowserHasCookie extends Constraint
{
private string $name;
private string $path;
private ?string $domain;

public function __construct(string $name, string $path = '/', string $domain = null)
{
$this->name = $name;
$this->path = $path;
$this->domain = $domain;
}




public function toString(): string
{
$str = sprintf('has cookie "%s"', $this->name);
if ('/' !== $this->path) {
$str .= sprintf(' with path "%s"', $this->path);
}
if ($this->domain) {
$str .= sprintf(' for domain "%s"', $this->domain);
}

return $str;
}






protected function matches($browser): bool
{
return null !== $browser->getCookieJar()->get($this->name, $this->path, $this->domain);
}






protected function failureDescription($browser): string
{
return 'the Browser '.$this->toString();
}
}
<?php










namespace Symfony\Component\BrowserKit\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\BrowserKit\AbstractBrowser;

final class BrowserCookieValueSame extends Constraint
{
private string $name;
private string $value;
private bool $raw;
private string $path;
private ?string $domain;

public function __construct(string $name, string $value, bool $raw = false, string $path = '/', string $domain = null)
{
$this->name = $name;
$this->path = $path;
$this->domain = $domain;
$this->value = $value;
$this->raw = $raw;
}




public function toString(): string
{
$str = sprintf('has cookie "%s"', $this->name);
if ('/' !== $this->path) {
$str .= sprintf(' with path "%s"', $this->path);
}
if ($this->domain) {
$str .= sprintf(' for domain "%s"', $this->domain);
}
$str .= sprintf(' with %svalue "%s"', $this->raw ? 'raw ' : '', $this->value);

return $str;
}






protected function matches($browser): bool
{
$cookie = $browser->getCookieJar()->get($this->name, $this->path, $this->domain);
if (!$cookie) {
return false;
}

return $this->value === ($this->raw ? $cookie->getRawValue() : $cookie->getValue());
}






protected function failureDescription($browser): string
{
return 'the Browser '.$this->toString();
}
}
<?php










namespace Symfony\Component\BrowserKit;

use Symfony\Component\BrowserKit\Exception\BadMethodCallException;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\DomCrawler\Form;
use Symfony\Component\DomCrawler\Link;
use Symfony\Component\Process\PhpProcess;











abstract class AbstractBrowser
{
protected $history;
protected $cookieJar;
protected $server = [];
protected $internalRequest;
protected $request;
protected $internalResponse;
protected $response;
protected $crawler;
protected $insulated = false;
protected $redirect;
protected $followRedirects = true;
protected $followMetaRefresh = false;

private int $maxRedirects = -1;
private int $redirectCount = 0;
private array $redirects = [];
private bool $isMainRequest = true;




public function __construct(array $server = [], History $history = null, CookieJar $cookieJar = null)
{
$this->setServerParameters($server);
$this->history = $history ?? new History();
$this->cookieJar = $cookieJar ?? new CookieJar();
}




public function followRedirects(bool $followRedirects = true)
{
$this->followRedirects = $followRedirects;
}




public function followMetaRefresh(bool $followMetaRefresh = true)
{
$this->followMetaRefresh = $followMetaRefresh;
}




public function isFollowingRedirects(): bool
{
return $this->followRedirects;
}




public function setMaxRedirects(int $maxRedirects)
{
$this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects;
$this->followRedirects = -1 != $this->maxRedirects;
}




public function getMaxRedirects(): int
{
return $this->maxRedirects;
}






public function insulate(bool $insulated = true)
{
if ($insulated && !class_exists(\Symfony\Component\Process\Process::class)) {
throw new \LogicException('Unable to isolate requests as the Symfony Process Component is not installed.');
}

$this->insulated = $insulated;
}




public function setServerParameters(array $server)
{
$this->server = array_merge([
'HTTP_USER_AGENT' => 'Symfony BrowserKit',
], $server);
}




public function setServerParameter(string $key, string $value)
{
$this->server[$key] = $value;
}




public function getServerParameter(string $key, mixed $default = ''): mixed
{
return $this->server[$key] ?? $default;
}

public function xmlHttpRequest(string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null, bool $changeHistory = true): Crawler
{
$this->setServerParameter('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');

try {
return $this->request($method, $uri, $parameters, $files, $server, $content, $changeHistory);
} finally {
unset($this->server['HTTP_X_REQUESTED_WITH']);
}
}




public function jsonRequest(string $method, string $uri, array $parameters = [], array $server = [], bool $changeHistory = true): Crawler
{
$content = json_encode($parameters);

$this->setServerParameter('CONTENT_TYPE', 'application/json');
$this->setServerParameter('HTTP_ACCEPT', 'application/json');

try {
return $this->request($method, $uri, [], [], $server, $content, $changeHistory);
} finally {
unset($this->server['CONTENT_TYPE']);
unset($this->server['HTTP_ACCEPT']);
}
}




public function getHistory(): History
{
return $this->history;
}




public function getCookieJar(): CookieJar
{
return $this->cookieJar;
}




public function getCrawler(): Crawler
{
if (null === $this->crawler) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

return $this->crawler;
}




public function getInternalResponse(): Response
{
if (null === $this->internalResponse) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

return $this->internalResponse;
}









public function getResponse(): object
{
if (null === $this->response) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

return $this->response;
}




public function getInternalRequest(): Request
{
if (null === $this->internalRequest) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

return $this->internalRequest;
}









public function getRequest(): object
{
if (null === $this->request) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

return $this->request;
}




public function click(Link $link): Crawler
{
if ($link instanceof Form) {
return $this->submit($link);
}

return $this->request($link->getMethod(), $link->getUri());
}






public function clickLink(string $linkText): Crawler
{
if (null === $this->crawler) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

return $this->click($this->crawler->selectLink($linkText)->link());
}







public function submit(Form $form, array $values = [], array $serverParameters = []): Crawler
{
$form->setValues($values);

return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles(), $serverParameters);
}










public function submitForm(string $button, array $fieldValues = [], string $method = 'POST', array $serverParameters = []): Crawler
{
if (null === $this->crawler) {
throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__));
}

$buttonNode = $this->crawler->selectButton($button);
$form = $buttonNode->form($fieldValues, $method);

return $this->submit($form, [], $serverParameters);
}












public function request(string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null, bool $changeHistory = true): Crawler
{
if ($this->isMainRequest) {
$this->redirectCount = 0;
} else {
++$this->redirectCount;
}

$originalUri = $uri;

$uri = $this->getAbsoluteUri($uri);

$server = array_merge($this->server, $server);

if (!empty($server['HTTP_HOST']) && null === parse_url($originalUri, \PHP_URL_HOST)) {
$uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri);
}

if (isset($server['HTTPS']) && null === parse_url($originalUri, \PHP_URL_SCHEME)) {
$uri = preg_replace('{^'.parse_url($uri, \PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri);
}

if (!isset($server['HTTP_REFERER']) && !$this->history->isEmpty()) {
$server['HTTP_REFERER'] = $this->history->current()->getUri();
}

if (empty($server['HTTP_HOST'])) {
$server['HTTP_HOST'] = $this->extractHost($uri);
}

$server['HTTPS'] = 'https' == parse_url($uri, \PHP_URL_SCHEME);

$this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);

$this->request = $this->filterRequest($this->internalRequest);

if (true === $changeHistory) {
$this->history->add($this->internalRequest);
}

if ($this->insulated) {
$this->response = $this->doRequestInProcess($this->request);
} else {
$this->response = $this->doRequest($this->request);
}

$this->internalResponse = $this->filterResponse($this->response);

$this->cookieJar->updateFromResponse($this->internalResponse, $uri);

$status = $this->internalResponse->getStatusCode();

if ($status >= 300 && $status < 400) {
$this->redirect = $this->internalResponse->getHeader('Location');
} else {
$this->redirect = null;
}

if ($this->followRedirects && $this->redirect) {
$this->redirects[serialize($this->history->current())] = true;

return $this->crawler = $this->followRedirect();
}

$this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type') ?? '');


if ($this->followMetaRefresh && null !== $redirect = $this->getMetaRefreshUrl()) {
$this->redirect = $redirect;
$this->redirects[serialize($this->history->current())] = true;
$this->crawler = $this->followRedirect();
}

return $this->crawler;
}








protected function doRequestInProcess(object $request)
{
$deprecationsFile = tempnam(sys_get_temp_dir(), 'deprec');
putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$deprecationsFile);
$_ENV['SYMFONY_DEPRECATIONS_SERIALIZE'] = $deprecationsFile;
$process = new PhpProcess($this->getScript($request), null, null);
$process->run();

if (file_exists($deprecationsFile)) {
$deprecations = file_get_contents($deprecationsFile);
unlink($deprecationsFile);
foreach ($deprecations ? unserialize($deprecations) : [] as $deprecation) {
if ($deprecation[0]) {

trigger_error($deprecation[1], \E_USER_DEPRECATED);
} else {
@trigger_error($deprecation[1], \E_USER_DEPRECATED);
}
}
}

if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s.', $process->getOutput(), $process->getErrorOutput()));
}

return unserialize($process->getOutput());
}






abstract protected function doRequest(object $request);








protected function getScript(object $request)
{
throw new \LogicException('To insulate requests, you need to override the getScript() method.');
}






protected function filterRequest(Request $request)
{
return $request;
}






protected function filterResponse(object $response)
{
return $response;
}






protected function createCrawlerFromContent(string $uri, string $content, string $type): ?Crawler
{
if (!class_exists(Crawler::class)) {
return null;
}

$crawler = new Crawler(null, $uri);
$crawler->addContent($content, $type);

return $crawler;
}




public function back(): Crawler
{
do {
$request = $this->history->back();
} while (\array_key_exists(serialize($request), $this->redirects));

return $this->requestFromRequest($request, false);
}




public function forward(): Crawler
{
do {
$request = $this->history->forward();
} while (\array_key_exists(serialize($request), $this->redirects));

return $this->requestFromRequest($request, false);
}




public function reload(): Crawler
{
return $this->requestFromRequest($this->history->current(), false);
}






public function followRedirect(): Crawler
{
if (empty($this->redirect)) {
throw new \LogicException('The request was not redirected.');
}

if (-1 !== $this->maxRedirects) {
if ($this->redirectCount > $this->maxRedirects) {
$this->redirectCount = 0;
throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects));
}
}

$request = $this->internalRequest;

if (\in_array($this->internalResponse->getStatusCode(), [301, 302, 303])) {
$method = 'GET';
$files = [];
$content = null;
} else {
$method = $request->getMethod();
$files = $request->getFiles();
$content = $request->getContent();
}

if ('GET' === strtoupper($method)) {

$parameters = [];
} else {
$parameters = $request->getParameters();
}

$server = $request->getServer();
$server = $this->updateServerFromUri($server, $this->redirect);

$this->isMainRequest = false;

$response = $this->request($method, $this->redirect, $parameters, $files, $server, $content);

$this->isMainRequest = true;

return $response;
}




private function getMetaRefreshUrl(): ?string
{
$metaRefresh = $this->getCrawler()->filter('head meta[http-equiv="refresh"]');
foreach ($metaRefresh->extract(['content']) as $content) {
if (preg_match('/^\s*0\s*;\s*URL\s*=\s*(?|\'([^\']++)|"([^"]++)|([^\'"].*))/i', $content, $m)) {
return str_replace("\t\r\n", '', rtrim($m[1]));
}
}

return null;
}






public function restart()
{
$this->cookieJar->clear();
$this->history->clear();
}




protected function getAbsoluteUri(string $uri): string
{

if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) {
return $uri;
}

if (!$this->history->isEmpty()) {
$currentUri = $this->history->current()->getUri();
} else {
$currentUri = sprintf('http%s://%s/',
isset($this->server['HTTPS']) ? 's' : '',
$this->server['HTTP_HOST'] ?? 'localhost'
);
}


if ('' !== trim($uri, '/') && str_starts_with($uri, '//')) {
return parse_url($currentUri, \PHP_URL_SCHEME).':'.$uri;
}


if (!$uri || '#' == $uri[0] || '?' == $uri[0]) {
return preg_replace('/[#?].*?$/', '', $currentUri).$uri;
}

if ('/' !== $uri[0]) {
$path = parse_url($currentUri, \PHP_URL_PATH);

if ('/' !== substr($path, -1)) {
$path = substr($path, 0, strrpos($path, '/') + 1);
}

$uri = $path.$uri;
}

return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
}






protected function requestFromRequest(Request $request, bool $changeHistory = true): Crawler
{
return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
}

private function updateServerFromUri(array $server, string $uri): array
{
$server['HTTP_HOST'] = $this->extractHost($uri);
$scheme = parse_url($uri, \PHP_URL_SCHEME);
$server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme;
unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']);

return $server;
}

private function extractHost(string $uri): ?string
{
$host = parse_url($uri, \PHP_URL_HOST);

if ($port = parse_url($uri, \PHP_URL_PORT)) {
return $host.':'.$port;
}

return $host;
}
}
<?php










namespace Symfony\Component\BrowserKit;

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\Mime\Part\AbstractPart;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;
use Symfony\Component\Mime\Part\TextPart;
use Symfony\Contracts\HttpClient\HttpClientInterface;







class HttpBrowser extends AbstractBrowser
{
private $client;

public function __construct(HttpClientInterface $client = null, History $history = null, CookieJar $cookieJar = null)
{
if (!$client && !class_exists(HttpClient::class)) {
throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
}

$this->client = $client ?? HttpClient::create();

parent::__construct([], $history, $cookieJar);
}




protected function doRequest(object $request): Response
{
$headers = $this->getHeaders($request);
[$body, $extraHeaders] = $this->getBodyAndExtraHeaders($request, $headers);

$response = $this->client->request($request->getMethod(), $request->getUri(), [
'headers' => array_merge($headers, $extraHeaders),
'body' => $body,
'max_redirects' => 0,
]);

return new Response($response->getContent(false), $response->getStatusCode(), $response->getHeaders(false));
}




private function getBodyAndExtraHeaders(Request $request, array $headers): array
{
if (\in_array($request->getMethod(), ['GET', 'HEAD']) && !isset($headers['content-type'])) {
return ['', []];
}

if (!class_exists(AbstractPart::class)) {
throw new \LogicException('You cannot pass non-empty bodies as the Mime component is not installed. Try running "composer require symfony/mime".');
}

if (null !== $content = $request->getContent()) {
if (isset($headers['content-type'])) {
return [$content, []];
}

$part = new TextPart($content, 'utf-8', 'plain', '8bit');

return [$part->bodyToString(), $part->getPreparedHeaders()->toArray()];
}

$fields = $request->getParameters();

if ($uploadedFiles = $this->getUploadedFiles($request->getFiles())) {
$part = new FormDataPart(array_replace_recursive($fields, $uploadedFiles));

return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()];
}

if (empty($fields)) {
return ['', []];
}

array_walk_recursive($fields, $caster = static function (&$v) use (&$caster) {
if (\is_object($v)) {
if ($vars = get_object_vars($v)) {
array_walk_recursive($vars, $caster);
$v = $vars;
} elseif (method_exists($v, '__toString')) {
$v = (string) $v;
}
}
});

return [http_build_query($fields, '', '&'), ['Content-Type' => 'application/x-www-form-urlencoded']];
}

protected function getHeaders(Request $request): array
{
$headers = [];
foreach ($request->getServer() as $key => $value) {
$key = strtolower(str_replace('_', '-', $key));
$contentHeaders = ['content-length' => true, 'content-md5' => true, 'content-type' => true];
if (str_starts_with($key, 'http-')) {
$headers[substr($key, 5)] = $value;
} elseif (isset($contentHeaders[$key])) {

$headers[$key] = $value;
}
}
$cookies = [];
foreach ($this->getCookieJar()->allRawValues($request->getUri()) as $name => $value) {
$cookies[] = $name.'='.$value;
}
if ($cookies) {
$headers['cookie'] = implode('; ', $cookies);
}

return $headers;
}





private function getUploadedFiles(array $files): array
{
$uploadedFiles = [];
foreach ($files as $name => $file) {
if (!\is_array($file)) {
return $uploadedFiles;
}
if (!isset($file['tmp_name'])) {
$uploadedFiles[$name] = $this->getUploadedFiles($file);
}
if (isset($file['tmp_name'])) {
$uploadedFiles[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
}
}

return $uploadedFiles;
}
}
<?php










namespace Symfony\Component\BrowserKit;




class Request
{
protected $uri;
protected $method;
protected $parameters;
protected $files;
protected $cookies;
protected $server;
protected $content;










public function __construct(string $uri, string $method, array $parameters = [], array $files = [], array $cookies = [], array $server = [], string $content = null)
{
$this->uri = $uri;
$this->method = $method;

array_walk_recursive($parameters, static function (&$value) {
$value = (string) $value;
});

$this->parameters = $parameters;
$this->files = $files;
$this->cookies = $cookies;
$this->server = $server;
$this->content = $content;
}




public function getUri(): string
{
return $this->uri;
}




public function getMethod(): string
{
return $this->method;
}




public function getParameters(): array
{
return $this->parameters;
}




public function getFiles(): array
{
return $this->files;
}




public function getCookies(): array
{
return $this->cookies;
}




public function getServer(): array
{
return $this->server;
}




public function getContent(): ?string
{
return $this->content;
}
}
<?php










namespace Symfony\Component\BrowserKit;






class History
{
protected $stack = [];
protected $position = -1;




public function clear()
{
$this->stack = [];
$this->position = -1;
}




public function add(Request $request)
{
$this->stack = \array_slice($this->stack, 0, $this->position + 1);
$this->stack[] = clone $request;
$this->position = \count($this->stack) - 1;
}




public function isEmpty(): bool
{
return 0 == \count($this->stack);
}






public function back(): Request
{
if ($this->position < 1) {
throw new \LogicException('You are already on the first page.');
}

return clone $this->stack[--$this->position];
}






public function forward(): Request
{
if ($this->position > \count($this->stack) - 2) {
throw new \LogicException('You are already on the last page.');
}

return clone $this->stack[++$this->position];
}






public function current(): Request
{
if (-1 == $this->position) {
throw new \LogicException('The page history is empty.');
}

return clone $this->stack[$this->position];
}
}
<?php










namespace Symfony\Component\BrowserKit;




final class Response
{
private string $content;
private int $status;
private array $headers;









public function __construct(string $content = '', int $status = 200, array $headers = [])
{
$this->content = $content;
$this->status = $status;
$this->headers = $headers;
}




public function __toString(): string
{
$headers = '';
foreach ($this->headers as $name => $value) {
if (\is_string($value)) {
$headers .= sprintf("%s: %s\n", $name, $value);
} else {
foreach ($value as $headerValue) {
$headers .= sprintf("%s: %s\n", $name, $headerValue);
}
}
}

return $headers."\n".$this->content;
}

public function getContent(): string
{
return $this->content;
}

public function getStatusCode(): int
{
return $this->status;
}

public function getHeaders(): array
{
return $this->headers;
}




public function getHeader(string $header, bool $first = true): string|array|null
{
$normalizedHeader = str_replace('-', '_', strtolower($header));
foreach ($this->headers as $key => $value) {
if (str_replace('-', '_', strtolower($key)) === $normalizedHeader) {
if ($first) {
return \is_array($value) ? (\count($value) ? $value[0] : '') : $value;
}

return \is_array($value) ? $value : [$value];
}
}

return $first ? null : [];
}
}
<?php










namespace Symfony\Component\BrowserKit;






class Cookie
{




private const DATE_FORMATS = [
'D, d M Y H:i:s T',
'D, d-M-y H:i:s T',
'D, d-M-Y H:i:s T',
'D, d-m-y H:i:s T',
'D, d-m-Y H:i:s T',
'D M j G:i:s Y',
'D M d H:i:s Y T',
];

protected $name;
protected $value;
protected $expires;
protected $path;
protected $domain;
protected $secure;
protected $httponly;
protected $rawValue;
private ?string $samesite;














public function __construct(string $name, ?string $value, string $expires = null, string $path = null, string $domain = '', bool $secure = false, bool $httponly = true, bool $encodedValue = false, string $samesite = null)
{
if ($encodedValue) {
$this->value = urldecode($value);
$this->rawValue = $value;
} else {
$this->value = $value;
$this->rawValue = rawurlencode($value ?? '');
}
$this->name = $name;
$this->path = empty($path) ? '/' : $path;
$this->domain = $domain;
$this->secure = $secure;
$this->httponly = $httponly;
$this->samesite = $samesite;

if (null !== $expires) {
$timestampAsDateTime = \DateTime::createFromFormat('U', $expires);
if (false === $timestampAsDateTime) {
throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.', $expires));
}

$this->expires = $timestampAsDateTime->format('U');
}
}




public function __toString(): string
{
$cookie = sprintf('%s=%s', $this->name, $this->rawValue);

if (null !== $this->expires) {
$dateTime = \DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT'));
$cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::DATE_FORMATS[0]));
}

if ('' !== $this->domain) {
$cookie .= '; domain='.$this->domain;
}

if ($this->path) {
$cookie .= '; path='.$this->path;
}

if ($this->secure) {
$cookie .= '; secure';
}

if ($this->httponly) {
$cookie .= '; httponly';
}

if (null !== $this->samesite) {
$cookie .= '; samesite='.$this->samesite;
}

return $cookie;
}






public static function fromString(string $cookie, string $url = null): static
{
$parts = explode(';', $cookie);

if (!str_contains($parts[0], '=')) {
throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0]));
}

[$name, $value] = explode('=', array_shift($parts), 2);

$values = [
'name' => trim($name),
'value' => trim($value),
'expires' => null,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => false,
'passedRawValue' => true,
'samesite' => null,
];

if (null !== $url) {
if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) {
throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url));
}

$values['domain'] = $urlParts['host'];
$values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : '';
}

foreach ($parts as $part) {
$part = trim($part);

if ('secure' === strtolower($part)) {

if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) {
continue;
}

$values['secure'] = true;

continue;
}

if ('httponly' === strtolower($part)) {
$values['httponly'] = true;

continue;
}

if (2 === \count($elements = explode('=', $part, 2))) {
if ('expires' === strtolower($elements[0])) {
$elements[1] = self::parseDate($elements[1]);
}

$values[strtolower($elements[0])] = $elements[1];
}
}

return new static(
$values['name'],
$values['value'],
$values['expires'],
$values['path'],
$values['domain'],
$values['secure'],
$values['httponly'],
$values['passedRawValue'],
$values['samesite']
);
}

private static function parseDate(string $dateValue): ?string
{

if (($length = \strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length - 1]) {
$dateValue = substr($dateValue, 1, -1);
}

foreach (self::DATE_FORMATS as $dateFormat) {
if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) {
return $date->format('U');
}
}


if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) {
return $date->format('U');
}

return null;
}




public function getName(): string
{
return $this->name;
}




public function getValue(): string
{
return $this->value;
}




public function getRawValue(): string
{
return $this->rawValue;
}




public function getExpiresTime(): ?string
{
return $this->expires;
}




public function getPath(): string
{
return $this->path;
}




public function getDomain(): string
{
return $this->domain;
}




public function isSecure(): bool
{
return $this->secure;
}




public function isHttpOnly(): bool
{
return $this->httponly;
}




public function isExpired(): bool
{
return null !== $this->expires && 0 != $this->expires && $this->expires <= time();
}




public function getSameSite(): ?string
{
return $this->samesite;
}
}
<?php










namespace Symfony\Component\BrowserKit;






class CookieJar
{
protected $cookieJar = [];

public function set(Cookie $cookie)
{
$this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
}









public function get(string $name, string $path = '/', string $domain = null): ?Cookie
{
$this->flushExpiredCookies();

foreach ($this->cookieJar as $cookieDomain => $pathCookies) {
if ($cookieDomain && $domain) {
$cookieDomain = '.'.ltrim($cookieDomain, '.');
if (!str_ends_with('.'.$domain, $cookieDomain)) {
continue;
}
}

foreach ($pathCookies as $cookiePath => $namedCookies) {
if (!str_starts_with($path, $cookiePath)) {
continue;
}
if (isset($namedCookies[$name])) {
return $namedCookies[$name];
}
}
}

return null;
}








public function expire(string $name, ?string $path = '/', string $domain = null)
{
if (null === $path) {
$path = '/';
}

if (empty($domain)) {


$domains = array_keys($this->cookieJar);
} else {
$domains = [$domain];
}

foreach ($domains as $domain) {
unset($this->cookieJar[$domain][$path][$name]);

if (empty($this->cookieJar[$domain][$path])) {
unset($this->cookieJar[$domain][$path]);

if (empty($this->cookieJar[$domain])) {
unset($this->cookieJar[$domain]);
}
}
}
}




public function clear()
{
$this->cookieJar = [];
}






public function updateFromSetCookie(array $setCookies, string $uri = null)
{
$cookies = [];

foreach ($setCookies as $cookie) {
foreach (explode(',', $cookie) as $i => $part) {
if (0 === $i || preg_match('/^(?P<token>\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) {
$cookies[] = ltrim($part);
} else {
$cookies[\count($cookies) - 1] .= ','.$part;
}
}
}

foreach ($cookies as $cookie) {
try {
$this->set(Cookie::fromString($cookie, $uri));
} catch (\InvalidArgumentException $e) {

}
}
}




public function updateFromResponse(Response $response, string $uri = null)
{
$this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri);
}






public function all(): array
{
$this->flushExpiredCookies();

$flattenedCookies = [];
foreach ($this->cookieJar as $path) {
foreach ($path as $cookies) {
foreach ($cookies as $cookie) {
$flattenedCookies[] = $cookie;
}
}
}

return $flattenedCookies;
}




public function allValues(string $uri, bool $returnsRawValue = false): array
{
$this->flushExpiredCookies();

$parts = array_replace(['path' => '/'], parse_url($uri));
$cookies = [];
foreach ($this->cookieJar as $domain => $pathCookies) {
if ($domain) {
$domain = '.'.ltrim($domain, '.');
if ($domain != substr('.'.$parts['host'], -\strlen($domain))) {
continue;
}
}

foreach ($pathCookies as $path => $namedCookies) {
if ($path != substr($parts['path'], 0, \strlen($path))) {
continue;
}

foreach ($namedCookies as $cookie) {
if ($cookie->isSecure() && 'https' != $parts['scheme']) {
continue;
}

$cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue();
}
}
}

return $cookies;
}




public function allRawValues(string $uri): array
{
return $this->allValues($uri, true);
}




public function flushExpiredCookies()
{
foreach ($this->cookieJar as $domain => $pathCookies) {
foreach ($pathCookies as $path => $namedCookies) {
foreach ($namedCookies as $name => $cookie) {
if ($cookie->isExpired()) {
unset($this->cookieJar[$domain][$path][$name]);
}
}
}
}
}
}
<?php










namespace Symfony\Component\BrowserKit\Exception;

class BadMethodCallException extends \BadMethodCallException
{
}
<?php










namespace Symfony\Contracts\EventDispatcher;

use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface;




interface EventDispatcherInterface extends PsrEventDispatcherInterface
{









public function dispatch(object $event, string $eventName = null): object;
}
<?php










namespace Symfony\Contracts\EventDispatcher;

use Psr\EventDispatcher\StoppableEventInterface;
















class Event implements StoppableEventInterface
{
private bool $propagationStopped = false;




public function isPropagationStopped(): bool
{
return $this->propagationStopped;
}








public function stopPropagation(): void
{
$this->propagationStopped = true;
}
}
<?php










if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) {
class PhpToken extends Symfony\Polyfill\Php80\PhpToken
{
}
}
<?php










if (\PHP_VERSION_ID < 80000) {
interface Stringable
{



public function __toString();
}
}
<?php










#[Attribute(Attribute::TARGET_CLASS)]
final class Attribute
{
public const TARGET_CLASS = 1;
public const TARGET_FUNCTION = 2;
public const TARGET_METHOD = 4;
public const TARGET_PROPERTY = 8;
public const TARGET_CLASS_CONSTANT = 16;
public const TARGET_PARAMETER = 32;
public const TARGET_ALL = 63;
public const IS_REPEATABLE = 64;


public $flags;

public function __construct(int $flags = self::TARGET_ALL)
{
$this->flags = $flags;
}
}
<?php










if (\PHP_VERSION_ID < 80000) {
class UnhandledMatchError extends Error
{
}
}
<?php










if (\PHP_VERSION_ID < 80000) {
class ValueError extends Error
{
}
}
<?php










namespace Symfony\Polyfill\Php80;








final class Php80
{
public static function fdiv(float $dividend, float $divisor): float
{
return @($dividend / $divisor);
}

public static function get_debug_type($value): string
{
switch (true) {
case null === $value: return 'null';
case \is_bool($value): return 'bool';
case \is_string($value): return 'string';
case \is_array($value): return 'array';
case \is_int($value): return 'int';
case \is_float($value): return 'float';
case \is_object($value): break;
case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class';
default:
if (null === $type = @get_resource_type($value)) {
return 'unknown';
}

if ('Unknown' === $type) {
$type = 'closed';
}

return "resource ($type)";
}

$class = \get_class($value);

if (false === strpos($class, '@')) {
return $class;
}

return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous';
}

public static function get_resource_id($res): int
{
if (!\is_resource($res) && null === @get_resource_type($res)) {
throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
}

return (int) $res;
}

public static function preg_last_error_msg(): string
{
switch (preg_last_error()) {
case \PREG_INTERNAL_ERROR:
return 'Internal error';
case \PREG_BAD_UTF8_ERROR:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
case \PREG_BAD_UTF8_OFFSET_ERROR:
return 'The offset did not correspond to the beginning of a valid UTF-8 code point';
case \PREG_BACKTRACK_LIMIT_ERROR:
return 'Backtrack limit exhausted';
case \PREG_RECURSION_LIMIT_ERROR:
return 'Recursion limit exhausted';
case \PREG_JIT_STACKLIMIT_ERROR:
return 'JIT stack limit exhausted';
case \PREG_NO_ERROR:
return 'No error';
default:
return 'Unknown error';
}
}

public static function str_contains(string $haystack, string $needle): bool
{
return '' === $needle || false !== strpos($haystack, $needle);
}

public static function str_starts_with(string $haystack, string $needle): bool
{
return 0 === strncmp($haystack, $needle, \strlen($needle));
}

public static function str_ends_with(string $haystack, string $needle): bool
{
if ('' === $needle || $needle === $haystack) {
return true;
}

if ('' === $haystack) {
return false;
}

$needleLength = \strlen($needle);

return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength);
}
}
<?php










use Symfony\Polyfill\Php80 as p;

if (\PHP_VERSION_ID >= 80000) {
return;
}

if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) {
define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN);
}

if (!function_exists('fdiv')) {
function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); }
}
if (!function_exists('preg_last_error_msg')) {
function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); }
}
if (!function_exists('str_contains')) {
function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); }
}
if (!function_exists('str_starts_with')) {
function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); }
}
if (!function_exists('str_ends_with')) {
function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); }
}
if (!function_exists('get_debug_type')) {
function get_debug_type($value): string { return p\Php80::get_debug_type($value); }
}
if (!function_exists('get_resource_id')) {
function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); }
}
<?php










namespace Symfony\Polyfill\Php80;






class PhpToken implements \Stringable
{



public $id;




public $text;




public $line;




public $pos;

public function __construct(int $id, string $text, int $line = -1, int $position = -1)
{
$this->id = $id;
$this->text = $text;
$this->line = $line;
$this->pos = $position;
}

public function getTokenName(): ?string
{
if ('UNKNOWN' === $name = token_name($this->id)) {
$name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text;
}

return $name;
}




public function is($kind): bool
{
foreach ((array) $kind as $value) {
if (\in_array($value, [$this->id, $this->text], true)) {
return true;
}
}

return false;
}

public function isIgnorable(): bool
{
return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true);
}

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




public static function tokenize(string $code, int $flags = 0): array
{
$line = 1;
$position = 0;
$tokens = token_get_all($code, $flags);
foreach ($tokens as $index => $token) {
if (\is_string($token)) {
$id = \ord($token);
$text = $token;
} else {
[$id, $text, $line] = $token;
}
$tokens[$index] = new static($id, $text, $line, $position);
$position += \strlen($text);
}

return $tokens;
}
}
<?php

return array (
'a' => 'A',
'b' => 'B',
'c' => 'C',
'd' => 'D',
'e' => 'E',
'f' => 'F',
'g' => 'G',
'h' => 'H',
'i' => 'I',
'j' => 'J',
'k' => 'K',
'l' => 'L',
'm' => 'M',
'n' => 'N',
'o' => 'O',
'p' => 'P',
'q' => 'Q',
'r' => 'R',
's' => 'S',
't' => 'T',
'u' => 'U',
'v' => 'V',
'w' => 'W',
'x' => 'X',
'y' => 'Y',
'z' => 'Z',
'µ' => 'Μ',
'à' => 'À',
'á' => 'Á',
'â' => 'Â',
'ã' => 'Ã',
'ä' => 'Ä',
'å' => 'Å',
'æ' => 'Æ',
'ç' => 'Ç',
'è' => 'È',
'é' => 'É',
'ê' => 'Ê',
'ë' => 'Ë',
'ì' => 'Ì',
'í' => 'Í',
'î' => 'Î',
'ï' => 'Ï',
'ð' => 'Ð',
'ñ' => 'Ñ',
'ò' => 'Ò',
'ó' => 'Ó',
'ô' => 'Ô',
'õ' => 'Õ',
'ö' => 'Ö',
'ø' => 'Ø',
'ù' => 'Ù',
'ú' => 'Ú',
'û' => 'Û',
'ü' => 'Ü',
'ý' => 'Ý',
'þ' => 'Þ',
'ÿ' => 'Ÿ',
'ā' => 'Ā',
'ă' => 'Ă',
'ą' => 'Ą',
'ć' => 'Ć',
'ĉ' => 'Ĉ',
'ċ' => 'Ċ',
'č' => 'Č',
'ď' => 'Ď',
'đ' => 'Đ',
'ē' => 'Ē',
'ĕ' => 'Ĕ',
'ė' => 'Ė',
'ę' => 'Ę',
'ě' => 'Ě',
'ĝ' => 'Ĝ',
'ğ' => 'Ğ',
'ġ' => 'Ġ',
'ģ' => 'Ģ',
'ĥ' => 'Ĥ',
'ħ' => 'Ħ',
'ĩ' => 'Ĩ',
'ī' => 'Ī',
'ĭ' => 'Ĭ',
'į' => 'Į',
'ı' => 'I',
'ĳ' => 'Ĳ',
'ĵ' => 'Ĵ',
'ķ' => 'Ķ',
'ĺ' => 'Ĺ',
'ļ' => 'Ļ',
'ľ' => 'Ľ',
'ŀ' => 'Ŀ',
'ł' => 'Ł',
'ń' => 'Ń',
'ņ' => 'Ņ',
'ň' => 'Ň',
'ŋ' => 'Ŋ',
'ō' => 'Ō',
'ŏ' => 'Ŏ',
'ő' => 'Ő',
'œ' => 'Œ',
'ŕ' => 'Ŕ',
'ŗ' => 'Ŗ',
'ř' => 'Ř',
'ś' => 'Ś',
'ŝ' => 'Ŝ',
'ş' => 'Ş',
'š' => 'Š',
'ţ' => 'Ţ',
'ť' => 'Ť',
'ŧ' => 'Ŧ',
'ũ' => 'Ũ',
'ū' => 'Ū',
'ŭ' => 'Ŭ',
'ů' => 'Ů',
'ű' => 'Ű',
'ų' => 'Ų',
'ŵ' => 'Ŵ',
'ŷ' => 'Ŷ',
'ź' => 'Ź',
'ż' => 'Ż',
'ž' => 'Ž',
'ſ' => 'S',
'ƀ' => 'Ƀ',
'ƃ' => 'Ƃ',
'ƅ' => 'Ƅ',
'ƈ' => 'Ƈ',
'ƌ' => 'Ƌ',
'ƒ' => 'Ƒ',
'ƕ' => 'Ƕ',
'ƙ' => 'Ƙ',
'ƚ' => 'Ƚ',
'ƞ' => 'Ƞ',
'ơ' => 'Ơ',
'ƣ' => 'Ƣ',
'ƥ' => 'Ƥ',
'ƨ' => 'Ƨ',
'ƭ' => 'Ƭ',
'ư' => 'Ư',
'ƴ' => 'Ƴ',
'ƶ' => 'Ƶ',
'ƹ' => 'Ƹ',
'ƽ' => 'Ƽ',
'ƿ' => 'Ƿ',
'ǅ' => 'Ǆ',
'ǆ' => 'Ǆ',
'ǈ' => 'Ǉ',
'ǉ' => 'Ǉ',
'ǋ' => 'Ǌ',
'ǌ' => 'Ǌ',
'ǎ' => 'Ǎ',
'ǐ' => 'Ǐ',
'ǒ' => 'Ǒ',
'ǔ' => 'Ǔ',
'ǖ' => 'Ǖ',
'ǘ' => 'Ǘ',
'ǚ' => 'Ǚ',
'ǜ' => 'Ǜ',
'ǝ' => 'Ǝ',
'ǟ' => 'Ǟ',
'ǡ' => 'Ǡ',
'ǣ' => 'Ǣ',
'ǥ' => 'Ǥ',
'ǧ' => 'Ǧ',
'ǩ' => 'Ǩ',
'ǫ' => 'Ǫ',
'ǭ' => 'Ǭ',
'ǯ' => 'Ǯ',
'ǲ' => 'Ǳ',
'ǳ' => 'Ǳ',
'ǵ' => 'Ǵ',
'ǹ' => 'Ǹ',
'ǻ' => 'Ǻ',
'ǽ' => 'Ǽ',
'ǿ' => 'Ǿ',
'ȁ' => 'Ȁ',
'ȃ' => 'Ȃ',
'ȅ' => 'Ȅ',
'ȇ' => 'Ȇ',
'ȉ' => 'Ȉ',
'ȋ' => 'Ȋ',
'ȍ' => 'Ȍ',
'ȏ' => 'Ȏ',
'ȑ' => 'Ȑ',
'ȓ' => 'Ȓ',
'ȕ' => 'Ȕ',
'ȗ' => 'Ȗ',
'ș' => 'Ș',
'ț' => 'Ț',
'ȝ' => 'Ȝ',
'ȟ' => 'Ȟ',
'ȣ' => 'Ȣ',
'ȥ' => 'Ȥ',
'ȧ' => 'Ȧ',
'ȩ' => 'Ȩ',
'ȫ' => 'Ȫ',
'ȭ' => 'Ȭ',
'ȯ' => 'Ȯ',
'ȱ' => 'Ȱ',
'ȳ' => 'Ȳ',
'ȼ' => 'Ȼ',
'ȿ' => 'Ȿ',
'ɀ' => 'Ɀ',
'ɂ' => 'Ɂ',
'ɇ' => 'Ɇ',
'ɉ' => 'Ɉ',
'ɋ' => 'Ɋ',
'ɍ' => 'Ɍ',
'ɏ' => 'Ɏ',
'ɐ' => 'Ɐ',
'ɑ' => 'Ɑ',
'ɒ' => 'Ɒ',
'ɓ' => 'Ɓ',
'ɔ' => 'Ɔ',
'ɖ' => 'Ɖ',
'ɗ' => 'Ɗ',
'ə' => 'Ə',
'ɛ' => 'Ɛ',
'ɜ' => 'Ɜ',
'ɠ' => 'Ɠ',
'ɡ' => 'Ɡ',
'ɣ' => 'Ɣ',
'ɥ' => 'Ɥ',
'ɦ' => 'Ɦ',
'ɨ' => 'Ɨ',
'ɩ' => 'Ɩ',
'ɪ' => 'Ɪ',
'ɫ' => 'Ɫ',
'ɬ' => 'Ɬ',
'ɯ' => 'Ɯ',
'ɱ' => 'Ɱ',
'ɲ' => 'Ɲ',
'ɵ' => 'Ɵ',
'ɽ' => 'Ɽ',
'ʀ' => 'Ʀ',
'ʂ' => 'Ʂ',
'ʃ' => 'Ʃ',
'ʇ' => 'Ʇ',
'ʈ' => 'Ʈ',
'ʉ' => 'Ʉ',
'ʊ' => 'Ʊ',
'ʋ' => 'Ʋ',
'ʌ' => 'Ʌ',
'ʒ' => 'Ʒ',
'ʝ' => 'Ʝ',
'ʞ' => 'Ʞ',
'ͅ' => 'Ι',
'ͱ' => 'Ͱ',
'ͳ' => 'Ͳ',
'ͷ' => 'Ͷ',
'ͻ' => 'Ͻ',
'ͼ' => 'Ͼ',
'ͽ' => 'Ͽ',
'ά' => 'Ά',
'έ' => 'Έ',
'ή' => 'Ή',
'ί' => 'Ί',
'α' => 'Α',
'β' => 'Β',
'γ' => 'Γ',
'δ' => 'Δ',
'ε' => 'Ε',
'ζ' => 'Ζ',
'η' => 'Η',
'θ' => 'Θ',
'ι' => 'Ι',
'κ' => 'Κ',
'λ' => 'Λ',
'μ' => 'Μ',
'ν' => 'Ν',
'ξ' => 'Ξ',
'ο' => 'Ο',
'π' => 'Π',
'ρ' => 'Ρ',
'ς' => 'Σ',
'σ' => 'Σ',
'τ' => 'Τ',
'υ' => 'Υ',
'φ' => 'Φ',
'χ' => 'Χ',
'ψ' => 'Ψ',
'ω' => 'Ω',
'ϊ' => 'Ϊ',
'ϋ' => 'Ϋ',
'ό' => 'Ό',
'ύ' => 'Ύ',
'ώ' => 'Ώ',
'ϐ' => 'Β',
'ϑ' => 'Θ',
'ϕ' => 'Φ',
'ϖ' => 'Π',
'ϗ' => 'Ϗ',
'ϙ' => 'Ϙ',
'ϛ' => 'Ϛ',
'ϝ' => 'Ϝ',
'ϟ' => 'Ϟ',
'ϡ' => 'Ϡ',
'ϣ' => 'Ϣ',
'ϥ' => 'Ϥ',
'ϧ' => 'Ϧ',
'ϩ' => 'Ϩ',
'ϫ' => 'Ϫ',
'ϭ' => 'Ϭ',
'ϯ' => 'Ϯ',
'ϰ' => 'Κ',
'ϱ' => 'Ρ',
'ϲ' => 'Ϲ',
'ϳ' => 'Ϳ',
'ϵ' => 'Ε',
'ϸ' => 'Ϸ',
'ϻ' => 'Ϻ',
'а' => 'А',
'б' => 'Б',
'в' => 'В',
'г' => 'Г',
'д' => 'Д',
'е' => 'Е',
'ж' => 'Ж',
'з' => 'З',
'и' => 'И',
'й' => 'Й',
'к' => 'К',
'л' => 'Л',
'м' => 'М',
'н' => 'Н',
'о' => 'О',
'п' => 'П',
'р' => 'Р',
'с' => 'С',
'т' => 'Т',
'у' => 'У',
'ф' => 'Ф',
'х' => 'Х',
'ц' => 'Ц',
'ч' => 'Ч',
'ш' => 'Ш',
'щ' => 'Щ',
'ъ' => 'Ъ',
'ы' => 'Ы',
'ь' => 'Ь',
'э' => 'Э',
'ю' => 'Ю',
'я' => 'Я',
'ѐ' => 'Ѐ',
'ё' => 'Ё',
'ђ' => 'Ђ',
'ѓ' => 'Ѓ',
'є' => 'Є',
'ѕ' => 'Ѕ',
'і' => 'І',
'ї' => 'Ї',
'ј' => 'Ј',
'љ' => 'Љ',
'њ' => 'Њ',
'ћ' => 'Ћ',
'ќ' => 'Ќ',
'ѝ' => 'Ѝ',
'ў' => 'Ў',
'џ' => 'Џ',
'ѡ' => 'Ѡ',
'ѣ' => 'Ѣ',
'ѥ' => 'Ѥ',
'ѧ' => 'Ѧ',
'ѩ' => 'Ѩ',
'ѫ' => 'Ѫ',
'ѭ' => 'Ѭ',
'ѯ' => 'Ѯ',
'ѱ' => 'Ѱ',
'ѳ' => 'Ѳ',
'ѵ' => 'Ѵ',
'ѷ' => 'Ѷ',
'ѹ' => 'Ѹ',
'ѻ' => 'Ѻ',
'ѽ' => 'Ѽ',
'ѿ' => 'Ѿ',
'ҁ' => 'Ҁ',
'ҋ' => 'Ҋ',
'ҍ' => 'Ҍ',
'ҏ' => 'Ҏ',
'ґ' => 'Ґ',
'ғ' => 'Ғ',
'ҕ' => 'Ҕ',
'җ' => 'Җ',
'ҙ' => 'Ҙ',
'қ' => 'Қ',
'ҝ' => 'Ҝ',
'ҟ' => 'Ҟ',
'ҡ' => 'Ҡ',
'ң' => 'Ң',
'ҥ' => 'Ҥ',
'ҧ' => 'Ҧ',
'ҩ' => 'Ҩ',
'ҫ' => 'Ҫ',
'ҭ' => 'Ҭ',
'ү' => 'Ү',
'ұ' => 'Ұ',
'ҳ' => 'Ҳ',
'ҵ' => 'Ҵ',
'ҷ' => 'Ҷ',
'ҹ' => 'Ҹ',
'һ' => 'Һ',
'ҽ' => 'Ҽ',
'ҿ' => 'Ҿ',
'ӂ' => 'Ӂ',
'ӄ' => 'Ӄ',
'ӆ' => 'Ӆ',
'ӈ' => 'Ӈ',
'ӊ' => 'Ӊ',
'ӌ' => 'Ӌ',
'ӎ' => 'Ӎ',
'ӏ' => 'Ӏ',
'ӑ' => 'Ӑ',
'ӓ' => 'Ӓ',
'ӕ' => 'Ӕ',
'ӗ' => 'Ӗ',
'ә' => 'Ә',
'ӛ' => 'Ӛ',
'ӝ' => 'Ӝ',
'ӟ' => 'Ӟ',
'ӡ' => 'Ӡ',
'ӣ' => 'Ӣ',
'ӥ' => 'Ӥ',
'ӧ' => 'Ӧ',
'ө' => 'Ө',
'ӫ' => 'Ӫ',
'ӭ' => 'Ӭ',
'ӯ' => 'Ӯ',
'ӱ' => 'Ӱ',
'ӳ' => 'Ӳ',
'ӵ' => 'Ӵ',
'ӷ' => 'Ӷ',
'ӹ' => 'Ӹ',
'ӻ' => 'Ӻ',
'ӽ' => 'Ӽ',
'ӿ' => 'Ӿ',
'ԁ' => 'Ԁ',
'ԃ' => 'Ԃ',
'ԅ' => 'Ԅ',
'ԇ' => 'Ԇ',
'ԉ' => 'Ԉ',
'ԋ' => 'Ԋ',
'ԍ' => 'Ԍ',
'ԏ' => 'Ԏ',
'ԑ' => 'Ԑ',
'ԓ' => 'Ԓ',
'ԕ' => 'Ԕ',
'ԗ' => 'Ԗ',
'ԙ' => 'Ԙ',
'ԛ' => 'Ԛ',
'ԝ' => 'Ԝ',
'ԟ' => 'Ԟ',
'ԡ' => 'Ԡ',
'ԣ' => 'Ԣ',
'ԥ' => 'Ԥ',
'ԧ' => 'Ԧ',
'ԩ' => 'Ԩ',
'ԫ' => 'Ԫ',
'ԭ' => 'Ԭ',
'ԯ' => 'Ԯ',
'ա' => 'Ա',
'բ' => 'Բ',
'գ' => 'Գ',
'դ' => 'Դ',
'ե' => 'Ե',
'զ' => 'Զ',
'է' => 'Է',
'ը' => 'Ը',
'թ' => 'Թ',
'ժ' => 'Ժ',
'ի' => 'Ի',
'լ' => 'Լ',
'խ' => 'Խ',
'ծ' => 'Ծ',
'կ' => 'Կ',
'հ' => 'Հ',
'ձ' => 'Ձ',
'ղ' => 'Ղ',
'ճ' => 'Ճ',
'մ' => 'Մ',
'յ' => 'Յ',
'ն' => 'Ն',
'շ' => 'Շ',
'ո' => 'Ո',
'չ' => 'Չ',
'պ' => 'Պ',
'ջ' => 'Ջ',
'ռ' => 'Ռ',
'ս' => 'Ս',
'վ' => 'Վ',
'տ' => 'Տ',
'ր' => 'Ր',
'ց' => 'Ց',
'ւ' => 'Ւ',
'փ' => 'Փ',
'ք' => 'Ք',
'օ' => 'Օ',
'ֆ' => 'Ֆ',
'ა' => 'Ა',
'ბ' => 'Ბ',
'გ' => 'Გ',
'დ' => 'Დ',
'ე' => 'Ე',
'ვ' => 'Ვ',
'ზ' => 'Ზ',
'თ' => 'Თ',
'ი' => 'Ი',
'კ' => 'Კ',
'ლ' => 'Ლ',
'მ' => 'Მ',
'ნ' => 'Ნ',
'ო' => 'Ო',
'პ' => 'Პ',
'ჟ' => 'Ჟ',
'რ' => 'Რ',
'ს' => 'Ს',
'ტ' => 'Ტ',
'უ' => 'Უ',
'ფ' => 'Ფ',
'ქ' => 'Ქ',
'ღ' => 'Ღ',
'ყ' => 'Ყ',
'შ' => 'Შ',
'ჩ' => 'Ჩ',
'ც' => 'Ც',
'ძ' => 'Ძ',
'წ' => 'Წ',
'ჭ' => 'Ჭ',
'ხ' => 'Ხ',
'ჯ' => 'Ჯ',
'ჰ' => 'Ჰ',
'ჱ' => 'Ჱ',
'ჲ' => 'Ჲ',
'ჳ' => 'Ჳ',
'ჴ' => 'Ჴ',
'ჵ' => 'Ჵ',
'ჶ' => 'Ჶ',
'ჷ' => 'Ჷ',
'ჸ' => 'Ჸ',
'ჹ' => 'Ჹ',
'ჺ' => 'Ჺ',
'ჽ' => 'Ჽ',
'ჾ' => 'Ჾ',
'ჿ' => 'Ჿ',
'ᏸ' => 'Ᏸ',
'ᏹ' => 'Ᏹ',
'ᏺ' => 'Ᏺ',
'ᏻ' => 'Ᏻ',
'ᏼ' => 'Ᏼ',
'ᏽ' => 'Ᏽ',
'ᲀ' => 'В',
'ᲁ' => 'Д',
'ᲂ' => 'О',
'ᲃ' => 'С',
'ᲄ' => 'Т',
'ᲅ' => 'Т',
'ᲆ' => 'Ъ',
'ᲇ' => 'Ѣ',
'ᲈ' => 'Ꙋ',
'ᵹ' => 'Ᵹ',
'ᵽ' => 'Ᵽ',
'ᶎ' => 'Ᶎ',
'ḁ' => 'Ḁ',
'ḃ' => 'Ḃ',
'ḅ' => 'Ḅ',
'ḇ' => 'Ḇ',
'ḉ' => 'Ḉ',
'ḋ' => 'Ḋ',
'ḍ' => 'Ḍ',
'ḏ' => 'Ḏ',
'ḑ' => 'Ḑ',
'ḓ' => 'Ḓ',
'ḕ' => 'Ḕ',
'ḗ' => 'Ḗ',
'ḙ' => 'Ḙ',
'ḛ' => 'Ḛ',
'ḝ' => 'Ḝ',
'ḟ' => 'Ḟ',
'ḡ' => 'Ḡ',
'ḣ' => 'Ḣ',
'ḥ' => 'Ḥ',
'ḧ' => 'Ḧ',
'ḩ' => 'Ḩ',
'ḫ' => 'Ḫ',
'ḭ' => 'Ḭ',
'ḯ' => 'Ḯ',
'ḱ' => 'Ḱ',
'ḳ' => 'Ḳ',
'ḵ' => 'Ḵ',
'ḷ' => 'Ḷ',
'ḹ' => 'Ḹ',
'ḻ' => 'Ḻ',
'ḽ' => 'Ḽ',
'ḿ' => 'Ḿ',
'ṁ' => 'Ṁ',
'ṃ' => 'Ṃ',
'ṅ' => 'Ṅ',
'ṇ' => 'Ṇ',
'ṉ' => 'Ṉ',
'ṋ' => 'Ṋ',
'ṍ' => 'Ṍ',
'ṏ' => 'Ṏ',
'ṑ' => 'Ṑ',
'ṓ' => 'Ṓ',
'ṕ' => 'Ṕ',
'ṗ' => 'Ṗ',
'ṙ' => 'Ṙ',
'ṛ' => 'Ṛ',
'ṝ' => 'Ṝ',
'ṟ' => 'Ṟ',
'ṡ' => 'Ṡ',
'ṣ' => 'Ṣ',
'ṥ' => 'Ṥ',
'ṧ' => 'Ṧ',
'ṩ' => 'Ṩ',
'ṫ' => 'Ṫ',
'ṭ' => 'Ṭ',
'ṯ' => 'Ṯ',
'ṱ' => 'Ṱ',
'ṳ' => 'Ṳ',
'ṵ' => 'Ṵ',
'ṷ' => 'Ṷ',
'ṹ' => 'Ṹ',
'ṻ' => 'Ṻ',
'ṽ' => 'Ṽ',
'ṿ' => 'Ṿ',
'ẁ' => 'Ẁ',
'ẃ' => 'Ẃ',
'ẅ' => 'Ẅ',
'ẇ' => 'Ẇ',
'ẉ' => 'Ẉ',
'ẋ' => 'Ẋ',
'ẍ' => 'Ẍ',
'ẏ' => 'Ẏ',
'ẑ' => 'Ẑ',
'ẓ' => 'Ẓ',
'ẕ' => 'Ẕ',
'ẛ' => 'Ṡ',
'ạ' => 'Ạ',
'ả' => 'Ả',
'ấ' => 'Ấ',
'ầ' => 'Ầ',
'ẩ' => 'Ẩ',
'ẫ' => 'Ẫ',
'ậ' => 'Ậ',
'ắ' => 'Ắ',
'ằ' => 'Ằ',
'ẳ' => 'Ẳ',
'ẵ' => 'Ẵ',
'ặ' => 'Ặ',
'ẹ' => 'Ẹ',
'ẻ' => 'Ẻ',
'ẽ' => 'Ẽ',
'ế' => 'Ế',
'ề' => 'Ề',
'ể' => 'Ể',
'ễ' => 'Ễ',
'ệ' => 'Ệ',
'ỉ' => 'Ỉ',
'ị' => 'Ị',
'ọ' => 'Ọ',
'ỏ' => 'Ỏ',
'ố' => 'Ố',
'ồ' => 'Ồ',
'ổ' => 'Ổ',
'ỗ' => 'Ỗ',
'ộ' => 'Ộ',
'ớ' => 'Ớ',
'ờ' => 'Ờ',
'ở' => 'Ở',
'ỡ' => 'Ỡ',
'ợ' => 'Ợ',
'ụ' => 'Ụ',
'ủ' => 'Ủ',
'ứ' => 'Ứ',
'ừ' => 'Ừ',
'ử' => 'Ử',
'ữ' => 'Ữ',
'ự' => 'Ự',
'ỳ' => 'Ỳ',
'ỵ' => 'Ỵ',
'ỷ' => 'Ỷ',
'ỹ' => 'Ỹ',
'ỻ' => 'Ỻ',
'ỽ' => 'Ỽ',
'ỿ' => 'Ỿ',
'ἀ' => 'Ἀ',
'ἁ' => 'Ἁ',
'ἂ' => 'Ἂ',
'ἃ' => 'Ἃ',
'ἄ' => 'Ἄ',
'ἅ' => 'Ἅ',
'ἆ' => 'Ἆ',
'ἇ' => 'Ἇ',
'ἐ' => 'Ἐ',
'ἑ' => 'Ἑ',
'ἒ' => 'Ἒ',
'ἓ' => 'Ἓ',
'ἔ' => 'Ἔ',
'ἕ' => 'Ἕ',
'ἠ' => 'Ἠ',
'ἡ' => 'Ἡ',
'ἢ' => 'Ἢ',
'ἣ' => 'Ἣ',
'ἤ' => 'Ἤ',
'ἥ' => 'Ἥ',
'ἦ' => 'Ἦ',
'ἧ' => 'Ἧ',
'ἰ' => 'Ἰ',
'ἱ' => 'Ἱ',
'ἲ' => 'Ἲ',
'ἳ' => 'Ἳ',
'ἴ' => 'Ἴ',
'ἵ' => 'Ἵ',
'ἶ' => 'Ἶ',
'ἷ' => 'Ἷ',
'ὀ' => 'Ὀ',
'ὁ' => 'Ὁ',
'ὂ' => 'Ὂ',
'ὃ' => 'Ὃ',
'ὄ' => 'Ὄ',
'ὅ' => 'Ὅ',
'ὑ' => 'Ὑ',
'ὓ' => 'Ὓ',
'ὕ' => 'Ὕ',
'ὗ' => 'Ὗ',
'ὠ' => 'Ὠ',
'ὡ' => 'Ὡ',
'ὢ' => 'Ὢ',
'ὣ' => 'Ὣ',
'ὤ' => 'Ὤ',
'ὥ' => 'Ὥ',
'ὦ' => 'Ὦ',
'ὧ' => 'Ὧ',
'ὰ' => 'Ὰ',
'ά' => 'Ά',
'ὲ' => 'Ὲ',
'έ' => 'Έ',
'ὴ' => 'Ὴ',
'ή' => 'Ή',
'ὶ' => 'Ὶ',
'ί' => 'Ί',
'ὸ' => 'Ὸ',
'ό' => 'Ό',
'ὺ' => 'Ὺ',
'ύ' => 'Ύ',
'ὼ' => 'Ὼ',
'ώ' => 'Ώ',
'ᾀ' => 'ἈΙ',
'ᾁ' => 'ἉΙ',
'ᾂ' => 'ἊΙ',
'ᾃ' => 'ἋΙ',
'ᾄ' => 'ἌΙ',
'ᾅ' => 'ἍΙ',
'ᾆ' => 'ἎΙ',
'ᾇ' => 'ἏΙ',
'ᾐ' => 'ἨΙ',
'ᾑ' => 'ἩΙ',
'ᾒ' => 'ἪΙ',
'ᾓ' => 'ἫΙ',
'ᾔ' => 'ἬΙ',
'ᾕ' => 'ἭΙ',
'ᾖ' => 'ἮΙ',
'ᾗ' => 'ἯΙ',
'ᾠ' => 'ὨΙ',
'ᾡ' => 'ὩΙ',
'ᾢ' => 'ὪΙ',
'ᾣ' => 'ὫΙ',
'ᾤ' => 'ὬΙ',
'ᾥ' => 'ὭΙ',
'ᾦ' => 'ὮΙ',
'ᾧ' => 'ὯΙ',
'ᾰ' => 'Ᾰ',
'ᾱ' => 'Ᾱ',
'ᾳ' => 'ΑΙ',
'ι' => 'Ι',
'ῃ' => 'ΗΙ',
'ῐ' => 'Ῐ',
'ῑ' => 'Ῑ',
'ῠ' => 'Ῠ',
'ῡ' => 'Ῡ',
'ῥ' => 'Ῥ',
'ῳ' => 'ΩΙ',
'ⅎ' => 'Ⅎ',
'ⅰ' => 'Ⅰ',
'ⅱ' => 'Ⅱ',
'ⅲ' => 'Ⅲ',
'ⅳ' => 'Ⅳ',
'ⅴ' => 'Ⅴ',
'ⅵ' => 'Ⅵ',
'ⅶ' => 'Ⅶ',
'ⅷ' => 'Ⅷ',
'ⅸ' => 'Ⅸ',
'ⅹ' => 'Ⅹ',
'ⅺ' => 'Ⅺ',
'ⅻ' => 'Ⅻ',
'ⅼ' => 'Ⅼ',
'ⅽ' => 'Ⅽ',
'ⅾ' => 'Ⅾ',
'ⅿ' => 'Ⅿ',
'ↄ' => 'Ↄ',
'ⓐ' => 'Ⓐ',
'ⓑ' => 'Ⓑ',
'ⓒ' => 'Ⓒ',
'ⓓ' => 'Ⓓ',
'ⓔ' => 'Ⓔ',
'ⓕ' => 'Ⓕ',
'ⓖ' => 'Ⓖ',
'ⓗ' => 'Ⓗ',
'ⓘ' => 'Ⓘ',
'ⓙ' => 'Ⓙ',
'ⓚ' => 'Ⓚ',
'ⓛ' => 'Ⓛ',
'ⓜ' => 'Ⓜ',
'ⓝ' => 'Ⓝ',
'ⓞ' => 'Ⓞ',
'ⓟ' => 'Ⓟ',
'ⓠ' => 'Ⓠ',
'ⓡ' => 'Ⓡ',
'ⓢ' => 'Ⓢ',
'ⓣ' => 'Ⓣ',
'ⓤ' => 'Ⓤ',
'ⓥ' => 'Ⓥ',
'ⓦ' => 'Ⓦ',
'ⓧ' => 'Ⓧ',
'ⓨ' => 'Ⓨ',
'ⓩ' => 'Ⓩ',
'ⰰ' => 'Ⰰ',
'ⰱ' => 'Ⰱ',
'ⰲ' => 'Ⰲ',
'ⰳ' => 'Ⰳ',
'ⰴ' => 'Ⰴ',
'ⰵ' => 'Ⰵ',
'ⰶ' => 'Ⰶ',
'ⰷ' => 'Ⰷ',
'ⰸ' => 'Ⰸ',
'ⰹ' => 'Ⰹ',
'ⰺ' => 'Ⰺ',
'ⰻ' => 'Ⰻ',
'ⰼ' => 'Ⰼ',
'ⰽ' => 'Ⰽ',
'ⰾ' => 'Ⰾ',
'ⰿ' => 'Ⰿ',
'ⱀ' => 'Ⱀ',
'ⱁ' => 'Ⱁ',
'ⱂ' => 'Ⱂ',
'ⱃ' => 'Ⱃ',
'ⱄ' => 'Ⱄ',
'ⱅ' => 'Ⱅ',
'ⱆ' => 'Ⱆ',
'ⱇ' => 'Ⱇ',
'ⱈ' => 'Ⱈ',
'ⱉ' => 'Ⱉ',
'ⱊ' => 'Ⱊ',
'ⱋ' => 'Ⱋ',
'ⱌ' => 'Ⱌ',
'ⱍ' => 'Ⱍ',
'ⱎ' => 'Ⱎ',
'ⱏ' => 'Ⱏ',
'ⱐ' => 'Ⱐ',
'ⱑ' => 'Ⱑ',
'ⱒ' => 'Ⱒ',
'ⱓ' => 'Ⱓ',
'ⱔ' => 'Ⱔ',
'ⱕ' => 'Ⱕ',
'ⱖ' => 'Ⱖ',
'ⱗ' => 'Ⱗ',
'ⱘ' => 'Ⱘ',
'ⱙ' => 'Ⱙ',
'ⱚ' => 'Ⱚ',
'ⱛ' => 'Ⱛ',
'ⱜ' => 'Ⱜ',
'ⱝ' => 'Ⱝ',
'ⱞ' => 'Ⱞ',
'ⱡ' => 'Ⱡ',
'ⱥ' => 'Ⱥ',
'ⱦ' => 'Ⱦ',
'ⱨ' => 'Ⱨ',
'ⱪ' => 'Ⱪ',
'ⱬ' => 'Ⱬ',
'ⱳ' => 'Ⱳ',
'ⱶ' => 'Ⱶ',
'ⲁ' => 'Ⲁ',
'ⲃ' => 'Ⲃ',
'ⲅ' => 'Ⲅ',
'ⲇ' => 'Ⲇ',
'ⲉ' => 'Ⲉ',
'ⲋ' => 'Ⲋ',
'ⲍ' => 'Ⲍ',
'ⲏ' => 'Ⲏ',
'ⲑ' => 'Ⲑ',
'ⲓ' => 'Ⲓ',
'ⲕ' => 'Ⲕ',
'ⲗ' => 'Ⲗ',
'ⲙ' => 'Ⲙ',
'ⲛ' => 'Ⲛ',
'ⲝ' => 'Ⲝ',
'ⲟ' => 'Ⲟ',
'ⲡ' => 'Ⲡ',
'ⲣ' => 'Ⲣ',
'ⲥ' => 'Ⲥ',
'ⲧ' => 'Ⲧ',
'ⲩ' => 'Ⲩ',
'ⲫ' => 'Ⲫ',
'ⲭ' => 'Ⲭ',
'ⲯ' => 'Ⲯ',
'ⲱ' => 'Ⲱ',
'ⲳ' => 'Ⲳ',
'ⲵ' => 'Ⲵ',
'ⲷ' => 'Ⲷ',
'ⲹ' => 'Ⲹ',
'ⲻ' => 'Ⲻ',
'ⲽ' => 'Ⲽ',
'ⲿ' => 'Ⲿ',
'ⳁ' => 'Ⳁ',
'ⳃ' => 'Ⳃ',
'ⳅ' => 'Ⳅ',
'ⳇ' => 'Ⳇ',
'ⳉ' => 'Ⳉ',
'ⳋ' => 'Ⳋ',
'ⳍ' => 'Ⳍ',
'ⳏ' => 'Ⳏ',
'ⳑ' => 'Ⳑ',
'ⳓ' => 'Ⳓ',
'ⳕ' => 'Ⳕ',
'ⳗ' => 'Ⳗ',
'ⳙ' => 'Ⳙ',
'ⳛ' => 'Ⳛ',
'ⳝ' => 'Ⳝ',
'ⳟ' => 'Ⳟ',
'ⳡ' => 'Ⳡ',
'ⳣ' => 'Ⳣ',
'ⳬ' => 'Ⳬ',
'ⳮ' => 'Ⳮ',
'ⳳ' => 'Ⳳ',
'ⴀ' => 'Ⴀ',
'ⴁ' => 'Ⴁ',
'ⴂ' => 'Ⴂ',
'ⴃ' => 'Ⴃ',
'ⴄ' => 'Ⴄ',
'ⴅ' => 'Ⴅ',
'ⴆ' => 'Ⴆ',
'ⴇ' => 'Ⴇ',
'ⴈ' => 'Ⴈ',
'ⴉ' => 'Ⴉ',
'ⴊ' => 'Ⴊ',
'ⴋ' => 'Ⴋ',
'ⴌ' => 'Ⴌ',
'ⴍ' => 'Ⴍ',
'ⴎ' => 'Ⴎ',
'ⴏ' => 'Ⴏ',
'ⴐ' => 'Ⴐ',
'ⴑ' => 'Ⴑ',
'ⴒ' => 'Ⴒ',
'ⴓ' => 'Ⴓ',
'ⴔ' => 'Ⴔ',
'ⴕ' => 'Ⴕ',
'ⴖ' => 'Ⴖ',
'ⴗ' => 'Ⴗ',
'ⴘ' => 'Ⴘ',
'ⴙ' => 'Ⴙ',
'ⴚ' => 'Ⴚ',
'ⴛ' => 'Ⴛ',
'ⴜ' => 'Ⴜ',
'ⴝ' => 'Ⴝ',
'ⴞ' => 'Ⴞ',
'ⴟ' => 'Ⴟ',
'ⴠ' => 'Ⴠ',
'ⴡ' => 'Ⴡ',
'ⴢ' => 'Ⴢ',
'ⴣ' => 'Ⴣ',
'ⴤ' => 'Ⴤ',
'ⴥ' => 'Ⴥ',
'ⴧ' => 'Ⴧ',
'ⴭ' => 'Ⴭ',
'ꙁ' => 'Ꙁ',
'ꙃ' => 'Ꙃ',
'ꙅ' => 'Ꙅ',
'ꙇ' => 'Ꙇ',
'ꙉ' => 'Ꙉ',
'ꙋ' => 'Ꙋ',
'ꙍ' => 'Ꙍ',
'ꙏ' => 'Ꙏ',
'ꙑ' => 'Ꙑ',
'ꙓ' => 'Ꙓ',
'ꙕ' => 'Ꙕ',
'ꙗ' => 'Ꙗ',
'ꙙ' => 'Ꙙ',
'ꙛ' => 'Ꙛ',
'ꙝ' => 'Ꙝ',
'ꙟ' => 'Ꙟ',
'ꙡ' => 'Ꙡ',
'ꙣ' => 'Ꙣ',
'ꙥ' => 'Ꙥ',
'ꙧ' => 'Ꙧ',
'ꙩ' => 'Ꙩ',
'ꙫ' => 'Ꙫ',
'ꙭ' => 'Ꙭ',
'ꚁ' => 'Ꚁ',
'ꚃ' => 'Ꚃ',
'ꚅ' => 'Ꚅ',
'ꚇ' => 'Ꚇ',
'ꚉ' => 'Ꚉ',
'ꚋ' => 'Ꚋ',
'ꚍ' => 'Ꚍ',
'ꚏ' => 'Ꚏ',
'ꚑ' => 'Ꚑ',
'ꚓ' => 'Ꚓ',
'ꚕ' => 'Ꚕ',
'ꚗ' => 'Ꚗ',
'ꚙ' => 'Ꚙ',
'ꚛ' => 'Ꚛ',
'ꜣ' => 'Ꜣ',
'ꜥ' => 'Ꜥ',
'ꜧ' => 'Ꜧ',
'ꜩ' => 'Ꜩ',
'ꜫ' => 'Ꜫ',
'ꜭ' => 'Ꜭ',
'ꜯ' => 'Ꜯ',
'ꜳ' => 'Ꜳ',
'ꜵ' => 'Ꜵ',
'ꜷ' => 'Ꜷ',
'ꜹ' => 'Ꜹ',
'ꜻ' => 'Ꜻ',
'ꜽ' => 'Ꜽ',
'ꜿ' => 'Ꜿ',
'ꝁ' => 'Ꝁ',
'ꝃ' => 'Ꝃ',
'ꝅ' => 'Ꝅ',
'ꝇ' => 'Ꝇ',
'ꝉ' => 'Ꝉ',
'ꝋ' => 'Ꝋ',
'ꝍ' => 'Ꝍ',
'ꝏ' => 'Ꝏ',
'ꝑ' => 'Ꝑ',
'ꝓ' => 'Ꝓ',
'ꝕ' => 'Ꝕ',
'ꝗ' => 'Ꝗ',
'ꝙ' => 'Ꝙ',
'ꝛ' => 'Ꝛ',
'ꝝ' => 'Ꝝ',
'ꝟ' => 'Ꝟ',
'ꝡ' => 'Ꝡ',
'ꝣ' => 'Ꝣ',
'ꝥ' => 'Ꝥ',
'ꝧ' => 'Ꝧ',
'ꝩ' => 'Ꝩ',
'ꝫ' => 'Ꝫ',
'ꝭ' => 'Ꝭ',
'ꝯ' => 'Ꝯ',
'ꝺ' => 'Ꝺ',
'ꝼ' => 'Ꝼ',
'ꝿ' => 'Ꝿ',
'ꞁ' => 'Ꞁ',
'ꞃ' => 'Ꞃ',
'ꞅ' => 'Ꞅ',
'ꞇ' => 'Ꞇ',
'ꞌ' => 'Ꞌ',
'ꞑ' => 'Ꞑ',
'ꞓ' => 'Ꞓ',
'ꞔ' => 'Ꞔ',
'ꞗ' => 'Ꞗ',
'ꞙ' => 'Ꞙ',
'ꞛ' => 'Ꞛ',
'ꞝ' => 'Ꞝ',
'ꞟ' => 'Ꞟ',
'ꞡ' => 'Ꞡ',
'ꞣ' => 'Ꞣ',
'ꞥ' => 'Ꞥ',
'ꞧ' => 'Ꞧ',
'ꞩ' => 'Ꞩ',
'ꞵ' => 'Ꞵ',
'ꞷ' => 'Ꞷ',
'ꞹ' => 'Ꞹ',
'ꞻ' => 'Ꞻ',
'ꞽ' => 'Ꞽ',
'ꞿ' => 'Ꞿ',
'ꟃ' => 'Ꟃ',
'ꟈ' => 'Ꟈ',
'ꟊ' => 'Ꟊ',
'ꟶ' => 'Ꟶ',
'ꭓ' => 'Ꭓ',
'ꭰ' => 'Ꭰ',
'ꭱ' => 'Ꭱ',
'ꭲ' => 'Ꭲ',
'ꭳ' => 'Ꭳ',
'ꭴ' => 'Ꭴ',
'ꭵ' => 'Ꭵ',
'ꭶ' => 'Ꭶ',
'ꭷ' => 'Ꭷ',
'ꭸ' => 'Ꭸ',
'ꭹ' => 'Ꭹ',
'ꭺ' => 'Ꭺ',
'ꭻ' => 'Ꭻ',
'ꭼ' => 'Ꭼ',
'ꭽ' => 'Ꭽ',
'ꭾ' => 'Ꭾ',
'ꭿ' => 'Ꭿ',
'ꮀ' => 'Ꮀ',
'ꮁ' => 'Ꮁ',
'ꮂ' => 'Ꮂ',
'ꮃ' => 'Ꮃ',
'ꮄ' => 'Ꮄ',
'ꮅ' => 'Ꮅ',
'ꮆ' => 'Ꮆ',
'ꮇ' => 'Ꮇ',
'ꮈ' => 'Ꮈ',
'ꮉ' => 'Ꮉ',
'ꮊ' => 'Ꮊ',
'ꮋ' => 'Ꮋ',
'ꮌ' => 'Ꮌ',
'ꮍ' => 'Ꮍ',
'ꮎ' => 'Ꮎ',
'ꮏ' => 'Ꮏ',
'ꮐ' => 'Ꮐ',
'ꮑ' => 'Ꮑ',
'ꮒ' => 'Ꮒ',
'ꮓ' => 'Ꮓ',
'ꮔ' => 'Ꮔ',
'ꮕ' => 'Ꮕ',
'ꮖ' => 'Ꮖ',
'ꮗ' => 'Ꮗ',
'ꮘ' => 'Ꮘ',
'ꮙ' => 'Ꮙ',
'ꮚ' => 'Ꮚ',
'ꮛ' => 'Ꮛ',
'ꮜ' => 'Ꮜ',
'ꮝ' => 'Ꮝ',
'ꮞ' => 'Ꮞ',
'ꮟ' => 'Ꮟ',
'ꮠ' => 'Ꮠ',
'ꮡ' => 'Ꮡ',
'ꮢ' => 'Ꮢ',
'ꮣ' => 'Ꮣ',
'ꮤ' => 'Ꮤ',
'ꮥ' => 'Ꮥ',
'ꮦ' => 'Ꮦ',
'ꮧ' => 'Ꮧ',
'ꮨ' => 'Ꮨ',
'ꮩ' => 'Ꮩ',
'ꮪ' => 'Ꮪ',
'ꮫ' => 'Ꮫ',
'ꮬ' => 'Ꮬ',
'ꮭ' => 'Ꮭ',
'ꮮ' => 'Ꮮ',
'ꮯ' => 'Ꮯ',
'ꮰ' => 'Ꮰ',
'ꮱ' => 'Ꮱ',
'ꮲ' => 'Ꮲ',
'ꮳ' => 'Ꮳ',
'ꮴ' => 'Ꮴ',
'ꮵ' => 'Ꮵ',
'ꮶ' => 'Ꮶ',
'ꮷ' => 'Ꮷ',
'ꮸ' => 'Ꮸ',
'ꮹ' => 'Ꮹ',
'ꮺ' => 'Ꮺ',
'ꮻ' => 'Ꮻ',
'ꮼ' => 'Ꮼ',
'ꮽ' => 'Ꮽ',
'ꮾ' => 'Ꮾ',
'ꮿ' => 'Ꮿ',
'ａ' => 'Ａ',
'ｂ' => 'Ｂ',
'ｃ' => 'Ｃ',
'ｄ' => 'Ｄ',
'ｅ' => 'Ｅ',
'ｆ' => 'Ｆ',
'ｇ' => 'Ｇ',
'ｈ' => 'Ｈ',
'ｉ' => 'Ｉ',
'ｊ' => 'Ｊ',
'ｋ' => 'Ｋ',
'ｌ' => 'Ｌ',
'ｍ' => 'Ｍ',
'ｎ' => 'Ｎ',
'ｏ' => 'Ｏ',
'ｐ' => 'Ｐ',
'ｑ' => 'Ｑ',
'ｒ' => 'Ｒ',
'ｓ' => 'Ｓ',
'ｔ' => 'Ｔ',
'ｕ' => 'Ｕ',
'ｖ' => 'Ｖ',
'ｗ' => 'Ｗ',
'ｘ' => 'Ｘ',
'ｙ' => 'Ｙ',
'ｚ' => 'Ｚ',
'𐐨' => '𐐀',
'𐐩' => '𐐁',
'𐐪' => '𐐂',
'𐐫' => '𐐃',
'𐐬' => '𐐄',
'𐐭' => '𐐅',
'𐐮' => '𐐆',
'𐐯' => '𐐇',
'𐐰' => '𐐈',
'𐐱' => '𐐉',
'𐐲' => '𐐊',
'𐐳' => '𐐋',
'𐐴' => '𐐌',
'𐐵' => '𐐍',
'𐐶' => '𐐎',
'𐐷' => '𐐏',
'𐐸' => '𐐐',
'𐐹' => '𐐑',
'𐐺' => '𐐒',
'𐐻' => '𐐓',
'𐐼' => '𐐔',
'𐐽' => '𐐕',
'𐐾' => '𐐖',
'𐐿' => '𐐗',
'𐑀' => '𐐘',
'𐑁' => '𐐙',
'𐑂' => '𐐚',
'𐑃' => '𐐛',
'𐑄' => '𐐜',
'𐑅' => '𐐝',
'𐑆' => '𐐞',
'𐑇' => '𐐟',
'𐑈' => '𐐠',
'𐑉' => '𐐡',
'𐑊' => '𐐢',
'𐑋' => '𐐣',
'𐑌' => '𐐤',
'𐑍' => '𐐥',
'𐑎' => '𐐦',
'𐑏' => '𐐧',
'𐓘' => '𐒰',
'𐓙' => '𐒱',
'𐓚' => '𐒲',
'𐓛' => '𐒳',
'𐓜' => '𐒴',
'𐓝' => '𐒵',
'𐓞' => '𐒶',
'𐓟' => '𐒷',
'𐓠' => '𐒸',
'𐓡' => '𐒹',
'𐓢' => '𐒺',
'𐓣' => '𐒻',
'𐓤' => '𐒼',
'𐓥' => '𐒽',
'𐓦' => '𐒾',
'𐓧' => '𐒿',
'𐓨' => '𐓀',
'𐓩' => '𐓁',
'𐓪' => '𐓂',
'𐓫' => '𐓃',
'𐓬' => '𐓄',
'𐓭' => '𐓅',
'𐓮' => '𐓆',
'𐓯' => '𐓇',
'𐓰' => '𐓈',
'𐓱' => '𐓉',
'𐓲' => '𐓊',
'𐓳' => '𐓋',
'𐓴' => '𐓌',
'𐓵' => '𐓍',
'𐓶' => '𐓎',
'𐓷' => '𐓏',
'𐓸' => '𐓐',
'𐓹' => '𐓑',
'𐓺' => '𐓒',
'𐓻' => '𐓓',
'𐳀' => '𐲀',
'𐳁' => '𐲁',
'𐳂' => '𐲂',
'𐳃' => '𐲃',
'𐳄' => '𐲄',
'𐳅' => '𐲅',
'𐳆' => '𐲆',
'𐳇' => '𐲇',
'𐳈' => '𐲈',
'𐳉' => '𐲉',
'𐳊' => '𐲊',
'𐳋' => '𐲋',
'𐳌' => '𐲌',
'𐳍' => '𐲍',
'𐳎' => '𐲎',
'𐳏' => '𐲏',
'𐳐' => '𐲐',
'𐳑' => '𐲑',
'𐳒' => '𐲒',
'𐳓' => '𐲓',
'𐳔' => '𐲔',
'𐳕' => '𐲕',
'𐳖' => '𐲖',
'𐳗' => '𐲗',
'𐳘' => '𐲘',
'𐳙' => '𐲙',
'𐳚' => '𐲚',
'𐳛' => '𐲛',
'𐳜' => '𐲜',
'𐳝' => '𐲝',
'𐳞' => '𐲞',
'𐳟' => '𐲟',
'𐳠' => '𐲠',
'𐳡' => '𐲡',
'𐳢' => '𐲢',
'𐳣' => '𐲣',
'𐳤' => '𐲤',
'𐳥' => '𐲥',
'𐳦' => '𐲦',
'𐳧' => '𐲧',
'𐳨' => '𐲨',
'𐳩' => '𐲩',
'𐳪' => '𐲪',
'𐳫' => '𐲫',
'𐳬' => '𐲬',
'𐳭' => '𐲭',
'𐳮' => '𐲮',
'𐳯' => '𐲯',
'𐳰' => '𐲰',
'𐳱' => '𐲱',
'𐳲' => '𐲲',
'𑣀' => '𑢠',
'𑣁' => '𑢡',
'𑣂' => '𑢢',
'𑣃' => '𑢣',
'𑣄' => '𑢤',
'𑣅' => '𑢥',
'𑣆' => '𑢦',
'𑣇' => '𑢧',
'𑣈' => '𑢨',
'𑣉' => '𑢩',
'𑣊' => '𑢪',
'𑣋' => '𑢫',
'𑣌' => '𑢬',
'𑣍' => '𑢭',
'𑣎' => '𑢮',
'𑣏' => '𑢯',
'𑣐' => '𑢰',
'𑣑' => '𑢱',
'𑣒' => '𑢲',
'𑣓' => '𑢳',
'𑣔' => '𑢴',
'𑣕' => '𑢵',
'𑣖' => '𑢶',
'𑣗' => '𑢷',
'𑣘' => '𑢸',
'𑣙' => '𑢹',
'𑣚' => '𑢺',
'𑣛' => '𑢻',
'𑣜' => '𑢼',
'𑣝' => '𑢽',
'𑣞' => '𑢾',
'𑣟' => '𑢿',
'𖹠' => '𖹀',
'𖹡' => '𖹁',
'𖹢' => '𖹂',
'𖹣' => '𖹃',
'𖹤' => '𖹄',
'𖹥' => '𖹅',
'𖹦' => '𖹆',
'𖹧' => '𖹇',
'𖹨' => '𖹈',
'𖹩' => '𖹉',
'𖹪' => '𖹊',
'𖹫' => '𖹋',
'𖹬' => '𖹌',
'𖹭' => '𖹍',
'𖹮' => '𖹎',
'𖹯' => '𖹏',
'𖹰' => '𖹐',
'𖹱' => '𖹑',
'𖹲' => '𖹒',
'𖹳' => '𖹓',
'𖹴' => '𖹔',
'𖹵' => '𖹕',
'𖹶' => '𖹖',
'𖹷' => '𖹗',
'𖹸' => '𖹘',
'𖹹' => '𖹙',
'𖹺' => '𖹚',
'𖹻' => '𖹛',
'𖹼' => '𖹜',
'𖹽' => '𖹝',
'𖹾' => '𖹞',
'𖹿' => '𖹟',
'𞤢' => '𞤀',
'𞤣' => '𞤁',
'𞤤' => '𞤂',
'𞤥' => '𞤃',
'𞤦' => '𞤄',
'𞤧' => '𞤅',
'𞤨' => '𞤆',
'𞤩' => '𞤇',
'𞤪' => '𞤈',
'𞤫' => '𞤉',
'𞤬' => '𞤊',
'𞤭' => '𞤋',
'𞤮' => '𞤌',
'𞤯' => '𞤍',
'𞤰' => '𞤎',
'𞤱' => '𞤏',
'𞤲' => '𞤐',
'𞤳' => '𞤑',
'𞤴' => '𞤒',
'𞤵' => '𞤓',
'𞤶' => '𞤔',
'𞤷' => '𞤕',
'𞤸' => '𞤖',
'𞤹' => '𞤗',
'𞤺' => '𞤘',
'𞤻' => '𞤙',
'𞤼' => '𞤚',
'𞤽' => '𞤛',
'𞤾' => '𞤜',
'𞤿' => '𞤝',
'𞥀' => '𞤞',
'𞥁' => '𞤟',
'𞥂' => '𞤠',
'𞥃' => '𞤡',
'ß' => 'SS',
'ﬀ' => 'FF',
'ﬁ' => 'FI',
'ﬂ' => 'FL',
'ﬃ' => 'FFI',
'ﬄ' => 'FFL',
'ﬅ' => 'ST',
'ﬆ' => 'ST',
'և' => 'ԵՒ',
'ﬓ' => 'ՄՆ',
'ﬔ' => 'ՄԵ',
'ﬕ' => 'ՄԻ',
'ﬖ' => 'ՎՆ',
'ﬗ' => 'ՄԽ',
'ŉ' => 'ʼN',
'ΐ' => 'Ϊ́',
'ΰ' => 'Ϋ́',
'ǰ' => 'J̌',
'ẖ' => 'H̱',
'ẗ' => 'T̈',
'ẘ' => 'W̊',
'ẙ' => 'Y̊',
'ẚ' => 'Aʾ',
'ὐ' => 'Υ̓',
'ὒ' => 'Υ̓̀',
'ὔ' => 'Υ̓́',
'ὖ' => 'Υ̓͂',
'ᾶ' => 'Α͂',
'ῆ' => 'Η͂',
'ῒ' => 'Ϊ̀',
'ΐ' => 'Ϊ́',
'ῖ' => 'Ι͂',
'ῗ' => 'Ϊ͂',
'ῢ' => 'Ϋ̀',
'ΰ' => 'Ϋ́',
'ῤ' => 'Ρ̓',
'ῦ' => 'Υ͂',
'ῧ' => 'Ϋ͂',
'ῶ' => 'Ω͂',
'ᾈ' => 'ἈΙ',
'ᾉ' => 'ἉΙ',
'ᾊ' => 'ἊΙ',
'ᾋ' => 'ἋΙ',
'ᾌ' => 'ἌΙ',
'ᾍ' => 'ἍΙ',
'ᾎ' => 'ἎΙ',
'ᾏ' => 'ἏΙ',
'ᾘ' => 'ἨΙ',
'ᾙ' => 'ἩΙ',
'ᾚ' => 'ἪΙ',
'ᾛ' => 'ἫΙ',
'ᾜ' => 'ἬΙ',
'ᾝ' => 'ἭΙ',
'ᾞ' => 'ἮΙ',
'ᾟ' => 'ἯΙ',
'ᾨ' => 'ὨΙ',
'ᾩ' => 'ὩΙ',
'ᾪ' => 'ὪΙ',
'ᾫ' => 'ὫΙ',
'ᾬ' => 'ὬΙ',
'ᾭ' => 'ὭΙ',
'ᾮ' => 'ὮΙ',
'ᾯ' => 'ὯΙ',
'ᾼ' => 'ΑΙ',
'ῌ' => 'ΗΙ',
'ῼ' => 'ΩΙ',
'ᾲ' => 'ᾺΙ',
'ᾴ' => 'ΆΙ',
'ῂ' => 'ῊΙ',
'ῄ' => 'ΉΙ',
'ῲ' => 'ῺΙ',
'ῴ' => 'ΏΙ',
'ᾷ' => 'Α͂Ι',
'ῇ' => 'Η͂Ι',
'ῷ' => 'Ω͂Ι',
);
<?php



return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';
<?php

return [
'İ' => 'i̇',
'µ' => 'μ',
'ſ' => 's',
'ͅ' => 'ι',
'ς' => 'σ',
'ϐ' => 'β',
'ϑ' => 'θ',
'ϕ' => 'φ',
'ϖ' => 'π',
'ϰ' => 'κ',
'ϱ' => 'ρ',
'ϵ' => 'ε',
'ẛ' => 'ṡ',
'ι' => 'ι',
'ß' => 'ss',
'ŉ' => 'ʼn',
'ǰ' => 'ǰ',
'ΐ' => 'ΐ',
'ΰ' => 'ΰ',
'և' => 'եւ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẚ' => 'aʾ',
'ẞ' => 'ss',
'ὐ' => 'ὐ',
'ὒ' => 'ὒ',
'ὔ' => 'ὔ',
'ὖ' => 'ὖ',
'ᾀ' => 'ἀι',
'ᾁ' => 'ἁι',
'ᾂ' => 'ἂι',
'ᾃ' => 'ἃι',
'ᾄ' => 'ἄι',
'ᾅ' => 'ἅι',
'ᾆ' => 'ἆι',
'ᾇ' => 'ἇι',
'ᾈ' => 'ἀι',
'ᾉ' => 'ἁι',
'ᾊ' => 'ἂι',
'ᾋ' => 'ἃι',
'ᾌ' => 'ἄι',
'ᾍ' => 'ἅι',
'ᾎ' => 'ἆι',
'ᾏ' => 'ἇι',
'ᾐ' => 'ἠι',
'ᾑ' => 'ἡι',
'ᾒ' => 'ἢι',
'ᾓ' => 'ἣι',
'ᾔ' => 'ἤι',
'ᾕ' => 'ἥι',
'ᾖ' => 'ἦι',
'ᾗ' => 'ἧι',
'ᾘ' => 'ἠι',
'ᾙ' => 'ἡι',
'ᾚ' => 'ἢι',
'ᾛ' => 'ἣι',
'ᾜ' => 'ἤι',
'ᾝ' => 'ἥι',
'ᾞ' => 'ἦι',
'ᾟ' => 'ἧι',
'ᾠ' => 'ὠι',
'ᾡ' => 'ὡι',
'ᾢ' => 'ὢι',
'ᾣ' => 'ὣι',
'ᾤ' => 'ὤι',
'ᾥ' => 'ὥι',
'ᾦ' => 'ὦι',
'ᾧ' => 'ὧι',
'ᾨ' => 'ὠι',
'ᾩ' => 'ὡι',
'ᾪ' => 'ὢι',
'ᾫ' => 'ὣι',
'ᾬ' => 'ὤι',
'ᾭ' => 'ὥι',
'ᾮ' => 'ὦι',
'ᾯ' => 'ὧι',
'ᾲ' => 'ὰι',
'ᾳ' => 'αι',
'ᾴ' => 'άι',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾶι',
'ᾼ' => 'αι',
'ῂ' => 'ὴι',
'ῃ' => 'ηι',
'ῄ' => 'ήι',
'ῆ' => 'ῆ',
'ῇ' => 'ῆι',
'ῌ' => 'ηι',
'ῒ' => 'ῒ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'ῢ' => 'ῢ',
'ῤ' => 'ῤ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'ῲ' => 'ὼι',
'ῳ' => 'ωι',
'ῴ' => 'ώι',
'ῶ' => 'ῶ',
'ῷ' => 'ῶι',
'ῼ' => 'ωι',
'ﬀ' => 'ff',
'ﬁ' => 'fi',
'ﬂ' => 'fl',
'ﬃ' => 'ffi',
'ﬄ' => 'ffl',
'ﬅ' => 'st',
'ﬆ' => 'st',
'ﬓ' => 'մն',
'ﬔ' => 'մե',
'ﬕ' => 'մի',
'ﬖ' => 'վն',
'ﬗ' => 'մխ',
];
<?php

return array (
'A' => 'a',
'B' => 'b',
'C' => 'c',
'D' => 'd',
'E' => 'e',
'F' => 'f',
'G' => 'g',
'H' => 'h',
'I' => 'i',
'J' => 'j',
'K' => 'k',
'L' => 'l',
'M' => 'm',
'N' => 'n',
'O' => 'o',
'P' => 'p',
'Q' => 'q',
'R' => 'r',
'S' => 's',
'T' => 't',
'U' => 'u',
'V' => 'v',
'W' => 'w',
'X' => 'x',
'Y' => 'y',
'Z' => 'z',
'À' => 'à',
'Á' => 'á',
'Â' => 'â',
'Ã' => 'ã',
'Ä' => 'ä',
'Å' => 'å',
'Æ' => 'æ',
'Ç' => 'ç',
'È' => 'è',
'É' => 'é',
'Ê' => 'ê',
'Ë' => 'ë',
'Ì' => 'ì',
'Í' => 'í',
'Î' => 'î',
'Ï' => 'ï',
'Ð' => 'ð',
'Ñ' => 'ñ',
'Ò' => 'ò',
'Ó' => 'ó',
'Ô' => 'ô',
'Õ' => 'õ',
'Ö' => 'ö',
'Ø' => 'ø',
'Ù' => 'ù',
'Ú' => 'ú',
'Û' => 'û',
'Ü' => 'ü',
'Ý' => 'ý',
'Þ' => 'þ',
'Ā' => 'ā',
'Ă' => 'ă',
'Ą' => 'ą',
'Ć' => 'ć',
'Ĉ' => 'ĉ',
'Ċ' => 'ċ',
'Č' => 'č',
'Ď' => 'ď',
'Đ' => 'đ',
'Ē' => 'ē',
'Ĕ' => 'ĕ',
'Ė' => 'ė',
'Ę' => 'ę',
'Ě' => 'ě',
'Ĝ' => 'ĝ',
'Ğ' => 'ğ',
'Ġ' => 'ġ',
'Ģ' => 'ģ',
'Ĥ' => 'ĥ',
'Ħ' => 'ħ',
'Ĩ' => 'ĩ',
'Ī' => 'ī',
'Ĭ' => 'ĭ',
'Į' => 'į',
'İ' => 'i̇',
'Ĳ' => 'ĳ',
'Ĵ' => 'ĵ',
'Ķ' => 'ķ',
'Ĺ' => 'ĺ',
'Ļ' => 'ļ',
'Ľ' => 'ľ',
'Ŀ' => 'ŀ',
'Ł' => 'ł',
'Ń' => 'ń',
'Ņ' => 'ņ',
'Ň' => 'ň',
'Ŋ' => 'ŋ',
'Ō' => 'ō',
'Ŏ' => 'ŏ',
'Ő' => 'ő',
'Œ' => 'œ',
'Ŕ' => 'ŕ',
'Ŗ' => 'ŗ',
'Ř' => 'ř',
'Ś' => 'ś',
'Ŝ' => 'ŝ',
'Ş' => 'ş',
'Š' => 'š',
'Ţ' => 'ţ',
'Ť' => 'ť',
'Ŧ' => 'ŧ',
'Ũ' => 'ũ',
'Ū' => 'ū',
'Ŭ' => 'ŭ',
'Ů' => 'ů',
'Ű' => 'ű',
'Ų' => 'ų',
'Ŵ' => 'ŵ',
'Ŷ' => 'ŷ',
'Ÿ' => 'ÿ',
'Ź' => 'ź',
'Ż' => 'ż',
'Ž' => 'ž',
'Ɓ' => 'ɓ',
'Ƃ' => 'ƃ',
'Ƅ' => 'ƅ',
'Ɔ' => 'ɔ',
'Ƈ' => 'ƈ',
'Ɖ' => 'ɖ',
'Ɗ' => 'ɗ',
'Ƌ' => 'ƌ',
'Ǝ' => 'ǝ',
'Ə' => 'ə',
'Ɛ' => 'ɛ',
'Ƒ' => 'ƒ',
'Ɠ' => 'ɠ',
'Ɣ' => 'ɣ',
'Ɩ' => 'ɩ',
'Ɨ' => 'ɨ',
'Ƙ' => 'ƙ',
'Ɯ' => 'ɯ',
'Ɲ' => 'ɲ',
'Ɵ' => 'ɵ',
'Ơ' => 'ơ',
'Ƣ' => 'ƣ',
'Ƥ' => 'ƥ',
'Ʀ' => 'ʀ',
'Ƨ' => 'ƨ',
'Ʃ' => 'ʃ',
'Ƭ' => 'ƭ',
'Ʈ' => 'ʈ',
'Ư' => 'ư',
'Ʊ' => 'ʊ',
'Ʋ' => 'ʋ',
'Ƴ' => 'ƴ',
'Ƶ' => 'ƶ',
'Ʒ' => 'ʒ',
'Ƹ' => 'ƹ',
'Ƽ' => 'ƽ',
'Ǆ' => 'ǆ',
'ǅ' => 'ǆ',
'Ǉ' => 'ǉ',
'ǈ' => 'ǉ',
'Ǌ' => 'ǌ',
'ǋ' => 'ǌ',
'Ǎ' => 'ǎ',
'Ǐ' => 'ǐ',
'Ǒ' => 'ǒ',
'Ǔ' => 'ǔ',
'Ǖ' => 'ǖ',
'Ǘ' => 'ǘ',
'Ǚ' => 'ǚ',
'Ǜ' => 'ǜ',
'Ǟ' => 'ǟ',
'Ǡ' => 'ǡ',
'Ǣ' => 'ǣ',
'Ǥ' => 'ǥ',
'Ǧ' => 'ǧ',
'Ǩ' => 'ǩ',
'Ǫ' => 'ǫ',
'Ǭ' => 'ǭ',
'Ǯ' => 'ǯ',
'Ǳ' => 'ǳ',
'ǲ' => 'ǳ',
'Ǵ' => 'ǵ',
'Ƕ' => 'ƕ',
'Ƿ' => 'ƿ',
'Ǹ' => 'ǹ',
'Ǻ' => 'ǻ',
'Ǽ' => 'ǽ',
'Ǿ' => 'ǿ',
'Ȁ' => 'ȁ',
'Ȃ' => 'ȃ',
'Ȅ' => 'ȅ',
'Ȇ' => 'ȇ',
'Ȉ' => 'ȉ',
'Ȋ' => 'ȋ',
'Ȍ' => 'ȍ',
'Ȏ' => 'ȏ',
'Ȑ' => 'ȑ',
'Ȓ' => 'ȓ',
'Ȕ' => 'ȕ',
'Ȗ' => 'ȗ',
'Ș' => 'ș',
'Ț' => 'ț',
'Ȝ' => 'ȝ',
'Ȟ' => 'ȟ',
'Ƞ' => 'ƞ',
'Ȣ' => 'ȣ',
'Ȥ' => 'ȥ',
'Ȧ' => 'ȧ',
'Ȩ' => 'ȩ',
'Ȫ' => 'ȫ',
'Ȭ' => 'ȭ',
'Ȯ' => 'ȯ',
'Ȱ' => 'ȱ',
'Ȳ' => 'ȳ',
'Ⱥ' => 'ⱥ',
'Ȼ' => 'ȼ',
'Ƚ' => 'ƚ',
'Ⱦ' => 'ⱦ',
'Ɂ' => 'ɂ',
'Ƀ' => 'ƀ',
'Ʉ' => 'ʉ',
'Ʌ' => 'ʌ',
'Ɇ' => 'ɇ',
'Ɉ' => 'ɉ',
'Ɋ' => 'ɋ',
'Ɍ' => 'ɍ',
'Ɏ' => 'ɏ',
'Ͱ' => 'ͱ',
'Ͳ' => 'ͳ',
'Ͷ' => 'ͷ',
'Ϳ' => 'ϳ',
'Ά' => 'ά',
'Έ' => 'έ',
'Ή' => 'ή',
'Ί' => 'ί',
'Ό' => 'ό',
'Ύ' => 'ύ',
'Ώ' => 'ώ',
'Α' => 'α',
'Β' => 'β',
'Γ' => 'γ',
'Δ' => 'δ',
'Ε' => 'ε',
'Ζ' => 'ζ',
'Η' => 'η',
'Θ' => 'θ',
'Ι' => 'ι',
'Κ' => 'κ',
'Λ' => 'λ',
'Μ' => 'μ',
'Ν' => 'ν',
'Ξ' => 'ξ',
'Ο' => 'ο',
'Π' => 'π',
'Ρ' => 'ρ',
'Σ' => 'σ',
'Τ' => 'τ',
'Υ' => 'υ',
'Φ' => 'φ',
'Χ' => 'χ',
'Ψ' => 'ψ',
'Ω' => 'ω',
'Ϊ' => 'ϊ',
'Ϋ' => 'ϋ',
'Ϗ' => 'ϗ',
'Ϙ' => 'ϙ',
'Ϛ' => 'ϛ',
'Ϝ' => 'ϝ',
'Ϟ' => 'ϟ',
'Ϡ' => 'ϡ',
'Ϣ' => 'ϣ',
'Ϥ' => 'ϥ',
'Ϧ' => 'ϧ',
'Ϩ' => 'ϩ',
'Ϫ' => 'ϫ',
'Ϭ' => 'ϭ',
'Ϯ' => 'ϯ',
'ϴ' => 'θ',
'Ϸ' => 'ϸ',
'Ϲ' => 'ϲ',
'Ϻ' => 'ϻ',
'Ͻ' => 'ͻ',
'Ͼ' => 'ͼ',
'Ͽ' => 'ͽ',
'Ѐ' => 'ѐ',
'Ё' => 'ё',
'Ђ' => 'ђ',
'Ѓ' => 'ѓ',
'Є' => 'є',
'Ѕ' => 'ѕ',
'І' => 'і',
'Ї' => 'ї',
'Ј' => 'ј',
'Љ' => 'љ',
'Њ' => 'њ',
'Ћ' => 'ћ',
'Ќ' => 'ќ',
'Ѝ' => 'ѝ',
'Ў' => 'ў',
'Џ' => 'џ',
'А' => 'а',
'Б' => 'б',
'В' => 'в',
'Г' => 'г',
'Д' => 'д',
'Е' => 'е',
'Ж' => 'ж',
'З' => 'з',
'И' => 'и',
'Й' => 'й',
'К' => 'к',
'Л' => 'л',
'М' => 'м',
'Н' => 'н',
'О' => 'о',
'П' => 'п',
'Р' => 'р',
'С' => 'с',
'Т' => 'т',
'У' => 'у',
'Ф' => 'ф',
'Х' => 'х',
'Ц' => 'ц',
'Ч' => 'ч',
'Ш' => 'ш',
'Щ' => 'щ',
'Ъ' => 'ъ',
'Ы' => 'ы',
'Ь' => 'ь',
'Э' => 'э',
'Ю' => 'ю',
'Я' => 'я',
'Ѡ' => 'ѡ',
'Ѣ' => 'ѣ',
'Ѥ' => 'ѥ',
'Ѧ' => 'ѧ',
'Ѩ' => 'ѩ',
'Ѫ' => 'ѫ',
'Ѭ' => 'ѭ',
'Ѯ' => 'ѯ',
'Ѱ' => 'ѱ',
'Ѳ' => 'ѳ',
'Ѵ' => 'ѵ',
'Ѷ' => 'ѷ',
'Ѹ' => 'ѹ',
'Ѻ' => 'ѻ',
'Ѽ' => 'ѽ',
'Ѿ' => 'ѿ',
'Ҁ' => 'ҁ',
'Ҋ' => 'ҋ',
'Ҍ' => 'ҍ',
'Ҏ' => 'ҏ',
'Ґ' => 'ґ',
'Ғ' => 'ғ',
'Ҕ' => 'ҕ',
'Җ' => 'җ',
'Ҙ' => 'ҙ',
'Қ' => 'қ',
'Ҝ' => 'ҝ',
'Ҟ' => 'ҟ',
'Ҡ' => 'ҡ',
'Ң' => 'ң',
'Ҥ' => 'ҥ',
'Ҧ' => 'ҧ',
'Ҩ' => 'ҩ',
'Ҫ' => 'ҫ',
'Ҭ' => 'ҭ',
'Ү' => 'ү',
'Ұ' => 'ұ',
'Ҳ' => 'ҳ',
'Ҵ' => 'ҵ',
'Ҷ' => 'ҷ',
'Ҹ' => 'ҹ',
'Һ' => 'һ',
'Ҽ' => 'ҽ',
'Ҿ' => 'ҿ',
'Ӏ' => 'ӏ',
'Ӂ' => 'ӂ',
'Ӄ' => 'ӄ',
'Ӆ' => 'ӆ',
'Ӈ' => 'ӈ',
'Ӊ' => 'ӊ',
'Ӌ' => 'ӌ',
'Ӎ' => 'ӎ',
'Ӑ' => 'ӑ',
'Ӓ' => 'ӓ',
'Ӕ' => 'ӕ',
'Ӗ' => 'ӗ',
'Ә' => 'ә',
'Ӛ' => 'ӛ',
'Ӝ' => 'ӝ',
'Ӟ' => 'ӟ',
'Ӡ' => 'ӡ',
'Ӣ' => 'ӣ',
'Ӥ' => 'ӥ',
'Ӧ' => 'ӧ',
'Ө' => 'ө',
'Ӫ' => 'ӫ',
'Ӭ' => 'ӭ',
'Ӯ' => 'ӯ',
'Ӱ' => 'ӱ',
'Ӳ' => 'ӳ',
'Ӵ' => 'ӵ',
'Ӷ' => 'ӷ',
'Ӹ' => 'ӹ',
'Ӻ' => 'ӻ',
'Ӽ' => 'ӽ',
'Ӿ' => 'ӿ',
'Ԁ' => 'ԁ',
'Ԃ' => 'ԃ',
'Ԅ' => 'ԅ',
'Ԇ' => 'ԇ',
'Ԉ' => 'ԉ',
'Ԋ' => 'ԋ',
'Ԍ' => 'ԍ',
'Ԏ' => 'ԏ',
'Ԑ' => 'ԑ',
'Ԓ' => 'ԓ',
'Ԕ' => 'ԕ',
'Ԗ' => 'ԗ',
'Ԙ' => 'ԙ',
'Ԛ' => 'ԛ',
'Ԝ' => 'ԝ',
'Ԟ' => 'ԟ',
'Ԡ' => 'ԡ',
'Ԣ' => 'ԣ',
'Ԥ' => 'ԥ',
'Ԧ' => 'ԧ',
'Ԩ' => 'ԩ',
'Ԫ' => 'ԫ',
'Ԭ' => 'ԭ',
'Ԯ' => 'ԯ',
'Ա' => 'ա',
'Բ' => 'բ',
'Գ' => 'գ',
'Դ' => 'դ',
'Ե' => 'ե',
'Զ' => 'զ',
'Է' => 'է',
'Ը' => 'ը',
'Թ' => 'թ',
'Ժ' => 'ժ',
'Ի' => 'ի',
'Լ' => 'լ',
'Խ' => 'խ',
'Ծ' => 'ծ',
'Կ' => 'կ',
'Հ' => 'հ',
'Ձ' => 'ձ',
'Ղ' => 'ղ',
'Ճ' => 'ճ',
'Մ' => 'մ',
'Յ' => 'յ',
'Ն' => 'ն',
'Շ' => 'շ',
'Ո' => 'ո',
'Չ' => 'չ',
'Պ' => 'պ',
'Ջ' => 'ջ',
'Ռ' => 'ռ',
'Ս' => 'ս',
'Վ' => 'վ',
'Տ' => 'տ',
'Ր' => 'ր',
'Ց' => 'ց',
'Ւ' => 'ւ',
'Փ' => 'փ',
'Ք' => 'ք',
'Օ' => 'օ',
'Ֆ' => 'ֆ',
'Ⴀ' => 'ⴀ',
'Ⴁ' => 'ⴁ',
'Ⴂ' => 'ⴂ',
'Ⴃ' => 'ⴃ',
'Ⴄ' => 'ⴄ',
'Ⴅ' => 'ⴅ',
'Ⴆ' => 'ⴆ',
'Ⴇ' => 'ⴇ',
'Ⴈ' => 'ⴈ',
'Ⴉ' => 'ⴉ',
'Ⴊ' => 'ⴊ',
'Ⴋ' => 'ⴋ',
'Ⴌ' => 'ⴌ',
'Ⴍ' => 'ⴍ',
'Ⴎ' => 'ⴎ',
'Ⴏ' => 'ⴏ',
'Ⴐ' => 'ⴐ',
'Ⴑ' => 'ⴑ',
'Ⴒ' => 'ⴒ',
'Ⴓ' => 'ⴓ',
'Ⴔ' => 'ⴔ',
'Ⴕ' => 'ⴕ',
'Ⴖ' => 'ⴖ',
'Ⴗ' => 'ⴗ',
'Ⴘ' => 'ⴘ',
'Ⴙ' => 'ⴙ',
'Ⴚ' => 'ⴚ',
'Ⴛ' => 'ⴛ',
'Ⴜ' => 'ⴜ',
'Ⴝ' => 'ⴝ',
'Ⴞ' => 'ⴞ',
'Ⴟ' => 'ⴟ',
'Ⴠ' => 'ⴠ',
'Ⴡ' => 'ⴡ',
'Ⴢ' => 'ⴢ',
'Ⴣ' => 'ⴣ',
'Ⴤ' => 'ⴤ',
'Ⴥ' => 'ⴥ',
'Ⴧ' => 'ⴧ',
'Ⴭ' => 'ⴭ',
'Ꭰ' => 'ꭰ',
'Ꭱ' => 'ꭱ',
'Ꭲ' => 'ꭲ',
'Ꭳ' => 'ꭳ',
'Ꭴ' => 'ꭴ',
'Ꭵ' => 'ꭵ',
'Ꭶ' => 'ꭶ',
'Ꭷ' => 'ꭷ',
'Ꭸ' => 'ꭸ',
'Ꭹ' => 'ꭹ',
'Ꭺ' => 'ꭺ',
'Ꭻ' => 'ꭻ',
'Ꭼ' => 'ꭼ',
'Ꭽ' => 'ꭽ',
'Ꭾ' => 'ꭾ',
'Ꭿ' => 'ꭿ',
'Ꮀ' => 'ꮀ',
'Ꮁ' => 'ꮁ',
'Ꮂ' => 'ꮂ',
'Ꮃ' => 'ꮃ',
'Ꮄ' => 'ꮄ',
'Ꮅ' => 'ꮅ',
'Ꮆ' => 'ꮆ',
'Ꮇ' => 'ꮇ',
'Ꮈ' => 'ꮈ',
'Ꮉ' => 'ꮉ',
'Ꮊ' => 'ꮊ',
'Ꮋ' => 'ꮋ',
'Ꮌ' => 'ꮌ',
'Ꮍ' => 'ꮍ',
'Ꮎ' => 'ꮎ',
'Ꮏ' => 'ꮏ',
'Ꮐ' => 'ꮐ',
'Ꮑ' => 'ꮑ',
'Ꮒ' => 'ꮒ',
'Ꮓ' => 'ꮓ',
'Ꮔ' => 'ꮔ',
'Ꮕ' => 'ꮕ',
'Ꮖ' => 'ꮖ',
'Ꮗ' => 'ꮗ',
'Ꮘ' => 'ꮘ',
'Ꮙ' => 'ꮙ',
'Ꮚ' => 'ꮚ',
'Ꮛ' => 'ꮛ',
'Ꮜ' => 'ꮜ',
'Ꮝ' => 'ꮝ',
'Ꮞ' => 'ꮞ',
'Ꮟ' => 'ꮟ',
'Ꮠ' => 'ꮠ',
'Ꮡ' => 'ꮡ',
'Ꮢ' => 'ꮢ',
'Ꮣ' => 'ꮣ',
'Ꮤ' => 'ꮤ',
'Ꮥ' => 'ꮥ',
'Ꮦ' => 'ꮦ',
'Ꮧ' => 'ꮧ',
'Ꮨ' => 'ꮨ',
'Ꮩ' => 'ꮩ',
'Ꮪ' => 'ꮪ',
'Ꮫ' => 'ꮫ',
'Ꮬ' => 'ꮬ',
'Ꮭ' => 'ꮭ',
'Ꮮ' => 'ꮮ',
'Ꮯ' => 'ꮯ',
'Ꮰ' => 'ꮰ',
'Ꮱ' => 'ꮱ',
'Ꮲ' => 'ꮲ',
'Ꮳ' => 'ꮳ',
'Ꮴ' => 'ꮴ',
'Ꮵ' => 'ꮵ',
'Ꮶ' => 'ꮶ',
'Ꮷ' => 'ꮷ',
'Ꮸ' => 'ꮸ',
'Ꮹ' => 'ꮹ',
'Ꮺ' => 'ꮺ',
'Ꮻ' => 'ꮻ',
'Ꮼ' => 'ꮼ',
'Ꮽ' => 'ꮽ',
'Ꮾ' => 'ꮾ',
'Ꮿ' => 'ꮿ',
'Ᏸ' => 'ᏸ',
'Ᏹ' => 'ᏹ',
'Ᏺ' => 'ᏺ',
'Ᏻ' => 'ᏻ',
'Ᏼ' => 'ᏼ',
'Ᏽ' => 'ᏽ',
'Ა' => 'ა',
'Ბ' => 'ბ',
'Გ' => 'გ',
'Დ' => 'დ',
'Ე' => 'ე',
'Ვ' => 'ვ',
'Ზ' => 'ზ',
'Თ' => 'თ',
'Ი' => 'ი',
'Კ' => 'კ',
'Ლ' => 'ლ',
'Მ' => 'მ',
'Ნ' => 'ნ',
'Ო' => 'ო',
'Პ' => 'პ',
'Ჟ' => 'ჟ',
'Რ' => 'რ',
'Ს' => 'ს',
'Ტ' => 'ტ',
'Უ' => 'უ',
'Ფ' => 'ფ',
'Ქ' => 'ქ',
'Ღ' => 'ღ',
'Ყ' => 'ყ',
'Შ' => 'შ',
'Ჩ' => 'ჩ',
'Ც' => 'ც',
'Ძ' => 'ძ',
'Წ' => 'წ',
'Ჭ' => 'ჭ',
'Ხ' => 'ხ',
'Ჯ' => 'ჯ',
'Ჰ' => 'ჰ',
'Ჱ' => 'ჱ',
'Ჲ' => 'ჲ',
'Ჳ' => 'ჳ',
'Ჴ' => 'ჴ',
'Ჵ' => 'ჵ',
'Ჶ' => 'ჶ',
'Ჷ' => 'ჷ',
'Ჸ' => 'ჸ',
'Ჹ' => 'ჹ',
'Ჺ' => 'ჺ',
'Ჽ' => 'ჽ',
'Ჾ' => 'ჾ',
'Ჿ' => 'ჿ',
'Ḁ' => 'ḁ',
'Ḃ' => 'ḃ',
'Ḅ' => 'ḅ',
'Ḇ' => 'ḇ',
'Ḉ' => 'ḉ',
'Ḋ' => 'ḋ',
'Ḍ' => 'ḍ',
'Ḏ' => 'ḏ',
'Ḑ' => 'ḑ',
'Ḓ' => 'ḓ',
'Ḕ' => 'ḕ',
'Ḗ' => 'ḗ',
'Ḙ' => 'ḙ',
'Ḛ' => 'ḛ',
'Ḝ' => 'ḝ',
'Ḟ' => 'ḟ',
'Ḡ' => 'ḡ',
'Ḣ' => 'ḣ',
'Ḥ' => 'ḥ',
'Ḧ' => 'ḧ',
'Ḩ' => 'ḩ',
'Ḫ' => 'ḫ',
'Ḭ' => 'ḭ',
'Ḯ' => 'ḯ',
'Ḱ' => 'ḱ',
'Ḳ' => 'ḳ',
'Ḵ' => 'ḵ',
'Ḷ' => 'ḷ',
'Ḹ' => 'ḹ',
'Ḻ' => 'ḻ',
'Ḽ' => 'ḽ',
'Ḿ' => 'ḿ',
'Ṁ' => 'ṁ',
'Ṃ' => 'ṃ',
'Ṅ' => 'ṅ',
'Ṇ' => 'ṇ',
'Ṉ' => 'ṉ',
'Ṋ' => 'ṋ',
'Ṍ' => 'ṍ',
'Ṏ' => 'ṏ',
'Ṑ' => 'ṑ',
'Ṓ' => 'ṓ',
'Ṕ' => 'ṕ',
'Ṗ' => 'ṗ',
'Ṙ' => 'ṙ',
'Ṛ' => 'ṛ',
'Ṝ' => 'ṝ',
'Ṟ' => 'ṟ',
'Ṡ' => 'ṡ',
'Ṣ' => 'ṣ',
'Ṥ' => 'ṥ',
'Ṧ' => 'ṧ',
'Ṩ' => 'ṩ',
'Ṫ' => 'ṫ',
'Ṭ' => 'ṭ',
'Ṯ' => 'ṯ',
'Ṱ' => 'ṱ',
'Ṳ' => 'ṳ',
'Ṵ' => 'ṵ',
'Ṷ' => 'ṷ',
'Ṹ' => 'ṹ',
'Ṻ' => 'ṻ',
'Ṽ' => 'ṽ',
'Ṿ' => 'ṿ',
'Ẁ' => 'ẁ',
'Ẃ' => 'ẃ',
'Ẅ' => 'ẅ',
'Ẇ' => 'ẇ',
'Ẉ' => 'ẉ',
'Ẋ' => 'ẋ',
'Ẍ' => 'ẍ',
'Ẏ' => 'ẏ',
'Ẑ' => 'ẑ',
'Ẓ' => 'ẓ',
'Ẕ' => 'ẕ',
'ẞ' => 'ß',
'Ạ' => 'ạ',
'Ả' => 'ả',
'Ấ' => 'ấ',
'Ầ' => 'ầ',
'Ẩ' => 'ẩ',
'Ẫ' => 'ẫ',
'Ậ' => 'ậ',
'Ắ' => 'ắ',
'Ằ' => 'ằ',
'Ẳ' => 'ẳ',
'Ẵ' => 'ẵ',
'Ặ' => 'ặ',
'Ẹ' => 'ẹ',
'Ẻ' => 'ẻ',
'Ẽ' => 'ẽ',
'Ế' => 'ế',
'Ề' => 'ề',
'Ể' => 'ể',
'Ễ' => 'ễ',
'Ệ' => 'ệ',
'Ỉ' => 'ỉ',
'Ị' => 'ị',
'Ọ' => 'ọ',
'Ỏ' => 'ỏ',
'Ố' => 'ố',
'Ồ' => 'ồ',
'Ổ' => 'ổ',
'Ỗ' => 'ỗ',
'Ộ' => 'ộ',
'Ớ' => 'ớ',
'Ờ' => 'ờ',
'Ở' => 'ở',
'Ỡ' => 'ỡ',
'Ợ' => 'ợ',
'Ụ' => 'ụ',
'Ủ' => 'ủ',
'Ứ' => 'ứ',
'Ừ' => 'ừ',
'Ử' => 'ử',
'Ữ' => 'ữ',
'Ự' => 'ự',
'Ỳ' => 'ỳ',
'Ỵ' => 'ỵ',
'Ỷ' => 'ỷ',
'Ỹ' => 'ỹ',
'Ỻ' => 'ỻ',
'Ỽ' => 'ỽ',
'Ỿ' => 'ỿ',
'Ἀ' => 'ἀ',
'Ἁ' => 'ἁ',
'Ἂ' => 'ἂ',
'Ἃ' => 'ἃ',
'Ἄ' => 'ἄ',
'Ἅ' => 'ἅ',
'Ἆ' => 'ἆ',
'Ἇ' => 'ἇ',
'Ἐ' => 'ἐ',
'Ἑ' => 'ἑ',
'Ἒ' => 'ἒ',
'Ἓ' => 'ἓ',
'Ἔ' => 'ἔ',
'Ἕ' => 'ἕ',
'Ἠ' => 'ἠ',
'Ἡ' => 'ἡ',
'Ἢ' => 'ἢ',
'Ἣ' => 'ἣ',
'Ἤ' => 'ἤ',
'Ἥ' => 'ἥ',
'Ἦ' => 'ἦ',
'Ἧ' => 'ἧ',
'Ἰ' => 'ἰ',
'Ἱ' => 'ἱ',
'Ἲ' => 'ἲ',
'Ἳ' => 'ἳ',
'Ἴ' => 'ἴ',
'Ἵ' => 'ἵ',
'Ἶ' => 'ἶ',
'Ἷ' => 'ἷ',
'Ὀ' => 'ὀ',
'Ὁ' => 'ὁ',
'Ὂ' => 'ὂ',
'Ὃ' => 'ὃ',
'Ὄ' => 'ὄ',
'Ὅ' => 'ὅ',
'Ὑ' => 'ὑ',
'Ὓ' => 'ὓ',
'Ὕ' => 'ὕ',
'Ὗ' => 'ὗ',
'Ὠ' => 'ὠ',
'Ὡ' => 'ὡ',
'Ὢ' => 'ὢ',
'Ὣ' => 'ὣ',
'Ὤ' => 'ὤ',
'Ὥ' => 'ὥ',
'Ὦ' => 'ὦ',
'Ὧ' => 'ὧ',
'ᾈ' => 'ᾀ',
'ᾉ' => 'ᾁ',
'ᾊ' => 'ᾂ',
'ᾋ' => 'ᾃ',
'ᾌ' => 'ᾄ',
'ᾍ' => 'ᾅ',
'ᾎ' => 'ᾆ',
'ᾏ' => 'ᾇ',
'ᾘ' => 'ᾐ',
'ᾙ' => 'ᾑ',
'ᾚ' => 'ᾒ',
'ᾛ' => 'ᾓ',
'ᾜ' => 'ᾔ',
'ᾝ' => 'ᾕ',
'ᾞ' => 'ᾖ',
'ᾟ' => 'ᾗ',
'ᾨ' => 'ᾠ',
'ᾩ' => 'ᾡ',
'ᾪ' => 'ᾢ',
'ᾫ' => 'ᾣ',
'ᾬ' => 'ᾤ',
'ᾭ' => 'ᾥ',
'ᾮ' => 'ᾦ',
'ᾯ' => 'ᾧ',
'Ᾰ' => 'ᾰ',
'Ᾱ' => 'ᾱ',
'Ὰ' => 'ὰ',
'Ά' => 'ά',
'ᾼ' => 'ᾳ',
'Ὲ' => 'ὲ',
'Έ' => 'έ',
'Ὴ' => 'ὴ',
'Ή' => 'ή',
'ῌ' => 'ῃ',
'Ῐ' => 'ῐ',
'Ῑ' => 'ῑ',
'Ὶ' => 'ὶ',
'Ί' => 'ί',
'Ῠ' => 'ῠ',
'Ῡ' => 'ῡ',
'Ὺ' => 'ὺ',
'Ύ' => 'ύ',
'Ῥ' => 'ῥ',
'Ὸ' => 'ὸ',
'Ό' => 'ό',
'Ὼ' => 'ὼ',
'Ώ' => 'ώ',
'ῼ' => 'ῳ',
'Ω' => 'ω',
'K' => 'k',
'Å' => 'å',
'Ⅎ' => 'ⅎ',
'Ⅰ' => 'ⅰ',
'Ⅱ' => 'ⅱ',
'Ⅲ' => 'ⅲ',
'Ⅳ' => 'ⅳ',
'Ⅴ' => 'ⅴ',
'Ⅵ' => 'ⅵ',
'Ⅶ' => 'ⅶ',
'Ⅷ' => 'ⅷ',
'Ⅸ' => 'ⅸ',
'Ⅹ' => 'ⅹ',
'Ⅺ' => 'ⅺ',
'Ⅻ' => 'ⅻ',
'Ⅼ' => 'ⅼ',
'Ⅽ' => 'ⅽ',
'Ⅾ' => 'ⅾ',
'Ⅿ' => 'ⅿ',
'Ↄ' => 'ↄ',
'Ⓐ' => 'ⓐ',
'Ⓑ' => 'ⓑ',
'Ⓒ' => 'ⓒ',
'Ⓓ' => 'ⓓ',
'Ⓔ' => 'ⓔ',
'Ⓕ' => 'ⓕ',
'Ⓖ' => 'ⓖ',
'Ⓗ' => 'ⓗ',
'Ⓘ' => 'ⓘ',
'Ⓙ' => 'ⓙ',
'Ⓚ' => 'ⓚ',
'Ⓛ' => 'ⓛ',
'Ⓜ' => 'ⓜ',
'Ⓝ' => 'ⓝ',
'Ⓞ' => 'ⓞ',
'Ⓟ' => 'ⓟ',
'Ⓠ' => 'ⓠ',
'Ⓡ' => 'ⓡ',
'Ⓢ' => 'ⓢ',
'Ⓣ' => 'ⓣ',
'Ⓤ' => 'ⓤ',
'Ⓥ' => 'ⓥ',
'Ⓦ' => 'ⓦ',
'Ⓧ' => 'ⓧ',
'Ⓨ' => 'ⓨ',
'Ⓩ' => 'ⓩ',
'Ⰰ' => 'ⰰ',
'Ⰱ' => 'ⰱ',
'Ⰲ' => 'ⰲ',
'Ⰳ' => 'ⰳ',
'Ⰴ' => 'ⰴ',
'Ⰵ' => 'ⰵ',
'Ⰶ' => 'ⰶ',
'Ⰷ' => 'ⰷ',
'Ⰸ' => 'ⰸ',
'Ⰹ' => 'ⰹ',
'Ⰺ' => 'ⰺ',
'Ⰻ' => 'ⰻ',
'Ⰼ' => 'ⰼ',
'Ⰽ' => 'ⰽ',
'Ⰾ' => 'ⰾ',
'Ⰿ' => 'ⰿ',
'Ⱀ' => 'ⱀ',
'Ⱁ' => 'ⱁ',
'Ⱂ' => 'ⱂ',
'Ⱃ' => 'ⱃ',
'Ⱄ' => 'ⱄ',
'Ⱅ' => 'ⱅ',
'Ⱆ' => 'ⱆ',
'Ⱇ' => 'ⱇ',
'Ⱈ' => 'ⱈ',
'Ⱉ' => 'ⱉ',
'Ⱊ' => 'ⱊ',
'Ⱋ' => 'ⱋ',
'Ⱌ' => 'ⱌ',
'Ⱍ' => 'ⱍ',
'Ⱎ' => 'ⱎ',
'Ⱏ' => 'ⱏ',
'Ⱐ' => 'ⱐ',
'Ⱑ' => 'ⱑ',
'Ⱒ' => 'ⱒ',
'Ⱓ' => 'ⱓ',
'Ⱔ' => 'ⱔ',
'Ⱕ' => 'ⱕ',
'Ⱖ' => 'ⱖ',
'Ⱗ' => 'ⱗ',
'Ⱘ' => 'ⱘ',
'Ⱙ' => 'ⱙ',
'Ⱚ' => 'ⱚ',
'Ⱛ' => 'ⱛ',
'Ⱜ' => 'ⱜ',
'Ⱝ' => 'ⱝ',
'Ⱞ' => 'ⱞ',
'Ⱡ' => 'ⱡ',
'Ɫ' => 'ɫ',
'Ᵽ' => 'ᵽ',
'Ɽ' => 'ɽ',
'Ⱨ' => 'ⱨ',
'Ⱪ' => 'ⱪ',
'Ⱬ' => 'ⱬ',
'Ɑ' => 'ɑ',
'Ɱ' => 'ɱ',
'Ɐ' => 'ɐ',
'Ɒ' => 'ɒ',
'Ⱳ' => 'ⱳ',
'Ⱶ' => 'ⱶ',
'Ȿ' => 'ȿ',
'Ɀ' => 'ɀ',
'Ⲁ' => 'ⲁ',
'Ⲃ' => 'ⲃ',
'Ⲅ' => 'ⲅ',
'Ⲇ' => 'ⲇ',
'Ⲉ' => 'ⲉ',
'Ⲋ' => 'ⲋ',
'Ⲍ' => 'ⲍ',
'Ⲏ' => 'ⲏ',
'Ⲑ' => 'ⲑ',
'Ⲓ' => 'ⲓ',
'Ⲕ' => 'ⲕ',
'Ⲗ' => 'ⲗ',
'Ⲙ' => 'ⲙ',
'Ⲛ' => 'ⲛ',
'Ⲝ' => 'ⲝ',
'Ⲟ' => 'ⲟ',
'Ⲡ' => 'ⲡ',
'Ⲣ' => 'ⲣ',
'Ⲥ' => 'ⲥ',
'Ⲧ' => 'ⲧ',
'Ⲩ' => 'ⲩ',
'Ⲫ' => 'ⲫ',
'Ⲭ' => 'ⲭ',
'Ⲯ' => 'ⲯ',
'Ⲱ' => 'ⲱ',
'Ⲳ' => 'ⲳ',
'Ⲵ' => 'ⲵ',
'Ⲷ' => 'ⲷ',
'Ⲹ' => 'ⲹ',
'Ⲻ' => 'ⲻ',
'Ⲽ' => 'ⲽ',
'Ⲿ' => 'ⲿ',
'Ⳁ' => 'ⳁ',
'Ⳃ' => 'ⳃ',
'Ⳅ' => 'ⳅ',
'Ⳇ' => 'ⳇ',
'Ⳉ' => 'ⳉ',
'Ⳋ' => 'ⳋ',
'Ⳍ' => 'ⳍ',
'Ⳏ' => 'ⳏ',
'Ⳑ' => 'ⳑ',
'Ⳓ' => 'ⳓ',
'Ⳕ' => 'ⳕ',
'Ⳗ' => 'ⳗ',
'Ⳙ' => 'ⳙ',
'Ⳛ' => 'ⳛ',
'Ⳝ' => 'ⳝ',
'Ⳟ' => 'ⳟ',
'Ⳡ' => 'ⳡ',
'Ⳣ' => 'ⳣ',
'Ⳬ' => 'ⳬ',
'Ⳮ' => 'ⳮ',
'Ⳳ' => 'ⳳ',
'Ꙁ' => 'ꙁ',
'Ꙃ' => 'ꙃ',
'Ꙅ' => 'ꙅ',
'Ꙇ' => 'ꙇ',
'Ꙉ' => 'ꙉ',
'Ꙋ' => 'ꙋ',
'Ꙍ' => 'ꙍ',
'Ꙏ' => 'ꙏ',
'Ꙑ' => 'ꙑ',
'Ꙓ' => 'ꙓ',
'Ꙕ' => 'ꙕ',
'Ꙗ' => 'ꙗ',
'Ꙙ' => 'ꙙ',
'Ꙛ' => 'ꙛ',
'Ꙝ' => 'ꙝ',
'Ꙟ' => 'ꙟ',
'Ꙡ' => 'ꙡ',
'Ꙣ' => 'ꙣ',
'Ꙥ' => 'ꙥ',
'Ꙧ' => 'ꙧ',
'Ꙩ' => 'ꙩ',
'Ꙫ' => 'ꙫ',
'Ꙭ' => 'ꙭ',
'Ꚁ' => 'ꚁ',
'Ꚃ' => 'ꚃ',
'Ꚅ' => 'ꚅ',
'Ꚇ' => 'ꚇ',
'Ꚉ' => 'ꚉ',
'Ꚋ' => 'ꚋ',
'Ꚍ' => 'ꚍ',
'Ꚏ' => 'ꚏ',
'Ꚑ' => 'ꚑ',
'Ꚓ' => 'ꚓ',
'Ꚕ' => 'ꚕ',
'Ꚗ' => 'ꚗ',
'Ꚙ' => 'ꚙ',
'Ꚛ' => 'ꚛ',
'Ꜣ' => 'ꜣ',
'Ꜥ' => 'ꜥ',
'Ꜧ' => 'ꜧ',
'Ꜩ' => 'ꜩ',
'Ꜫ' => 'ꜫ',
'Ꜭ' => 'ꜭ',
'Ꜯ' => 'ꜯ',
'Ꜳ' => 'ꜳ',
'Ꜵ' => 'ꜵ',
'Ꜷ' => 'ꜷ',
'Ꜹ' => 'ꜹ',
'Ꜻ' => 'ꜻ',
'Ꜽ' => 'ꜽ',
'Ꜿ' => 'ꜿ',
'Ꝁ' => 'ꝁ',
'Ꝃ' => 'ꝃ',
'Ꝅ' => 'ꝅ',
'Ꝇ' => 'ꝇ',
'Ꝉ' => 'ꝉ',
'Ꝋ' => 'ꝋ',
'Ꝍ' => 'ꝍ',
'Ꝏ' => 'ꝏ',
'Ꝑ' => 'ꝑ',
'Ꝓ' => 'ꝓ',
'Ꝕ' => 'ꝕ',
'Ꝗ' => 'ꝗ',
'Ꝙ' => 'ꝙ',
'Ꝛ' => 'ꝛ',
'Ꝝ' => 'ꝝ',
'Ꝟ' => 'ꝟ',
'Ꝡ' => 'ꝡ',
'Ꝣ' => 'ꝣ',
'Ꝥ' => 'ꝥ',
'Ꝧ' => 'ꝧ',
'Ꝩ' => 'ꝩ',
'Ꝫ' => 'ꝫ',
'Ꝭ' => 'ꝭ',
'Ꝯ' => 'ꝯ',
'Ꝺ' => 'ꝺ',
'Ꝼ' => 'ꝼ',
'Ᵹ' => 'ᵹ',
'Ꝿ' => 'ꝿ',
'Ꞁ' => 'ꞁ',
'Ꞃ' => 'ꞃ',
'Ꞅ' => 'ꞅ',
'Ꞇ' => 'ꞇ',
'Ꞌ' => 'ꞌ',
'Ɥ' => 'ɥ',
'Ꞑ' => 'ꞑ',
'Ꞓ' => 'ꞓ',
'Ꞗ' => 'ꞗ',
'Ꞙ' => 'ꞙ',
'Ꞛ' => 'ꞛ',
'Ꞝ' => 'ꞝ',
'Ꞟ' => 'ꞟ',
'Ꞡ' => 'ꞡ',
'Ꞣ' => 'ꞣ',
'Ꞥ' => 'ꞥ',
'Ꞧ' => 'ꞧ',
'Ꞩ' => 'ꞩ',
'Ɦ' => 'ɦ',
'Ɜ' => 'ɜ',
'Ɡ' => 'ɡ',
'Ɬ' => 'ɬ',
'Ɪ' => 'ɪ',
'Ʞ' => 'ʞ',
'Ʇ' => 'ʇ',
'Ʝ' => 'ʝ',
'Ꭓ' => 'ꭓ',
'Ꞵ' => 'ꞵ',
'Ꞷ' => 'ꞷ',
'Ꞹ' => 'ꞹ',
'Ꞻ' => 'ꞻ',
'Ꞽ' => 'ꞽ',
'Ꞿ' => 'ꞿ',
'Ꟃ' => 'ꟃ',
'Ꞔ' => 'ꞔ',
'Ʂ' => 'ʂ',
'Ᶎ' => 'ᶎ',
'Ꟈ' => 'ꟈ',
'Ꟊ' => 'ꟊ',
'Ꟶ' => 'ꟶ',
'Ａ' => 'ａ',
'Ｂ' => 'ｂ',
'Ｃ' => 'ｃ',
'Ｄ' => 'ｄ',
'Ｅ' => 'ｅ',
'Ｆ' => 'ｆ',
'Ｇ' => 'ｇ',
'Ｈ' => 'ｈ',
'Ｉ' => 'ｉ',
'Ｊ' => 'ｊ',
'Ｋ' => 'ｋ',
'Ｌ' => 'ｌ',
'Ｍ' => 'ｍ',
'Ｎ' => 'ｎ',
'Ｏ' => 'ｏ',
'Ｐ' => 'ｐ',
'Ｑ' => 'ｑ',
'Ｒ' => 'ｒ',
'Ｓ' => 'ｓ',
'Ｔ' => 'ｔ',
'Ｕ' => 'ｕ',
'Ｖ' => 'ｖ',
'Ｗ' => 'ｗ',
'Ｘ' => 'ｘ',
'Ｙ' => 'ｙ',
'Ｚ' => 'ｚ',
'𐐀' => '𐐨',
'𐐁' => '𐐩',
'𐐂' => '𐐪',
'𐐃' => '𐐫',
'𐐄' => '𐐬',
'𐐅' => '𐐭',
'𐐆' => '𐐮',
'𐐇' => '𐐯',
'𐐈' => '𐐰',
'𐐉' => '𐐱',
'𐐊' => '𐐲',
'𐐋' => '𐐳',
'𐐌' => '𐐴',
'𐐍' => '𐐵',
'𐐎' => '𐐶',
'𐐏' => '𐐷',
'𐐐' => '𐐸',
'𐐑' => '𐐹',
'𐐒' => '𐐺',
'𐐓' => '𐐻',
'𐐔' => '𐐼',
'𐐕' => '𐐽',
'𐐖' => '𐐾',
'𐐗' => '𐐿',
'𐐘' => '𐑀',
'𐐙' => '𐑁',
'𐐚' => '𐑂',
'𐐛' => '𐑃',
'𐐜' => '𐑄',
'𐐝' => '𐑅',
'𐐞' => '𐑆',
'𐐟' => '𐑇',
'𐐠' => '𐑈',
'𐐡' => '𐑉',
'𐐢' => '𐑊',
'𐐣' => '𐑋',
'𐐤' => '𐑌',
'𐐥' => '𐑍',
'𐐦' => '𐑎',
'𐐧' => '𐑏',
'𐒰' => '𐓘',
'𐒱' => '𐓙',
'𐒲' => '𐓚',
'𐒳' => '𐓛',
'𐒴' => '𐓜',
'𐒵' => '𐓝',
'𐒶' => '𐓞',
'𐒷' => '𐓟',
'𐒸' => '𐓠',
'𐒹' => '𐓡',
'𐒺' => '𐓢',
'𐒻' => '𐓣',
'𐒼' => '𐓤',
'𐒽' => '𐓥',
'𐒾' => '𐓦',
'𐒿' => '𐓧',
'𐓀' => '𐓨',
'𐓁' => '𐓩',
'𐓂' => '𐓪',
'𐓃' => '𐓫',
'𐓄' => '𐓬',
'𐓅' => '𐓭',
'𐓆' => '𐓮',
'𐓇' => '𐓯',
'𐓈' => '𐓰',
'𐓉' => '𐓱',
'𐓊' => '𐓲',
'𐓋' => '𐓳',
'𐓌' => '𐓴',
'𐓍' => '𐓵',
'𐓎' => '𐓶',
'𐓏' => '𐓷',
'𐓐' => '𐓸',
'𐓑' => '𐓹',
'𐓒' => '𐓺',
'𐓓' => '𐓻',
'𐲀' => '𐳀',
'𐲁' => '𐳁',
'𐲂' => '𐳂',
'𐲃' => '𐳃',
'𐲄' => '𐳄',
'𐲅' => '𐳅',
'𐲆' => '𐳆',
'𐲇' => '𐳇',
'𐲈' => '𐳈',
'𐲉' => '𐳉',
'𐲊' => '𐳊',
'𐲋' => '𐳋',
'𐲌' => '𐳌',
'𐲍' => '𐳍',
'𐲎' => '𐳎',
'𐲏' => '𐳏',
'𐲐' => '𐳐',
'𐲑' => '𐳑',
'𐲒' => '𐳒',
'𐲓' => '𐳓',
'𐲔' => '𐳔',
'𐲕' => '𐳕',
'𐲖' => '𐳖',
'𐲗' => '𐳗',
'𐲘' => '𐳘',
'𐲙' => '𐳙',
'𐲚' => '𐳚',
'𐲛' => '𐳛',
'𐲜' => '𐳜',
'𐲝' => '𐳝',
'𐲞' => '𐳞',
'𐲟' => '𐳟',
'𐲠' => '𐳠',
'𐲡' => '𐳡',
'𐲢' => '𐳢',
'𐲣' => '𐳣',
'𐲤' => '𐳤',
'𐲥' => '𐳥',
'𐲦' => '𐳦',
'𐲧' => '𐳧',
'𐲨' => '𐳨',
'𐲩' => '𐳩',
'𐲪' => '𐳪',
'𐲫' => '𐳫',
'𐲬' => '𐳬',
'𐲭' => '𐳭',
'𐲮' => '𐳮',
'𐲯' => '𐳯',
'𐲰' => '𐳰',
'𐲱' => '𐳱',
'𐲲' => '𐳲',
'𑢠' => '𑣀',
'𑢡' => '𑣁',
'𑢢' => '𑣂',
'𑢣' => '𑣃',
'𑢤' => '𑣄',
'𑢥' => '𑣅',
'𑢦' => '𑣆',
'𑢧' => '𑣇',
'𑢨' => '𑣈',
'𑢩' => '𑣉',
'𑢪' => '𑣊',
'𑢫' => '𑣋',
'𑢬' => '𑣌',
'𑢭' => '𑣍',
'𑢮' => '𑣎',
'𑢯' => '𑣏',
'𑢰' => '𑣐',
'𑢱' => '𑣑',
'𑢲' => '𑣒',
'𑢳' => '𑣓',
'𑢴' => '𑣔',
'𑢵' => '𑣕',
'𑢶' => '𑣖',
'𑢷' => '𑣗',
'𑢸' => '𑣘',
'𑢹' => '𑣙',
'𑢺' => '𑣚',
'𑢻' => '𑣛',
'𑢼' => '𑣜',
'𑢽' => '𑣝',
'𑢾' => '𑣞',
'𑢿' => '𑣟',
'𖹀' => '𖹠',
'𖹁' => '𖹡',
'𖹂' => '𖹢',
'𖹃' => '𖹣',
'𖹄' => '𖹤',
'𖹅' => '𖹥',
'𖹆' => '𖹦',
'𖹇' => '𖹧',
'𖹈' => '𖹨',
'𖹉' => '𖹩',
'𖹊' => '𖹪',
'𖹋' => '𖹫',
'𖹌' => '𖹬',
'𖹍' => '𖹭',
'𖹎' => '𖹮',
'𖹏' => '𖹯',
'𖹐' => '𖹰',
'𖹑' => '𖹱',
'𖹒' => '𖹲',
'𖹓' => '𖹳',
'𖹔' => '𖹴',
'𖹕' => '𖹵',
'𖹖' => '𖹶',
'𖹗' => '𖹷',
'𖹘' => '𖹸',
'𖹙' => '𖹹',
'𖹚' => '𖹺',
'𖹛' => '𖹻',
'𖹜' => '𖹼',
'𖹝' => '𖹽',
'𖹞' => '𖹾',
'𖹟' => '𖹿',
'𞤀' => '𞤢',
'𞤁' => '𞤣',
'𞤂' => '𞤤',
'𞤃' => '𞤥',
'𞤄' => '𞤦',
'𞤅' => '𞤧',
'𞤆' => '𞤨',
'𞤇' => '𞤩',
'𞤈' => '𞤪',
'𞤉' => '𞤫',
'𞤊' => '𞤬',
'𞤋' => '𞤭',
'𞤌' => '𞤮',
'𞤍' => '𞤯',
'𞤎' => '𞤰',
'𞤏' => '𞤱',
'𞤐' => '𞤲',
'𞤑' => '𞤳',
'𞤒' => '𞤴',
'𞤓' => '𞤵',
'𞤔' => '𞤶',
'𞤕' => '𞤷',
'𞤖' => '𞤸',
'𞤗' => '𞤹',
'𞤘' => '𞤺',
'𞤙' => '𞤻',
'𞤚' => '𞤼',
'𞤛' => '𞤽',
'𞤜' => '𞤾',
'𞤝' => '𞤿',
'𞤞' => '𞥀',
'𞤟' => '𞥁',
'𞤠' => '𞥂',
'𞤡' => '𞥃',
);
<?php










use Symfony\Polyfill\Mbstring as p;

if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}

if (!function_exists('mb_convert_encoding')) {
function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); }
}
if (!function_exists('mb_encode_mimeheader')) {
function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); }
}
if (!function_exists('mb_convert_case')) {
function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
function mb_language($language = null) { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
}
if (!function_exists('mb_check_encoding')) {
function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); }
}
if (!function_exists('mb_detect_order')) {
function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); }
}
if (!function_exists('mb_strpos')) {
function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); }
}
if (!function_exists('mb_http_input')) {
function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); }
}

if (!function_exists('mb_convert_variables')) {
function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); }
}

if (!function_exists('mb_ord')) {
function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); }
}
if (!function_exists('mb_chr')) {
function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
}

if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
}

if (extension_loaded('mbstring')) {
return;
}

if (!defined('MB_CASE_UPPER')) {
define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
define('MB_CASE_TITLE', 2);
}
<?php










use Symfony\Polyfill\Mbstring as p;

if (!function_exists('mb_convert_encoding')) {
function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); }
}
if (!function_exists('mb_encode_mimeheader')) {
function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); }
}
if (!function_exists('mb_convert_case')) {
function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); }
}
if (!function_exists('mb_check_encoding')) {
function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); }
}
if (!function_exists('mb_detect_order')) {
function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); }
}
if (!function_exists('mb_strpos')) {
function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); }
}
if (!function_exists('mb_http_input')) {
function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); }
}

if (!function_exists('mb_convert_variables')) {
function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); }
}

if (!function_exists('mb_ord')) {
function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); }
}
if (!function_exists('mb_chr')) {
function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
}

if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
}

if (extension_loaded('mbstring')) {
return;
}

if (!defined('MB_CASE_UPPER')) {
define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
define('MB_CASE_TITLE', 2);
}
<?php










namespace Symfony\Polyfill\Mbstring;























































final class Mbstring
{
public const MB_CASE_FOLD = \PHP_INT_MAX;

private const SIMPLE_CASE_FOLD = [
['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
];

private static $encodingList = ['ASCII', 'UTF-8'];
private static $language = 'neutral';
private static $internalEncoding = 'UTF-8';

public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
{
if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) {
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
} else {
$fromEncoding = self::getEncoding($fromEncoding);
}

$toEncoding = self::getEncoding($toEncoding);

if ('BASE64' === $fromEncoding) {
$s = base64_decode($s);
$fromEncoding = $toEncoding;
}

if ('BASE64' === $toEncoding) {
return base64_encode($s);
}

if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
$fromEncoding = 'Windows-1252';
}
if ('UTF-8' !== $fromEncoding) {
$s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
}

return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
}

if ('HTML-ENTITIES' === $fromEncoding) {
$s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8');
$fromEncoding = 'UTF-8';
}

return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
}

public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
{
$ok = true;
array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
$ok = false;
}
});

return $ok ? $fromEncoding : false;
}

public static function mb_decode_mimeheader($s)
{
return iconv_mime_decode($s, 2, self::$internalEncoding);
}

public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
{
trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING);
}

public static function mb_decode_numericentity($s, $convmap, $encoding = null)
{
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);

return null;
}

if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
return false;
}

if (null !== $encoding && !\is_scalar($encoding)) {
trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);

return ''; 
}

$s = (string) $s;
if ('' === $s) {
return '';
}

$encoding = self::getEncoding($encoding);

if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}

$cnt = floor(\count($convmap) / 4) * 4;

for ($i = 0; $i < $cnt; $i += 4) {

$convmap[$i] += $convmap[$i + 2];
$convmap[$i + 1] += $convmap[$i + 2];
}

$s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
$c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
for ($i = 0; $i < $cnt; $i += 4) {
if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
return self::mb_chr($c - $convmap[$i + 2]);
}
}

return $m[0];
}, $s);

if (null === $encoding) {
return $s;
}

return iconv('UTF-8', $encoding.'//IGNORE', $s);
}

public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
{
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);

return null;
}

if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
return false;
}

if (null !== $encoding && !\is_scalar($encoding)) {
trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);

return null; 
}

if (null !== $is_hex && !\is_scalar($is_hex)) {
trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING);

return null;
}

$s = (string) $s;
if ('' === $s) {
return '';
}

$encoding = self::getEncoding($encoding);

if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}

static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];

$cnt = floor(\count($convmap) / 4) * 4;
$i = 0;
$len = \strlen($s);
$result = '';

while ($i < $len) {
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;
$c = self::mb_ord($uchr);

for ($j = 0; $j < $cnt; $j += 4) {
if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
$cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
$result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
continue 2;
}
}
$result .= $uchr;
}

if (null === $encoding) {
return $result;
}

return iconv('UTF-8', $encoding.'//IGNORE', $result);
}

public static function mb_convert_case($s, $mode, $encoding = null)
{
$s = (string) $s;
if ('' === $s) {
return '';
}

$encoding = self::getEncoding($encoding);

if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}

if (\MB_CASE_TITLE == $mode) {
static $titleRegexp = null;
if (null === $titleRegexp) {
$titleRegexp = self::getData('titleCaseRegexp');
}
$s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s);
} else {
if (\MB_CASE_UPPER == $mode) {
static $upper = null;
if (null === $upper) {
$upper = self::getData('upperCase');
}
$map = $upper;
} else {
if (self::MB_CASE_FOLD === $mode) {
static $caseFolding = null;
if (null === $caseFolding) {
$caseFolding = self::getData('caseFolding');
}
$s = strtr($s, $caseFolding);
}

static $lower = null;
if (null === $lower) {
$lower = self::getData('lowerCase');
}
$map = $lower;
}

static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];

$i = 0;
$len = \strlen($s);

while ($i < $len) {
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;

if (isset($map[$uchr])) {
$uchr = $map[$uchr];
$nlen = \strlen($uchr);

if ($nlen == $ulen) {
$nlen = $i;
do {
$s[--$nlen] = $uchr[--$ulen];
} while ($ulen);
} else {
$s = substr_replace($s, $uchr, $i - $ulen, $ulen);
$len += $nlen - $ulen;
$i += $nlen - $ulen;
}
}
}
}

if (null === $encoding) {
return $s;
}

return iconv('UTF-8', $encoding.'//IGNORE', $s);
}

public static function mb_internal_encoding($encoding = null)
{
if (null === $encoding) {
return self::$internalEncoding;
}

$normalizedEncoding = self::getEncoding($encoding);

if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
self::$internalEncoding = $normalizedEncoding;

return true;
}

if (80000 > \PHP_VERSION_ID) {
return false;
}

throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
}

public static function mb_language($lang = null)
{
if (null === $lang) {
return self::$language;
}

switch ($normalizedLang = strtolower($lang)) {
case 'uni':
case 'neutral':
self::$language = $normalizedLang;

return true;
}

if (80000 > \PHP_VERSION_ID) {
return false;
}

throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
}

public static function mb_list_encodings()
{
return ['UTF-8'];
}

public static function mb_encoding_aliases($encoding)
{
switch (strtoupper($encoding)) {
case 'UTF8':
case 'UTF-8':
return ['utf8'];
}

return false;
}

public static function mb_check_encoding($var = null, $encoding = null)
{
if (PHP_VERSION_ID < 70200 && \is_array($var)) {
trigger_error('mb_check_encoding() expects parameter 1 to be string, array given', \E_USER_WARNING);

return null;
}

if (null === $encoding) {
if (null === $var) {
return false;
}
$encoding = self::$internalEncoding;
}

if (!\is_array($var)) {
return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var);
}

foreach ($var as $key => $value) {
if (!self::mb_check_encoding($key, $encoding)) {
return false;
}
if (!self::mb_check_encoding($value, $encoding)) {
return false;
}
}

return true;

}

public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
{
if (null === $encodingList) {
$encodingList = self::$encodingList;
} else {
if (!\is_array($encodingList)) {
$encodingList = array_map('trim', explode(',', $encodingList));
}
$encodingList = array_map('strtoupper', $encodingList);
}

foreach ($encodingList as $enc) {
switch ($enc) {
case 'ASCII':
if (!preg_match('/[\x80-\xFF]/', $str)) {
return $enc;
}
break;

case 'UTF8':
case 'UTF-8':
if (preg_match('//u', $str)) {
return 'UTF-8';
}
break;

default:
if (0 === strncmp($enc, 'ISO-8859-', 9)) {
return $enc;
}
}
}

return false;
}

public static function mb_detect_order($encodingList = null)
{
if (null === $encodingList) {
return self::$encodingList;
}

if (!\is_array($encodingList)) {
$encodingList = array_map('trim', explode(',', $encodingList));
}
$encodingList = array_map('strtoupper', $encodingList);

foreach ($encodingList as $enc) {
switch ($enc) {
default:
if (strncmp($enc, 'ISO-8859-', 9)) {
return false;
}

case 'ASCII':
case 'UTF8':
case 'UTF-8':
}
}

self::$encodingList = $encodingList;

return true;
}

public static function mb_strlen($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return \strlen($s);
}

return @iconv_strlen($s, $encoding);
}

public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strpos($haystack, $needle, $offset);
}

$needle = (string) $needle;
if ('' === $needle) {
if (80000 > \PHP_VERSION_ID) {
trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING);

return false;
}

return 0;
}

return iconv_strpos($haystack, $needle, $offset, $encoding);
}

public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strrpos($haystack, $needle, $offset);
}

if ($offset != (int) $offset) {
$offset = 0;
} elseif ($offset = (int) $offset) {
if ($offset < 0) {
if (0 > $offset += self::mb_strlen($needle)) {
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
}
$offset = 0;
} else {
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
}
}

$pos = '' !== $needle || 80000 > \PHP_VERSION_ID
? iconv_strrpos($haystack, $needle, $encoding)
: self::mb_strlen($haystack, $encoding);

return false !== $pos ? $offset + $pos : false;
}

public static function mb_str_split($string, $split_length = 1, $encoding = null)
{
if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING);

return null;
}

if (1 > $split_length = (int) $split_length) {
if (80000 > \PHP_VERSION_ID) {
trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING);

return false;
}

throw new \ValueError('Argument #2 ($length) must be greater than 0');
}

if (null === $encoding) {
$encoding = mb_internal_encoding();
}

if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
$rx = '/(';
while (65535 < $split_length) {
$rx .= '.{65535}';
$split_length -= 65535;
}
$rx .= '.{'.$split_length.'})/us';

return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY);
}

$result = [];
$length = mb_strlen($string, $encoding);

for ($i = 0; $i < $length; $i += $split_length) {
$result[] = mb_substr($string, $i, $split_length, $encoding);
}

return $result;
}

public static function mb_strtolower($s, $encoding = null)
{
return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding);
}

public static function mb_strtoupper($s, $encoding = null)
{
return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding);
}

public static function mb_substitute_character($c = null)
{
if (null === $c) {
return 'none';
}
if (0 === strcasecmp($c, 'none')) {
return true;
}
if (80000 > \PHP_VERSION_ID) {
return false;
}
if (\is_int($c) || 'long' === $c || 'entity' === $c) {
return false;
}

throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
}

public static function mb_substr($s, $start, $length = null, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return (string) substr($s, $start, null === $length ? 2147483647 : $length);
}

if ($start < 0) {
$start = iconv_strlen($s, $encoding) + $start;
if ($start < 0) {
$start = 0;
}
}

if (null === $length) {
$length = 2147483647;
} elseif ($length < 0) {
$length = iconv_strlen($s, $encoding) + $length - $start;
if ($length < 0) {
return '';
}
}

return (string) iconv_substr($s, $start, $length, $encoding);
}

public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
{
[$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [
self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding),
self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding),
]);

return self::mb_strpos($haystack, $needle, $offset, $encoding);
}

public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
{
$pos = self::mb_stripos($haystack, $needle, 0, $encoding);

return self::getSubpart($pos, $part, $haystack, $encoding);
}

public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
$pos = strrpos($haystack, $needle);
} else {
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = iconv_strrpos($haystack, $needle, $encoding);
}

return self::getSubpart($pos, $part, $haystack, $encoding);
}

public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
{
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = self::mb_strripos($haystack, $needle, $encoding);

return self::getSubpart($pos, $part, $haystack, $encoding);
}

public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding);
$needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding);

$haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack);
$needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle);

return self::mb_strrpos($haystack, $needle, $offset, $encoding);
}

public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
{
$pos = strpos($haystack, $needle);
if (false === $pos) {
return false;
}
if ($part) {
return substr($haystack, 0, $pos);
}

return substr($haystack, $pos);
}

public static function mb_get_info($type = 'all')
{
$info = [
'internal_encoding' => self::$internalEncoding,
'http_output' => 'pass',
'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
'func_overload' => 0,
'func_overload_list' => 'no overload',
'mail_charset' => 'UTF-8',
'mail_header_encoding' => 'BASE64',
'mail_body_encoding' => 'BASE64',
'illegal_chars' => 0,
'encoding_translation' => 'Off',
'language' => self::$language,
'detect_order' => self::$encodingList,
'substitute_character' => 'none',
'strict_detection' => 'Off',
];

if ('all' === $type) {
return $info;
}
if (isset($info[$type])) {
return $info[$type];
}

return false;
}

public static function mb_http_input($type = '')
{
return false;
}

public static function mb_http_output($encoding = null)
{
return null !== $encoding ? 'pass' === $encoding : 'pass';
}

public static function mb_strwidth($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);

if ('UTF-8' !== $encoding) {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}

$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);

return ($wide << 1) + iconv_strlen($s, 'UTF-8');
}

public static function mb_substr_count($haystack, $needle, $encoding = null)
{
return substr_count($haystack, $needle);
}

public static function mb_output_handler($contents, $status)
{
return $contents;
}

public static function mb_chr($code, $encoding = null)
{
if (0x80 > $code %= 0x200000) {
$s = \chr($code);
} elseif (0x800 > $code) {
$s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
} elseif (0x10000 > $code) {
$s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
} else {
$s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
}

if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, $encoding, 'UTF-8');
}

return $s;
}

public static function mb_ord($s, $encoding = null)
{
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, 'UTF-8', $encoding);
}

if (1 === \strlen($s)) {
return \ord($s);
}

$code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
if (0xF0 <= $code) {
return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
}
if (0xE0 <= $code) {
return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
}
if (0xC0 <= $code) {
return (($code - 0xC0) << 6) + $s[2] - 0x80;
}

return $code;
}

public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, string $encoding = null): string
{
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
}

if (null === $encoding) {
$encoding = self::mb_internal_encoding();
}

try {
$validEncoding = @self::mb_check_encoding('', $encoding);
} catch (\ValueError $e) {
throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding));
}


if (!$validEncoding) {
throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding));
}

if (self::mb_strlen($pad_string, $encoding) <= 0) {
throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
}

$paddingRequired = $length - self::mb_strlen($string, $encoding);

if ($paddingRequired < 1) {
return $string;
}

switch ($pad_type) {
case \STR_PAD_LEFT:
return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string;
case \STR_PAD_RIGHT:
return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding);
default:
$leftPaddingLength = floor($paddingRequired / 2);
$rightPaddingLength = $paddingRequired - $leftPaddingLength;

return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
}
}

private static function getSubpart($pos, $part, $haystack, $encoding)
{
if (false === $pos) {
return false;
}
if ($part) {
return self::mb_substr($haystack, 0, $pos, $encoding);
}

return self::mb_substr($haystack, $pos, null, $encoding);
}

private static function html_encoding_callback(array $m)
{
$i = 1;
$entities = '';
$m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8'));

while (isset($m[$i])) {
if (0x80 > $m[$i]) {
$entities .= \chr($m[$i++]);
continue;
}
if (0xF0 <= $m[$i]) {
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} elseif (0xE0 <= $m[$i]) {
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} else {
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
}

$entities .= '&#'.$c.';';
}

return $entities;
}

private static function title_case(array $s)
{
return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8');
}

private static function getData($file)
{
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
return require $file;
}

return false;
}

private static function getEncoding($encoding)
{
if (null === $encoding) {
return self::$internalEncoding;
}

if ('UTF-8' === $encoding) {
return 'UTF-8';
}

$encoding = strtoupper($encoding);

if ('8BIT' === $encoding || 'BINARY' === $encoding) {
return 'CP850';
}

if ('UTF8' === $encoding) {
return 'UTF-8';
}

return $encoding;
}
}
<?php










namespace Symfony\Component\CssSelector;

use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser;
use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser;
use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser;
use Symfony\Component\CssSelector\Parser\Shortcut\HashParser;
use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension;
use Symfony\Component\CssSelector\XPath\Translator;







class CssSelectorConverter
{
private $translator;
private array $cache;

private static array $xmlCache = [];
private static array $htmlCache = [];




public function __construct(bool $html = true)
{
$this->translator = new Translator();

if ($html) {
$this->translator->registerExtension(new HtmlExtension($this->translator));
$this->cache = &self::$htmlCache;
} else {
$this->cache = &self::$xmlCache;
}

$this->translator
->registerParserShortcut(new EmptyStringParser())
->registerParserShortcut(new ElementParser())
->registerParserShortcut(new ClassParser())
->registerParserShortcut(new HashParser())
;
}







public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
{
return $this->cache[$prefix][$cssExpr] ?? $this->cache[$prefix][$cssExpr] = $this->translator->cssToXPath($cssExpr, $prefix);
}
}
<?php










namespace Symfony\Component\CssSelector\XPath;

use Symfony\Component\CssSelector\Node\SelectorNode;











interface TranslatorInterface
{



public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string;




public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string;
}
<?php










namespace Symfony\Component\CssSelector\XPath;

use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
use Symfony\Component\CssSelector\Node\FunctionNode;
use Symfony\Component\CssSelector\Node\NodeInterface;
use Symfony\Component\CssSelector\Node\SelectorNode;
use Symfony\Component\CssSelector\Parser\Parser;
use Symfony\Component\CssSelector\Parser\ParserInterface;











class Translator implements TranslatorInterface
{
private $mainParser;




private array $shortcutParsers = [];




private array $extensions = [];

private array $nodeTranslators = [];
private array $combinationTranslators = [];
private array $functionTranslators = [];
private array $pseudoClassTranslators = [];
private array $attributeMatchingTranslators = [];

public function __construct(ParserInterface $parser = null)
{
$this->mainParser = $parser ?? new Parser();

$this
->registerExtension(new Extension\NodeExtension())
->registerExtension(new Extension\CombinationExtension())
->registerExtension(new Extension\FunctionExtension())
->registerExtension(new Extension\PseudoClassExtension())
->registerExtension(new Extension\AttributeMatchingExtension())
;
}

public static function getXpathLiteral(string $element): string
{
if (!str_contains($element, "'")) {
return "'".$element."'";
}

if (!str_contains($element, '"')) {
return '"'.$element.'"';
}

$string = $element;
$parts = [];
while (true) {
if (false !== $pos = strpos($string, "'")) {
$parts[] = sprintf("'%s'", substr($string, 0, $pos));
$parts[] = "\"'\"";
$string = substr($string, $pos + 1);
} else {
$parts[] = "'$string'";
break;
}
}

return sprintf('concat(%s)', implode(', ', $parts));
}




public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
{
$selectors = $this->parseSelectors($cssExpr);


foreach ($selectors as $index => $selector) {
if (null !== $selector->getPseudoElement()) {
throw new ExpressionErrorException('Pseudo-elements are not supported.');
}

$selectors[$index] = $this->selectorToXPath($selector, $prefix);
}

return implode(' | ', $selectors);
}




public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string
{
return ($prefix ?: '').$this->nodeToXPath($selector);
}




public function registerExtension(Extension\ExtensionInterface $extension): static
{
$this->extensions[$extension->getName()] = $extension;

$this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators());
$this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators());
$this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators());
$this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators());
$this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators());

return $this;
}




public function getExtension(string $name): Extension\ExtensionInterface
{
if (!isset($this->extensions[$name])) {
throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name));
}

return $this->extensions[$name];
}




public function registerParserShortcut(ParserInterface $shortcut): static
{
$this->shortcutParsers[] = $shortcut;

return $this;
}




public function nodeToXPath(NodeInterface $node): XPathExpr
{
if (!isset($this->nodeTranslators[$node->getNodeName()])) {
throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName()));
}

return $this->nodeTranslators[$node->getNodeName()]($node, $this);
}




public function addCombination(string $combiner, NodeInterface $xpath, NodeInterface $combinedXpath): XPathExpr
{
if (!isset($this->combinationTranslators[$combiner])) {
throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner));
}

return $this->combinationTranslators[$combiner]($this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath));
}




public function addFunction(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
if (!isset($this->functionTranslators[$function->getName()])) {
throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName()));
}

return $this->functionTranslators[$function->getName()]($xpath, $function);
}




public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr
{
if (!isset($this->pseudoClassTranslators[$pseudoClass])) {
throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass));
}

return $this->pseudoClassTranslators[$pseudoClass]($xpath);
}




public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, ?string $value): XPathExpr
{
if (!isset($this->attributeMatchingTranslators[$operator])) {
throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator));
}

return $this->attributeMatchingTranslators[$operator]($xpath, $attribute, $value);
}




private function parseSelectors(string $css): array
{
foreach ($this->shortcutParsers as $shortcut) {
$tokens = $shortcut->parse($css);

if (!empty($tokens)) {
return $tokens;
}
}

return $this->mainParser->parse($css);
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;

use Symfony\Component\CssSelector\Node;
use Symfony\Component\CssSelector\XPath\Translator;
use Symfony\Component\CssSelector\XPath\XPathExpr;











class NodeExtension extends AbstractExtension
{
public const ELEMENT_NAME_IN_LOWER_CASE = 1;
public const ATTRIBUTE_NAME_IN_LOWER_CASE = 2;
public const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4;

private int $flags;

public function __construct(int $flags = 0)
{
$this->flags = $flags;
}




public function setFlag(int $flag, bool $on): static
{
if ($on && !$this->hasFlag($flag)) {
$this->flags += $flag;
}

if (!$on && $this->hasFlag($flag)) {
$this->flags -= $flag;
}

return $this;
}

public function hasFlag(int $flag): bool
{
return (bool) ($this->flags & $flag);
}




public function getNodeTranslators(): array
{
return [
'Selector' => [$this, 'translateSelector'],
'CombinedSelector' => [$this, 'translateCombinedSelector'],
'Negation' => [$this, 'translateNegation'],
'Function' => [$this, 'translateFunction'],
'Pseudo' => [$this, 'translatePseudo'],
'Attribute' => [$this, 'translateAttribute'],
'Class' => [$this, 'translateClass'],
'Hash' => [$this, 'translateHash'],
'Element' => [$this, 'translateElement'],
];
}

public function translateSelector(Node\SelectorNode $node, Translator $translator): XPathExpr
{
return $translator->nodeToXPath($node->getTree());
}

public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator): XPathExpr
{
return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector());
}

public function translateNegation(Node\NegationNode $node, Translator $translator): XPathExpr
{
$xpath = $translator->nodeToXPath($node->getSelector());
$subXpath = $translator->nodeToXPath($node->getSubSelector());
$subXpath->addNameTest();

if ($subXpath->getCondition()) {
return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition()));
}

return $xpath->addCondition('0');
}

public function translateFunction(Node\FunctionNode $node, Translator $translator): XPathExpr
{
$xpath = $translator->nodeToXPath($node->getSelector());

return $translator->addFunction($xpath, $node);
}

public function translatePseudo(Node\PseudoNode $node, Translator $translator): XPathExpr
{
$xpath = $translator->nodeToXPath($node->getSelector());

return $translator->addPseudoClass($xpath, $node->getIdentifier());
}

public function translateAttribute(Node\AttributeNode $node, Translator $translator): XPathExpr
{
$name = $node->getAttribute();
$safe = $this->isSafeName($name);

if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) {
$name = strtolower($name);
}

if ($node->getNamespace()) {
$name = sprintf('%s:%s', $node->getNamespace(), $name);
$safe = $safe && $this->isSafeName($node->getNamespace());
}

$attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name));
$value = $node->getValue();
$xpath = $translator->nodeToXPath($node->getSelector());

if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) {
$value = strtolower($value);
}

return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value);
}

public function translateClass(Node\ClassNode $node, Translator $translator): XPathExpr
{
$xpath = $translator->nodeToXPath($node->getSelector());

return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName());
}

public function translateHash(Node\HashNode $node, Translator $translator): XPathExpr
{
$xpath = $translator->nodeToXPath($node->getSelector());

return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId());
}

public function translateElement(Node\ElementNode $node): XPathExpr
{
$element = $node->getElement();

if ($element && $this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
$element = strtolower($element);
}

if ($element) {
$safe = $this->isSafeName($element);
} else {
$element = '*';
$safe = true;
}

if ($node->getNamespace()) {
$element = sprintf('%s:%s', $node->getNamespace(), $element);
$safe = $safe && $this->isSafeName($node->getNamespace());
}

$xpath = new XPathExpr('', $element);

if (!$safe) {
$xpath->addNameTest();
}

return $xpath;
}




public function getName(): string
{
return 'node';
}

private function isSafeName(string $name): bool
{
return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name);
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;

use Symfony\Component\CssSelector\XPath\XPathExpr;











class CombinationExtension extends AbstractExtension
{



public function getCombinationTranslators(): array
{
return [
' ' => [$this, 'translateDescendant'],
'>' => [$this, 'translateChild'],
'+' => [$this, 'translateDirectAdjacent'],
'~' => [$this, 'translateIndirectAdjacent'],
];
}

public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
{
return $xpath->join('/descendant-or-self::*/', $combinedXpath);
}

public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
{
return $xpath->join('/', $combinedXpath);
}

public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
{
return $xpath
->join('/following-sibling::', $combinedXpath)
->addNameTest()
->addCondition('position() = 1');
}

public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
{
return $xpath->join('/following-sibling::', $combinedXpath);
}




public function getName(): string
{
return 'combination';
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;

use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
use Symfony\Component\CssSelector\Node\FunctionNode;
use Symfony\Component\CssSelector\XPath\Translator;
use Symfony\Component\CssSelector\XPath\XPathExpr;











class HtmlExtension extends AbstractExtension
{
public function __construct(Translator $translator)
{
$translator
->getExtension('node')
->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true)
->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true);
}




public function getPseudoClassTranslators(): array
{
return [
'checked' => [$this, 'translateChecked'],
'link' => [$this, 'translateLink'],
'disabled' => [$this, 'translateDisabled'],
'enabled' => [$this, 'translateEnabled'],
'selected' => [$this, 'translateSelected'],
'invalid' => [$this, 'translateInvalid'],
'hover' => [$this, 'translateHover'],
'visited' => [$this, 'translateVisited'],
];
}




public function getFunctionTranslators(): array
{
return [
'lang' => [$this, 'translateLang'],
];
}

public function translateChecked(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition(
'(@checked '
."and (name(.) = 'input' or name(.) = 'command')"
."and (@type = 'checkbox' or @type = 'radio'))"
);
}

public function translateLink(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')");
}

public function translateDisabled(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition(
'('
.'@disabled and'
.'('
."(name(.) = 'input' and @type != 'hidden')"
." or name(.) = 'button'"
." or name(.) = 'select'"
." or name(.) = 'textarea'"
." or name(.) = 'command'"
." or name(.) = 'fieldset'"
." or name(.) = 'optgroup'"
." or name(.) = 'option'"
.')'
.') or ('
."(name(.) = 'input' and @type != 'hidden')"
." or name(.) = 'button'"
." or name(.) = 'select'"
." or name(.) = 'textarea'"
.')'
.' and ancestor::fieldset[@disabled]'
);

}

public function translateEnabled(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition(
'('
.'@href and ('
."name(.) = 'a'"
." or name(.) = 'link'"
." or name(.) = 'area'"
.')'
.') or ('
.'('
."name(.) = 'command'"
." or name(.) = 'fieldset'"
." or name(.) = 'optgroup'"
.')'
.' and not(@disabled)'
.') or ('
.'('
."(name(.) = 'input' and @type != 'hidden')"
." or name(.) = 'button'"
." or name(.) = 'select'"
." or name(.) = 'textarea'"
." or name(.) = 'keygen'"
.')'
.' and not (@disabled or ancestor::fieldset[@disabled])'
.') or ('
."name(.) = 'option' and not("
.'@disabled or ancestor::optgroup[@disabled]'
.')'
.')'
);
}




public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
$arguments = $function->getArguments();
foreach ($arguments as $token) {
if (!($token->isString() || $token->isIdentifier())) {
throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
}
}

return $xpath->addCondition(sprintf(
'ancestor-or-self::*[@lang][1][starts-with(concat('
."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')"
.', %s)]',
'lang',
Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-')
));
}

public function translateSelected(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition("(@selected and name(.) = 'option')");
}

public function translateInvalid(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition('0');
}

public function translateHover(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition('0');
}

public function translateVisited(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition('0');
}




public function getName(): string
{
return 'html';
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;











abstract class AbstractExtension implements ExtensionInterface
{



public function getNodeTranslators(): array
{
return [];
}




public function getCombinationTranslators(): array
{
return [];
}




public function getFunctionTranslators(): array
{
return [];
}




public function getPseudoClassTranslators(): array
{
return [];
}




public function getAttributeMatchingTranslators(): array
{
return [];
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;

use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
use Symfony\Component\CssSelector\XPath\XPathExpr;











class PseudoClassExtension extends AbstractExtension
{



public function getPseudoClassTranslators(): array
{
return [
'root' => [$this, 'translateRoot'],
'first-child' => [$this, 'translateFirstChild'],
'last-child' => [$this, 'translateLastChild'],
'first-of-type' => [$this, 'translateFirstOfType'],
'last-of-type' => [$this, 'translateLastOfType'],
'only-child' => [$this, 'translateOnlyChild'],
'only-of-type' => [$this, 'translateOnlyOfType'],
'empty' => [$this, 'translateEmpty'],
];
}

public function translateRoot(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition('not(parent::*)');
}

public function translateFirstChild(XPathExpr $xpath): XPathExpr
{
return $xpath
->addStarPrefix()
->addNameTest()
->addCondition('position() = 1');
}

public function translateLastChild(XPathExpr $xpath): XPathExpr
{
return $xpath
->addStarPrefix()
->addNameTest()
->addCondition('position() = last()');
}




public function translateFirstOfType(XPathExpr $xpath): XPathExpr
{
if ('*' === $xpath->getElement()) {
throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
}

return $xpath
->addStarPrefix()
->addCondition('position() = 1');
}




public function translateLastOfType(XPathExpr $xpath): XPathExpr
{
if ('*' === $xpath->getElement()) {
throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
}

return $xpath
->addStarPrefix()
->addCondition('position() = last()');
}

public function translateOnlyChild(XPathExpr $xpath): XPathExpr
{
return $xpath
->addStarPrefix()
->addNameTest()
->addCondition('last() = 1');
}

public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
{
$element = $xpath->getElement();

return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
}

public function translateEmpty(XPathExpr $xpath): XPathExpr
{
return $xpath->addCondition('not(*) and not(string-length())');
}




public function getName(): string
{
return 'pseudo-class';
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;











interface ExtensionInterface
{







public function getNodeTranslators(): array;






public function getCombinationTranslators(): array;






public function getFunctionTranslators(): array;






public function getPseudoClassTranslators(): array;






public function getAttributeMatchingTranslators(): array;




public function getName(): string;
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;

use Symfony\Component\CssSelector\XPath\Translator;
use Symfony\Component\CssSelector\XPath\XPathExpr;











class AttributeMatchingExtension extends AbstractExtension
{



public function getAttributeMatchingTranslators(): array
{
return [
'exists' => [$this, 'translateExists'],
'=' => [$this, 'translateEquals'],
'~=' => [$this, 'translateIncludes'],
'|=' => [$this, 'translateDashMatch'],
'^=' => [$this, 'translatePrefixMatch'],
'$=' => [$this, 'translateSuffixMatch'],
'*=' => [$this, 'translateSubstringMatch'],
'!=' => [$this, 'translateDifferent'],
];
}

public function translateExists(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition($attribute);
}

public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value)));
}

public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition($value ? sprintf(
'%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)',
$attribute,
Translator::getXpathLiteral(' '.$value.' ')
) : '0');
}

public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition(sprintf(
'%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))',
$attribute,
Translator::getXpathLiteral($value),
Translator::getXpathLiteral($value.'-')
));
}

public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition($value ? sprintf(
'%1$s and starts-with(%1$s, %2$s)',
$attribute,
Translator::getXpathLiteral($value)
) : '0');
}

public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition($value ? sprintf(
'%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s',
$attribute,
\strlen($value) - 1,
Translator::getXpathLiteral($value)
) : '0');
}

public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition($value ? sprintf(
'%1$s and contains(%1$s, %2$s)',
$attribute,
Translator::getXpathLiteral($value)
) : '0');
}

public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
{
return $xpath->addCondition(sprintf(
$value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s',
$attribute,
Translator::getXpathLiteral($value)
));
}




public function getName(): string
{
return 'attribute-matching';
}
}
<?php










namespace Symfony\Component\CssSelector\XPath\Extension;

use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
use Symfony\Component\CssSelector\Node\FunctionNode;
use Symfony\Component\CssSelector\Parser\Parser;
use Symfony\Component\CssSelector\XPath\Translator;
use Symfony\Component\CssSelector\XPath\XPathExpr;











class FunctionExtension extends AbstractExtension
{



public function getFunctionTranslators(): array
{
return [
'nth-child' => [$this, 'translateNthChild'],
'nth-last-child' => [$this, 'translateNthLastChild'],
'nth-of-type' => [$this, 'translateNthOfType'],
'nth-last-of-type' => [$this, 'translateNthLastOfType'],
'contains' => [$this, 'translateContains'],
'lang' => [$this, 'translateLang'],
];
}




public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr
{
try {
[$a, $b] = Parser::parseSeries($function->getArguments());
} catch (SyntaxErrorException $e) {
throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e);
}

$xpath->addStarPrefix();
if ($addNameTest) {
$xpath->addNameTest();
}

if (0 === $a) {
return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
}

if ($a < 0) {
if ($b < 1) {
return $xpath->addCondition('false()');
}

$sign = '<=';
} else {
$sign = '>=';
}

$expr = 'position()';

if ($last) {
$expr = 'last() - '.$expr;
--$b;
}

if (0 !== $b) {
$expr .= ' - '.$b;
}

$conditions = [sprintf('%s %s 0', $expr, $sign)];

if (1 !== $a && -1 !== $a) {
$conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
}

return $xpath->addCondition(implode(' and ', $conditions));








}

public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
return $this->translateNthChild($xpath, $function, true);
}

public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
return $this->translateNthChild($xpath, $function, false, false);
}




public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
if ('*' === $xpath->getElement()) {
throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
}

return $this->translateNthChild($xpath, $function, true, false);
}




public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
$arguments = $function->getArguments();
foreach ($arguments as $token) {
if (!($token->isString() || $token->isIdentifier())) {
throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
}
}

return $xpath->addCondition(sprintf(
'contains(string(.), %s)',
Translator::getXpathLiteral($arguments[0]->getValue())
));
}




public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
{
$arguments = $function->getArguments();
foreach ($arguments as $token) {
if (!($token->isString() || $token->isIdentifier())) {
throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
}
}

return $xpath->addCondition(sprintf(
'lang(%s)',
Translator::getXpathLiteral($arguments[0]->getValue())
));
}




public function getName(): string
{
return 'function';
}
}
<?php










namespace Symfony\Component\CssSelector\XPath;











class XPathExpr
{
private string $path;
private string $element;
private string $condition;

public function __construct(string $path = '', string $element = '*', string $condition = '', bool $starPrefix = false)
{
$this->path = $path;
$this->element = $element;
$this->condition = $condition;

if ($starPrefix) {
$this->addStarPrefix();
}
}

public function getElement(): string
{
return $this->element;
}




public function addCondition(string $condition): static
{
$this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition;

return $this;
}

public function getCondition(): string
{
return $this->condition;
}




public function addNameTest(): static
{
if ('*' !== $this->element) {
$this->addCondition('name() = '.Translator::getXpathLiteral($this->element));
$this->element = '*';
}

return $this;
}




public function addStarPrefix(): static
{
$this->path .= '*/';

return $this;
}






public function join(string $combiner, self $expr): static
{
$path = $this->__toString().$combiner;

if ('*/' !== $expr->path) {
$path .= $expr->path;
}

$this->path = $path;
$this->element = $expr->element;
$this->condition = $expr->condition;

return $this;
}

public function __toString(): string
{
$path = $this->path.$this->element;
$condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']';

return $path.$condition;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Shortcut;

use Symfony\Component\CssSelector\Node\ElementNode;
use Symfony\Component\CssSelector\Node\SelectorNode;
use Symfony\Component\CssSelector\Parser\ParserInterface;















class EmptyStringParser implements ParserInterface
{



public function parse(string $source): array
{

if ('' == $source) {
return [new SelectorNode(new ElementNode(null, '*'))];
}

return [];
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Shortcut;

use Symfony\Component\CssSelector\Node\ElementNode;
use Symfony\Component\CssSelector\Node\SelectorNode;
use Symfony\Component\CssSelector\Parser\ParserInterface;











class ElementParser implements ParserInterface
{



public function parse(string $source): array
{






if (preg_match('/^(?:([a-z]++)\|)?([\w-]++|\*)$/i', trim($source), $matches)) {
return [new SelectorNode(new ElementNode($matches[1] ?: null, $matches[2]))];
}

return [];
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Shortcut;

use Symfony\Component\CssSelector\Node\ElementNode;
use Symfony\Component\CssSelector\Node\HashNode;
use Symfony\Component\CssSelector\Node\SelectorNode;
use Symfony\Component\CssSelector\Parser\ParserInterface;











class HashParser implements ParserInterface
{



public function parse(string $source): array
{







if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+#([\w-]++)$/i', trim($source), $matches)) {
return [
new SelectorNode(new HashNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])),
];
}

return [];
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Shortcut;

use Symfony\Component\CssSelector\Node\ClassNode;
use Symfony\Component\CssSelector\Node\ElementNode;
use Symfony\Component\CssSelector\Node\SelectorNode;
use Symfony\Component\CssSelector\Parser\ParserInterface;











class ClassParser implements ParserInterface
{



public function parse(string $source): array
{







if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+\.([\w-]++)$/i', trim($source), $matches)) {
return [
new SelectorNode(new ClassNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])),
];
}

return [];
}
}
<?php










namespace Symfony\Component\CssSelector\Parser;

use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
use Symfony\Component\CssSelector\Node;
use Symfony\Component\CssSelector\Parser\Tokenizer\Tokenizer;











class Parser implements ParserInterface
{
private $tokenizer;

public function __construct(Tokenizer $tokenizer = null)
{
$this->tokenizer = $tokenizer ?? new Tokenizer();
}




public function parse(string $source): array
{
$reader = new Reader($source);
$stream = $this->tokenizer->tokenize($reader);

return $this->parseSelectorList($stream);
}








public static function parseSeries(array $tokens): array
{
foreach ($tokens as $token) {
if ($token->isString()) {
throw SyntaxErrorException::stringAsFunctionArgument();
}
}

$joined = trim(implode('', array_map(function (Token $token) {
return $token->getValue();
}, $tokens)));

$int = function ($string) {
if (!is_numeric($string)) {
throw SyntaxErrorException::stringAsFunctionArgument();
}

return (int) $string;
};

switch (true) {
case 'odd' === $joined:
return [2, 1];
case 'even' === $joined:
return [2, 0];
case 'n' === $joined:
return [1, 0];
case !str_contains($joined, 'n'):
return [0, $int($joined)];
}

$split = explode('n', $joined);
$first = $split[0] ?? null;

return [
$first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1,
isset($split[1]) && $split[1] ? $int($split[1]) : 0,
];
}

private function parseSelectorList(TokenStream $stream): array
{
$stream->skipWhitespace();
$selectors = [];

while (true) {
$selectors[] = $this->parserSelectorNode($stream);

if ($stream->getPeek()->isDelimiter([','])) {
$stream->getNext();
$stream->skipWhitespace();
} else {
break;
}
}

return $selectors;
}

private function parserSelectorNode(TokenStream $stream): Node\SelectorNode
{
[$result, $pseudoElement] = $this->parseSimpleSelector($stream);

while (true) {
$stream->skipWhitespace();
$peek = $stream->getPeek();

if ($peek->isFileEnd() || $peek->isDelimiter([','])) {
break;
}

if (null !== $pseudoElement) {
throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
}

if ($peek->isDelimiter(['+', '>', '~'])) {
$combinator = $stream->getNext()->getValue();
$stream->skipWhitespace();
} else {
$combinator = ' ';
}

[$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream);
$result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector);
}

return new Node\SelectorNode($result, $pseudoElement);
}






private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false): array
{
$stream->skipWhitespace();

$selectorStart = \count($stream->getUsed());
$result = $this->parseElementNode($stream);
$pseudoElement = null;

while (true) {
$peek = $stream->getPeek();
if ($peek->isWhitespace()
|| $peek->isFileEnd()
|| $peek->isDelimiter([',', '+', '>', '~'])
|| ($insideNegation && $peek->isDelimiter([')']))
) {
break;
}

if (null !== $pseudoElement) {
throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
}

if ($peek->isHash()) {
$result = new Node\HashNode($result, $stream->getNext()->getValue());
} elseif ($peek->isDelimiter(['.'])) {
$stream->getNext();
$result = new Node\ClassNode($result, $stream->getNextIdentifier());
} elseif ($peek->isDelimiter(['['])) {
$stream->getNext();
$result = $this->parseAttributeNode($result, $stream);
} elseif ($peek->isDelimiter([':'])) {
$stream->getNext();

if ($stream->getPeek()->isDelimiter([':'])) {
$stream->getNext();
$pseudoElement = $stream->getNextIdentifier();

continue;
}

$identifier = $stream->getNextIdentifier();
if (\in_array(strtolower($identifier), ['first-line', 'first-letter', 'before', 'after'])) {


$pseudoElement = $identifier;

continue;
}

if (!$stream->getPeek()->isDelimiter(['('])) {
$result = new Node\PseudoNode($result, $identifier);

continue;
}

$stream->getNext();
$stream->skipWhitespace();

if ('not' === strtolower($identifier)) {
if ($insideNegation) {
throw SyntaxErrorException::nestedNot();
}

[$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true);
$next = $stream->getNext();

if (null !== $argumentPseudoElement) {
throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()');
}

if (!$next->isDelimiter([')'])) {
throw SyntaxErrorException::unexpectedToken('")"', $next);
}

$result = new Node\NegationNode($result, $argument);
} else {
$arguments = [];
$next = null;

while (true) {
$stream->skipWhitespace();
$next = $stream->getNext();

if ($next->isIdentifier()
|| $next->isString()
|| $next->isNumber()
|| $next->isDelimiter(['+', '-'])
) {
$arguments[] = $next;
} elseif ($next->isDelimiter([')'])) {
break;
} else {
throw SyntaxErrorException::unexpectedToken('an argument', $next);
}
}

if (empty($arguments)) {
throw SyntaxErrorException::unexpectedToken('at least one argument', $next);
}

$result = new Node\FunctionNode($result, $identifier, $arguments);
}
} else {
throw SyntaxErrorException::unexpectedToken('selector', $peek);
}
}

if (\count($stream->getUsed()) === $selectorStart) {
throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek());
}

return [$result, $pseudoElement];
}

private function parseElementNode(TokenStream $stream): Node\ElementNode
{
$peek = $stream->getPeek();

if ($peek->isIdentifier() || $peek->isDelimiter(['*'])) {
if ($peek->isIdentifier()) {
$namespace = $stream->getNext()->getValue();
} else {
$stream->getNext();
$namespace = null;
}

if ($stream->getPeek()->isDelimiter(['|'])) {
$stream->getNext();
$element = $stream->getNextIdentifierOrStar();
} else {
$element = $namespace;
$namespace = null;
}
} else {
$element = $namespace = null;
}

return new Node\ElementNode($namespace, $element);
}

private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream): Node\AttributeNode
{
$stream->skipWhitespace();
$attribute = $stream->getNextIdentifierOrStar();

if (null === $attribute && !$stream->getPeek()->isDelimiter(['|'])) {
throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek());
}

if ($stream->getPeek()->isDelimiter(['|'])) {
$stream->getNext();

if ($stream->getPeek()->isDelimiter(['='])) {
$namespace = null;
$stream->getNext();
$operator = '|=';
} else {
$namespace = $attribute;
$attribute = $stream->getNextIdentifier();
$operator = null;
}
} else {
$namespace = $operator = null;
}

if (null === $operator) {
$stream->skipWhitespace();
$next = $stream->getNext();

if ($next->isDelimiter([']'])) {
return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null);
} elseif ($next->isDelimiter(['='])) {
$operator = '=';
} elseif ($next->isDelimiter(['^', '$', '*', '~', '|', '!'])
&& $stream->getPeek()->isDelimiter(['='])
) {
$operator = $next->getValue().'=';
$stream->getNext();
} else {
throw SyntaxErrorException::unexpectedToken('operator', $next);
}
}

$stream->skipWhitespace();
$value = $stream->getNext();

if ($value->isNumber()) {

$value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition());
}

if (!($value->isIdentifier() || $value->isString())) {
throw SyntaxErrorException::unexpectedToken('string or identifier', $value);
}

$stream->skipWhitespace();
$next = $stream->getNext();

if (!$next->isDelimiter([']'])) {
throw SyntaxErrorException::unexpectedToken('"]"', $next);
}

return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue());
}
}
<?php










namespace Symfony\Component\CssSelector\Parser;

use Symfony\Component\CssSelector\Node\SelectorNode;











interface ParserInterface
{





public function parse(string $source): array;
}
<?php










namespace Symfony\Component\CssSelector\Parser\Tokenizer;











class TokenizerPatterns
{
private string $unicodeEscapePattern;
private string $simpleEscapePattern;
private string $newLineEscapePattern;
private string $escapePattern;
private string $stringEscapePattern;
private string $nonAsciiPattern;
private string $nmCharPattern;
private string $nmStartPattern;
private string $identifierPattern;
private string $hashPattern;
private string $numberPattern;
private string $quotedStringPattern;

public function __construct()
{
$this->unicodeEscapePattern = '\\\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?';
$this->simpleEscapePattern = '\\\\(.)';
$this->newLineEscapePattern = '\\\\(?:\n|\r\n|\r|\f)';
$this->escapePattern = $this->unicodeEscapePattern.'|\\\\[^\n\r\f0-9a-f]';
$this->stringEscapePattern = $this->newLineEscapePattern.'|'.$this->escapePattern;
$this->nonAsciiPattern = '[^\x00-\x7F]';
$this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern;
$this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern;
$this->identifierPattern = '-?(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*';
$this->hashPattern = '#((?:'.$this->nmCharPattern.')+)';
$this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)';
$this->quotedStringPattern = '([^\n\r\f\\\\%s]|'.$this->stringEscapePattern.')*';
}

public function getNewLineEscapePattern(): string
{
return '~'.$this->newLineEscapePattern.'~';
}

public function getSimpleEscapePattern(): string
{
return '~'.$this->simpleEscapePattern.'~';
}

public function getUnicodeEscapePattern(): string
{
return '~'.$this->unicodeEscapePattern.'~i';
}

public function getIdentifierPattern(): string
{
return '~^'.$this->identifierPattern.'~i';
}

public function getHashPattern(): string
{
return '~^'.$this->hashPattern.'~i';
}

public function getNumberPattern(): string
{
return '~^'.$this->numberPattern.'~';
}

public function getQuotedStringPattern(string $quote): string
{
return '~^'.sprintf($this->quotedStringPattern, $quote).'~i';
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Tokenizer;











class TokenizerEscaping
{
private $patterns;

public function __construct(TokenizerPatterns $patterns)
{
$this->patterns = $patterns;
}

public function escapeUnicode(string $value): string
{
$value = $this->replaceUnicodeSequences($value);

return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value);
}

public function escapeUnicodeAndNewLine(string $value): string
{
$value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value);

return $this->escapeUnicode($value);
}

private function replaceUnicodeSequences(string $value): string
{
return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) {
$c = hexdec($match[1]);

if (0x80 > $c %= 0x200000) {
return \chr($c);
}
if (0x800 > $c) {
return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
}
if (0x10000 > $c) {
return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
}

return '';
}, $value);
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Tokenizer;

use Symfony\Component\CssSelector\Parser\Handler;
use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\Token;
use Symfony\Component\CssSelector\Parser\TokenStream;











class Tokenizer
{



private array $handlers;

public function __construct()
{
$patterns = new TokenizerPatterns();
$escaping = new TokenizerEscaping($patterns);

$this->handlers = [
new Handler\WhitespaceHandler(),
new Handler\IdentifierHandler($patterns, $escaping),
new Handler\HashHandler($patterns, $escaping),
new Handler\StringHandler($patterns, $escaping),
new Handler\NumberHandler($patterns),
new Handler\CommentHandler(),
];
}




public function tokenize(Reader $reader): TokenStream
{
$stream = new TokenStream();

while (!$reader->isEOF()) {
foreach ($this->handlers as $handler) {
if ($handler->handle($reader, $stream)) {
continue 2;
}
}

$stream->push(new Token(Token::TYPE_DELIMITER, $reader->getSubstring(1), $reader->getPosition()));
$reader->moveForward(1);
}

return $stream
->push(new Token(Token::TYPE_FILE_END, null, $reader->getPosition()))
->freeze();
}
}
<?php










namespace Symfony\Component\CssSelector\Parser;

use Symfony\Component\CssSelector\Exception\InternalErrorException;
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;











class TokenStream
{



private array $tokens = [];




private array $used = [];

private int $cursor = 0;
private $peeked;
private bool $peeking = false;






public function push(Token $token): static
{
$this->tokens[] = $token;

return $this;
}






public function freeze(): static
{
return $this;
}






public function getNext(): Token
{
if ($this->peeking) {
$this->peeking = false;
$this->used[] = $this->peeked;

return $this->peeked;
}

if (!isset($this->tokens[$this->cursor])) {
throw new InternalErrorException('Unexpected token stream end.');
}

return $this->tokens[$this->cursor++];
}




public function getPeek(): Token
{
if (!$this->peeking) {
$this->peeked = $this->getNext();
$this->peeking = true;
}

return $this->peeked;
}






public function getUsed(): array
{
return $this->used;
}






public function getNextIdentifier(): string
{
$next = $this->getNext();

if (!$next->isIdentifier()) {
throw SyntaxErrorException::unexpectedToken('identifier', $next);
}

return $next->getValue();
}






public function getNextIdentifierOrStar(): ?string
{
$next = $this->getNext();

if ($next->isIdentifier()) {
return $next->getValue();
}

if ($next->isDelimiter(['*'])) {
return null;
}

throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next);
}




public function skipWhitespace()
{
$peek = $this->getPeek();

if ($peek->isWhitespace()) {
$this->getNext();
}
}
}
<?php










namespace Symfony\Component\CssSelector\Parser;











class Token
{
public const TYPE_FILE_END = 'eof';
public const TYPE_DELIMITER = 'delimiter';
public const TYPE_WHITESPACE = 'whitespace';
public const TYPE_IDENTIFIER = 'identifier';
public const TYPE_HASH = 'hash';
public const TYPE_NUMBER = 'number';
public const TYPE_STRING = 'string';

private ?string $type;
private ?string $value;
private ?int $position;

public function __construct(?string $type, ?string $value, ?int $position)
{
$this->type = $type;
$this->value = $value;
$this->position = $position;
}

public function getType(): ?int
{
return $this->type;
}

public function getValue(): ?string
{
return $this->value;
}

public function getPosition(): ?int
{
return $this->position;
}

public function isFileEnd(): bool
{
return self::TYPE_FILE_END === $this->type;
}

public function isDelimiter(array $values = []): bool
{
if (self::TYPE_DELIMITER !== $this->type) {
return false;
}

if (empty($values)) {
return true;
}

return \in_array($this->value, $values);
}

public function isWhitespace(): bool
{
return self::TYPE_WHITESPACE === $this->type;
}

public function isIdentifier(): bool
{
return self::TYPE_IDENTIFIER === $this->type;
}

public function isHash(): bool
{
return self::TYPE_HASH === $this->type;
}

public function isNumber(): bool
{
return self::TYPE_NUMBER === $this->type;
}

public function isString(): bool
{
return self::TYPE_STRING === $this->type;
}

public function __toString(): string
{
if ($this->value) {
return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position);
}

return sprintf('<%s at %s>', $this->type, $this->position);
}
}
<?php










namespace Symfony\Component\CssSelector\Parser;











class Reader
{
private string $source;
private int $length;
private int $position = 0;

public function __construct(string $source)
{
$this->source = $source;
$this->length = \strlen($source);
}

public function isEOF(): bool
{
return $this->position >= $this->length;
}

public function getPosition(): int
{
return $this->position;
}

public function getRemainingLength(): int
{
return $this->length - $this->position;
}

public function getSubstring(int $length, int $offset = 0): string
{
return substr($this->source, $this->position + $offset, $length);
}

public function getOffset(string $string)
{
$position = strpos($this->source, $string, $this->position);

return false === $position ? false : $position - $this->position;
}

public function findPattern(string $pattern): array|false
{
$source = substr($this->source, $this->position);

if (preg_match($pattern, $source, $matches)) {
return $matches;
}

return false;
}

public function moveForward(int $length)
{
$this->position += $length;
}

public function moveToEnd()
{
$this->position = $this->length;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\Token;
use Symfony\Component\CssSelector\Parser\TokenStream;











class WhitespaceHandler implements HandlerInterface
{



public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern('~^[ \t\r\n\f]+~');

if (false === $match) {
return false;
}

$stream->push(new Token(Token::TYPE_WHITESPACE, $match[0], $reader->getPosition()));
$reader->moveForward(\strlen($match[0]));

return true;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\TokenStream;











class CommentHandler implements HandlerInterface
{



public function handle(Reader $reader, TokenStream $stream): bool
{
if ('/*' !== $reader->getSubstring(2)) {
return false;
}

$offset = $reader->getOffset('*/');

if (false === $offset) {
$reader->moveToEnd();
} else {
$reader->moveForward($offset + 2);
}

return true;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\Token;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
use Symfony\Component\CssSelector\Parser\TokenStream;











class IdentifierHandler implements HandlerInterface
{
private $patterns;
private $escaping;

public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
{
$this->patterns = $patterns;
$this->escaping = $escaping;
}




public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern($this->patterns->getIdentifierPattern());

if (!$match) {
return false;
}

$value = $this->escaping->escapeUnicode($match[0]);
$stream->push(new Token(Token::TYPE_IDENTIFIER, $value, $reader->getPosition()));
$reader->moveForward(\strlen($match[0]));

return true;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\TokenStream;











interface HandlerInterface
{
public function handle(Reader $reader, TokenStream $stream): bool;
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\Token;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
use Symfony\Component\CssSelector\Parser\TokenStream;











class NumberHandler implements HandlerInterface
{
private $patterns;

public function __construct(TokenizerPatterns $patterns)
{
$this->patterns = $patterns;
}




public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern($this->patterns->getNumberPattern());

if (!$match) {
return false;
}

$stream->push(new Token(Token::TYPE_NUMBER, $match[0], $reader->getPosition()));
$reader->moveForward(\strlen($match[0]));

return true;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Exception\InternalErrorException;
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\Token;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
use Symfony\Component\CssSelector\Parser\TokenStream;











class StringHandler implements HandlerInterface
{
private $patterns;
private $escaping;

public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
{
$this->patterns = $patterns;
$this->escaping = $escaping;
}




public function handle(Reader $reader, TokenStream $stream): bool
{
$quote = $reader->getSubstring(1);

if (!\in_array($quote, ["'", '"'])) {
return false;
}

$reader->moveForward(1);
$match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote));

if (!$match) {
throw new InternalErrorException(sprintf('Should have found at least an empty match at %d.', $reader->getPosition()));
}


if (\strlen($match[0]) === $reader->getRemainingLength()) {
throw SyntaxErrorException::unclosedString($reader->getPosition() - 1);
}


if ($quote !== $reader->getSubstring(1, \strlen($match[0]))) {
throw SyntaxErrorException::unclosedString($reader->getPosition() - 1);
}

$string = $this->escaping->escapeUnicodeAndNewLine($match[0]);
$stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition()));
$reader->moveForward(\strlen($match[0]) + 1);

return true;
}
}
<?php










namespace Symfony\Component\CssSelector\Parser\Handler;

use Symfony\Component\CssSelector\Parser\Reader;
use Symfony\Component\CssSelector\Parser\Token;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping;
use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns;
use Symfony\Component\CssSelector\Parser\TokenStream;











class HashHandler implements HandlerInterface
{
private $patterns;
private $escaping;

public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
{
$this->patterns = $patterns;
$this->escaping = $escaping;
}




public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern($this->patterns->getHashPattern());

if (!$match) {
return false;
}

$value = $this->escaping->escapeUnicode($match[1]);
$stream->push(new Token(Token::TYPE_HASH, $value, $reader->getPosition()));
$reader->moveForward(\strlen($match[0]));

return true;
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











interface NodeInterface
{
public function getNodeName(): string;

public function getSpecificity(): Specificity;

public function __toString(): string;
}
<?php










namespace Symfony\Component\CssSelector\Node;











class HashNode extends AbstractNode
{
private $selector;
private string $id;

public function __construct(NodeInterface $selector, string $id)
{
$this->selector = $selector;
$this->id = $id;
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getId(): string
{
return $this->id;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0));
}

public function __toString(): string
{
return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











abstract class AbstractNode implements NodeInterface
{
private string $nodeName;

public function getNodeName(): string
{
return $this->nodeName ??= preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class CombinedSelectorNode extends AbstractNode
{
private $selector;
private string $combinator;
private $subSelector;

public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector)
{
$this->selector = $selector;
$this->combinator = $combinator;
$this->subSelector = $subSelector;
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getCombinator(): string
{
return $this->combinator;
}

public function getSubSelector(): NodeInterface
{
return $this->subSelector;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity());
}

public function __toString(): string
{
$combinator = ' ' === $this->combinator ? '<followed>' : $this->combinator;

return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class ElementNode extends AbstractNode
{
private ?string $namespace;
private ?string $element;

public function __construct(string $namespace = null, string $element = null)
{
$this->namespace = $namespace;
$this->element = $element;
}

public function getNamespace(): ?string
{
return $this->namespace;
}

public function getElement(): ?string
{
return $this->element;
}




public function getSpecificity(): Specificity
{
return new Specificity(0, 0, $this->element ? 1 : 0);
}

public function __toString(): string
{
$element = $this->element ?: '*';

return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class ClassNode extends AbstractNode
{
private $selector;
private string $name;

public function __construct(NodeInterface $selector, string $name)
{
$this->selector = $selector;
$this->name = $name;
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getName(): string
{
return $this->name;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
}

public function __toString(): string
{
return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;













class Specificity
{
public const A_FACTOR = 100;
public const B_FACTOR = 10;
public const C_FACTOR = 1;

private int $a;
private int $b;
private int $c;

public function __construct(int $a, int $b, int $c)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
}

public function plus(self $specificity): self
{
return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c);
}

public function getValue(): int
{
return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR;
}





public function compareTo(self $specificity): int
{
if ($this->a !== $specificity->a) {
return $this->a > $specificity->a ? 1 : -1;
}

if ($this->b !== $specificity->b) {
return $this->b > $specificity->b ? 1 : -1;
}

if ($this->c !== $specificity->c) {
return $this->c > $specificity->c ? 1 : -1;
}

return 0;
}
}
<?php










namespace Symfony\Component\CssSelector\Node;

use Symfony\Component\CssSelector\Parser\Token;











class FunctionNode extends AbstractNode
{
private $selector;
private string $name;
private array $arguments;




public function __construct(NodeInterface $selector, string $name, array $arguments = [])
{
$this->selector = $selector;
$this->name = strtolower($name);
$this->arguments = $arguments;
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getName(): string
{
return $this->name;
}




public function getArguments(): array
{
return $this->arguments;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
}

public function __toString(): string
{
$arguments = implode(', ', array_map(function (Token $token) {
return "'".$token->getValue()."'";
}, $this->arguments));

return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : '');
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class NegationNode extends AbstractNode
{
private $selector;
private $subSelector;

public function __construct(NodeInterface $selector, NodeInterface $subSelector)
{
$this->selector = $selector;
$this->subSelector = $subSelector;
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getSubSelector(): NodeInterface
{
return $this->subSelector;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity());
}

public function __toString(): string
{
return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class SelectorNode extends AbstractNode
{
private $tree;
private ?string $pseudoElement;

public function __construct(NodeInterface $tree, string $pseudoElement = null)
{
$this->tree = $tree;
$this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null;
}

public function getTree(): NodeInterface
{
return $this->tree;
}

public function getPseudoElement(): ?string
{
return $this->pseudoElement;
}




public function getSpecificity(): Specificity
{
return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0));
}

public function __toString(): string
{
return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : '');
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class PseudoNode extends AbstractNode
{
private $selector;
private string $identifier;

public function __construct(NodeInterface $selector, string $identifier)
{
$this->selector = $selector;
$this->identifier = strtolower($identifier);
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getIdentifier(): string
{
return $this->identifier;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
}

public function __toString(): string
{
return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier);
}
}
<?php










namespace Symfony\Component\CssSelector\Node;











class AttributeNode extends AbstractNode
{
private $selector;
private ?string $namespace;
private string $attribute;
private string $operator;
private ?string $value;

public function __construct(NodeInterface $selector, ?string $namespace, string $attribute, string $operator, ?string $value)
{
$this->selector = $selector;
$this->namespace = $namespace;
$this->attribute = $attribute;
$this->operator = $operator;
$this->value = $value;
}

public function getSelector(): NodeInterface
{
return $this->selector;
}

public function getNamespace(): ?string
{
return $this->namespace;
}

public function getAttribute(): string
{
return $this->attribute;
}

public function getOperator(): string
{
return $this->operator;
}

public function getValue(): ?string
{
return $this->value;
}




public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
}

public function __toString(): string
{
$attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute;

return 'exists' === $this->operator
? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute)
: sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value);
}
}
<?php










namespace Symfony\Component\CssSelector\Exception;

use Symfony\Component\CssSelector\Parser\Token;









class SyntaxErrorException extends ParseException
{
public static function unexpectedToken(string $expectedValue, Token $foundToken): self
{
return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken));
}

public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation): self
{
return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation));
}

public static function unclosedString(int $position): self
{
return new self(sprintf('Unclosed/invalid string at %s.', $position));
}

public static function nestedNot(): self
{
return new self('Got nested ::not().');
}

public static function stringAsFunctionArgument(): self
{
return new self('String not allowed as function argument.');
}
}
<?php










namespace Symfony\Component\CssSelector\Exception;









class ParseException extends \Exception implements ExceptionInterface
{
}
<?php










namespace Symfony\Component\CssSelector\Exception;









interface ExceptionInterface extends \Throwable
{
}
<?php










namespace Symfony\Component\CssSelector\Exception;









class ExpressionErrorException extends ParseException
{
}
<?php










namespace Symfony\Component\CssSelector\Exception;









class InternalErrorException extends ParseException
{
}
<?php










namespace Symfony\Component\Process;

use Symfony\Component\Process\Exception\RuntimeException;








class InputStream implements \IteratorAggregate
{

private $onEmpty = null;
private $input = [];
private $open = true;




public function onEmpty(callable $onEmpty = null)
{
$this->onEmpty = $onEmpty;
}







public function write(mixed $input)
{
if (null === $input) {
return;
}
if ($this->isClosed()) {
throw new RuntimeException(sprintf('"%s" is closed.', static::class));
}
$this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
}




public function close()
{
$this->open = false;
}




public function isClosed()
{
return !$this->open;
}

public function getIterator(): \Traversable
{
$this->open = true;

while ($this->open || $this->input) {
if (!$this->input) {
yield '';
continue;
}
$current = array_shift($this->input);

if ($current instanceof \Iterator) {
yield from $current;
} else {
yield $current;
}
if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) {
$this->write($onEmpty($this));
}
}
}
}
<?php










namespace Symfony\Component\Process;

use Symfony\Component\Process\Exception\InvalidArgumentException;








class ProcessUtils
{



private function __construct()
{
}









public static function validateInput(string $caller, mixed $input): mixed
{
if (null !== $input) {
if (\is_resource($input)) {
return $input;
}
if (\is_string($input)) {
return $input;
}
if (\is_scalar($input)) {
return (string) $input;
}
if ($input instanceof Process) {
return $input->getIterator($input::ITER_SKIP_ERR);
}
if ($input instanceof \Iterator) {
return $input;
}
if ($input instanceof \Traversable) {
return new \IteratorIterator($input);
}

throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller));
}

return $input;
}
}
<?php










namespace Symfony\Component\Process\Pipes;

use Symfony\Component\Process\Exception\InvalidArgumentException;






abstract class AbstractPipes implements PipesInterface
{
public array $pipes = [];

private $inputBuffer = '';
private $input;
private $blocked = true;
private $lastError;




public function __construct(mixed $input)
{
if (\is_resource($input) || $input instanceof \Iterator) {
$this->input = $input;
} elseif (\is_string($input)) {
$this->inputBuffer = $input;
} else {
$this->inputBuffer = (string) $input;
}
}




public function close()
{
foreach ($this->pipes as $pipe) {
if (\is_resource($pipe)) {
fclose($pipe);
}
}
$this->pipes = [];
}




protected function hasSystemCallBeenInterrupted(): bool
{
$lastError = $this->lastError;
$this->lastError = null;


return null !== $lastError && false !== stripos($lastError, 'interrupted system call');
}




protected function unblock()
{
if (!$this->blocked) {
return;
}

foreach ($this->pipes as $pipe) {
stream_set_blocking($pipe, 0);
}
if (\is_resource($this->input)) {
stream_set_blocking($this->input, 0);
}

$this->blocked = false;
}






protected function write(): ?array
{
if (!isset($this->pipes[0])) {
return null;
}
$input = $this->input;

if ($input instanceof \Iterator) {
if (!$input->valid()) {
$input = null;
} elseif (\is_resource($input = $input->current())) {
stream_set_blocking($input, 0);
} elseif (!isset($this->inputBuffer[0])) {
if (!\is_string($input)) {
if (!\is_scalar($input)) {
throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input)));
}
$input = (string) $input;
}
$this->inputBuffer = $input;
$this->input->next();
$input = null;
} else {
$input = null;
}
}

$r = $e = [];
$w = [$this->pipes[0]];


if (false === @stream_select($r, $w, $e, 0, 0)) {
return null;
}

foreach ($w as $stdin) {
if (isset($this->inputBuffer[0])) {
$written = fwrite($stdin, $this->inputBuffer);
$this->inputBuffer = substr($this->inputBuffer, $written);
if (isset($this->inputBuffer[0])) {
return [$this->pipes[0]];
}
}

if ($input) {
while (true) {
$data = fread($input, self::CHUNK_SIZE);
if (!isset($data[0])) {
break;
}
$written = fwrite($stdin, $data);
$data = substr($data, $written);
if (isset($data[0])) {
$this->inputBuffer = $data;

return [$this->pipes[0]];
}
}
if (feof($input)) {
if ($this->input instanceof \Iterator) {
$this->input->next();
} else {
$this->input = null;
}
}
}
}


if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
$this->input = null;
fclose($this->pipes[0]);
unset($this->pipes[0]);
} elseif (!$w) {
return [$this->pipes[0]];
}

return null;
}




public function handleError(int $type, string $msg)
{
$this->lastError = $msg;
}
}
<?php










namespace Symfony\Component\Process\Pipes;

use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Process;











class WindowsPipes extends AbstractPipes
{
private $files = [];
private $fileHandles = [];
private $lockHandles = [];
private $readBytes = [
Process::STDOUT => 0,
Process::STDERR => 0,
];
private $haveReadSupport;

public function __construct(mixed $input, bool $haveReadSupport)
{
$this->haveReadSupport = $haveReadSupport;

if ($this->haveReadSupport) {




$pipes = [
Process::STDOUT => Process::OUT,
Process::STDERR => Process::ERR,
];
$tmpDir = sys_get_temp_dir();
$lastError = 'unknown reason';
set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; });
for ($i = 0;; ++$i) {
foreach ($pipes as $pipe => $name) {
$file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name);

if (!$h = fopen($file.'.lock', 'w')) {
if (file_exists($file.'.lock')) {
continue 2;
}
restore_error_handler();
throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
}
if (!flock($h, \LOCK_EX | \LOCK_NB)) {
continue 2;
}
if (isset($this->lockHandles[$pipe])) {
flock($this->lockHandles[$pipe], \LOCK_UN);
fclose($this->lockHandles[$pipe]);
}
$this->lockHandles[$pipe] = $h;

if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) {
flock($this->lockHandles[$pipe], \LOCK_UN);
fclose($this->lockHandles[$pipe]);
unset($this->lockHandles[$pipe]);
continue 2;
}
$this->fileHandles[$pipe] = $h;
$this->files[$pipe] = $file;
}
break;
}
restore_error_handler();
}

parent::__construct($input);
}

public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}

public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}

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




public function getDescriptors(): array
{
if (!$this->haveReadSupport) {
$nullstream = fopen('NUL', 'c');

return [
['pipe', 'r'],
$nullstream,
$nullstream,
];
}




return [
['pipe', 'r'],
['file', 'NUL', 'w'],
['file', 'NUL', 'w'],
];
}




public function getFiles(): array
{
return $this->files;
}




public function readAndWrite(bool $blocking, bool $close = false): array
{
$this->unblock();
$w = $this->write();
$read = $r = $e = [];

if ($blocking) {
if ($w) {
@stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6);
} elseif ($this->fileHandles) {
usleep(Process::TIMEOUT_PRECISION * 1E6);
}
}
foreach ($this->fileHandles as $type => $fileHandle) {
$data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]);

if (isset($data[0])) {
$this->readBytes[$type] += \strlen($data);
$read[$type] = $data;
}
if ($close) {
ftruncate($fileHandle, 0);
fclose($fileHandle);
flock($this->lockHandles[$type], \LOCK_UN);
fclose($this->lockHandles[$type]);
unset($this->fileHandles[$type], $this->lockHandles[$type]);
}
}

return $read;
}




public function haveReadSupport(): bool
{
return $this->haveReadSupport;
}




public function areOpen(): bool
{
return $this->pipes && $this->fileHandles;
}




public function close()
{
parent::close();
foreach ($this->fileHandles as $type => $handle) {
ftruncate($handle, 0);
fclose($handle);
flock($this->lockHandles[$type], \LOCK_UN);
fclose($this->lockHandles[$type]);
}
$this->fileHandles = $this->lockHandles = [];
}
}
<?php










namespace Symfony\Component\Process\Pipes;

use Symfony\Component\Process\Process;








class UnixPipes extends AbstractPipes
{
private $ttyMode;
private $ptyMode;
private $haveReadSupport;

public function __construct(?bool $ttyMode, bool $ptyMode, mixed $input, bool $haveReadSupport)
{
$this->ttyMode = $ttyMode;
$this->ptyMode = $ptyMode;
$this->haveReadSupport = $haveReadSupport;

parent::__construct($input);
}

public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}

public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}

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




public function getDescriptors(): array
{
if (!$this->haveReadSupport) {
$nullstream = fopen('/dev/null', 'c');

return [
['pipe', 'r'],
$nullstream,
$nullstream,
];
}

if ($this->ttyMode) {
return [
['file', '/dev/tty', 'r'],
['file', '/dev/tty', 'w'],
['file', '/dev/tty', 'w'],
];
}

if ($this->ptyMode && Process::isPtySupported()) {
return [
['pty'],
['pty'],
['pty'],
];
}

return [
['pipe', 'r'],
['pipe', 'w'], 
['pipe', 'w'], 
];
}




public function getFiles(): array
{
return [];
}




public function readAndWrite(bool $blocking, bool $close = false): array
{
$this->unblock();
$w = $this->write();

$read = $e = [];
$r = $this->pipes;
unset($r[0]);


set_error_handler([$this, 'handleError']);
if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
restore_error_handler();


if (!$this->hasSystemCallBeenInterrupted()) {
$this->pipes = [];
}

return $read;
}
restore_error_handler();

foreach ($r as $pipe) {


$read[$type = array_search($pipe, $this->pipes, true)] = '';

do {
$data = @fread($pipe, self::CHUNK_SIZE);
$read[$type] .= $data;
} while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1])));

if (!isset($read[$type][0])) {
unset($read[$type]);
}

if ($close && feof($pipe)) {
fclose($pipe);
unset($this->pipes[$type]);
}
}

return $read;
}




public function haveReadSupport(): bool
{
return $this->haveReadSupport;
}




public function areOpen(): bool
{
return (bool) $this->pipes;
}
}
<?php










namespace Symfony\Component\Process\Pipes;








interface PipesInterface
{
public const CHUNK_SIZE = 16384;




public function getDescriptors(): array;






public function getFiles(): array;









public function readAndWrite(bool $blocking, bool $close = false): array;




public function areOpen(): bool;




public function haveReadSupport(): bool;




public function close();
}
<?php










namespace Symfony\Component\Process;







class PhpExecutableFinder
{
private $executableFinder;

public function __construct()
{
$this->executableFinder = new ExecutableFinder();
}




public function find(bool $includeArgs = true): string|false
{
if ($php = getenv('PHP_BINARY')) {
if (!is_executable($php)) {
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v';
if ($php = strtok(exec($command.' '.escapeshellarg($php)), \PHP_EOL)) {
if (!is_executable($php)) {
return false;
}
} else {
return false;
}
}

if (@is_dir($php)) {
return false;
}

return $php;
}

$args = $this->findArguments();
$args = $includeArgs && $args ? ' '.implode(' ', $args) : '';


if (\PHP_BINARY && \in_array(\PHP_SAPI, ['cgi-fcgi', 'cli', 'cli-server', 'phpdbg'], true)) {
return \PHP_BINARY.$args;
}

if ($php = getenv('PHP_PATH')) {
if (!@is_executable($php) || @is_dir($php)) {
return false;
}

return $php;
}

if ($php = getenv('PHP_PEAR_PHP_BIN')) {
if (@is_executable($php) && !@is_dir($php)) {
return $php;
}
}

if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php')) && !@is_dir($php)) {
return $php;
}

$dirs = [\PHP_BINDIR];
if ('\\' === \DIRECTORY_SEPARATOR) {
$dirs[] = 'C:\xampp\php\\';
}

return $this->executableFinder->find('php', false, $dirs);
}




public function findArguments(): array
{
$arguments = [];
if ('phpdbg' === \PHP_SAPI) {
$arguments[] = '-qrr';
}

return $arguments;
}
}
<?php










namespace Symfony\Component\Process;

use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\RuntimeException;










class PhpProcess extends Process
{







public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null)
{
if (null === $php) {
$executableFinder = new PhpExecutableFinder();
$php = $executableFinder->find(false);
$php = false === $php ? null : array_merge([$php], $executableFinder->findArguments());
}
if ('phpdbg' === \PHP_SAPI) {
$file = tempnam(sys_get_temp_dir(), 'dbg');
file_put_contents($file, $script);
register_shutdown_function('unlink', $file);
$php[] = $file;
$script = null;
}

parent::__construct($php, $cwd, $env, $script, $timeout);
}




public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, mixed $input = null, ?float $timeout = 60): static
{
throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class));
}




public function start(callable $callback = null, array $env = [])
{
if (null === $this->getCommandLine()) {
throw new RuntimeException('Unable to find the PHP executable.');
}

parent::start($callback, $env);
}
}
<?php










namespace Symfony\Component\Process;







class ExecutableFinder
{
private $suffixes = ['.exe', '.bat', '.cmd', '.com'];




public function setSuffixes(array $suffixes)
{
$this->suffixes = $suffixes;
}




public function addSuffix(string $suffix)
{
$this->suffixes[] = $suffix;
}








public function find(string $name, string $default = null, array $extraDirs = []): ?string
{
if (\ini_get('open_basedir')) {
$searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs);
$dirs = [];
foreach ($searchPath as $path) {

if (@is_dir($path)) {
$dirs[] = $path;
} else {
if (basename($path) == $name && @is_executable($path)) {
return $path;
}
}
}
} else {
$dirs = array_merge(
explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
$extraDirs
);
}

$suffixes = [''];
if ('\\' === \DIRECTORY_SEPARATOR) {
$pathExt = getenv('PATHEXT');
$suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
}
foreach ($suffixes as $suffix) {
foreach ($dirs as $dir) {
if (@is_file($file = $dir.\DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === \DIRECTORY_SEPARATOR || @is_executable($file))) {
return $file;
}
}
}

return $default;
}
}
<?php










namespace Symfony\Component\Process;

use Symfony\Component\Process\Exception\InvalidArgumentException;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Pipes\PipesInterface;
use Symfony\Component\Process\Pipes\UnixPipes;
use Symfony\Component\Process\Pipes\WindowsPipes;










class Process implements \IteratorAggregate
{
public const ERR = 'err';
public const OUT = 'out';

public const STATUS_READY = 'ready';
public const STATUS_STARTED = 'started';
public const STATUS_TERMINATED = 'terminated';

public const STDIN = 0;
public const STDOUT = 1;
public const STDERR = 2;


public const TIMEOUT_PRECISION = 0.2;

public const ITER_NON_BLOCKING = 1; 
public const ITER_KEEP_OUTPUT = 2; 
public const ITER_SKIP_OUT = 4; 
public const ITER_SKIP_ERR = 8; 

private $callback;
private $hasCallback = false;
private $commandline;
private $cwd;
private $env = [];
private $input;
private $starttime;
private $lastOutputTime;
private $timeout;
private $idleTimeout;
private $exitcode;
private $fallbackStatus = [];
private $processInformation;
private $outputDisabled = false;
private $stdout;
private $stderr;
private $process;
private $status = self::STATUS_READY;
private $incrementalOutputOffset = 0;
private $incrementalErrorOutputOffset = 0;
private $tty = false;
private $pty;
private $options = ['suppress_errors' => true, 'bypass_shell' => true];

private $useFileHandles = false;

private $processPipes;

private $latestSignal;

private static $sigchild;






public static $exitCodes = [
0 => 'OK',
1 => 'General error',
2 => 'Misuse of shell builtins',

126 => 'Invoked command cannot execute',
127 => 'Command not found',
128 => 'Invalid exit argument',


129 => 'Hangup',
130 => 'Interrupt',
131 => 'Quit and dump core',
132 => 'Illegal instruction',
133 => 'Trace/breakpoint trap',
134 => 'Process aborted',
135 => 'Bus error: "access to undefined portion of memory object"',
136 => 'Floating point exception: "erroneous arithmetic operation"',
137 => 'Kill (terminate immediately)',
138 => 'User-defined 1',
139 => 'Segmentation violation',
140 => 'User-defined 2',
141 => 'Write to pipe with no one reading',
142 => 'Signal raised by alarm',
143 => 'Termination (request to terminate)',

145 => 'Child process terminated, stopped (or continued*)',
146 => 'Continue if stopped',
147 => 'Stop executing temporarily',
148 => 'Terminal stop signal',
149 => 'Background process attempting to read from tty ("in")',
150 => 'Background process attempting to write to tty ("out")',
151 => 'Urgent data available on socket',
152 => 'CPU time limit exceeded',
153 => 'File size limit exceeded',
154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
155 => 'Profiling timer expired',

157 => 'Pollable event',

159 => 'Bad syscall',
];










public function __construct(array $command, string $cwd = null, array $env = null, mixed $input = null, ?float $timeout = 60)
{
if (!\function_exists('proc_open')) {
throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.');
}

$this->commandline = $command;
$this->cwd = $cwd;





if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) {
$this->cwd = getcwd();
}
if (null !== $env) {
$this->setEnv($env);
}

$this->setInput($input);
$this->setTimeout($timeout);
$this->useFileHandles = '\\' === \DIRECTORY_SEPARATOR;
$this->pty = false;
}






















public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, mixed $input = null, ?float $timeout = 60): static
{
$process = new static([], $cwd, $env, $input, $timeout);
$process->commandline = $command;

return $process;
}

public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}

public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}

public function __destruct()
{
if ($this->options['create_new_console'] ?? false) {
$this->processPipes->close();
} else {
$this->stop(0);
}
}

public function __clone()
{
$this->resetProcessData();
}
























public function run(callable $callback = null, array $env = []): int
{
$this->start($callback, $env);

return $this->wait();
}













public function mustRun(callable $callback = null, array $env = []): static
{
if (0 !== $this->run($callback, $env)) {
throw new ProcessFailedException($this);
}

return $this;
}




















public function start(callable $callback = null, array $env = [])
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running.');
}

$this->resetProcessData();
$this->starttime = $this->lastOutputTime = microtime(true);
$this->callback = $this->buildCallback($callback);
$this->hasCallback = null !== $callback;
$descriptors = $this->getDescriptors();

if ($this->env) {
$env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->env, $env, 'strcasecmp') : $this->env;
}

$env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->getDefaultEnv(), $env, 'strcasecmp') : $this->getDefaultEnv();

if (\is_array($commandline = $this->commandline)) {
$commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));

if ('\\' !== \DIRECTORY_SEPARATOR) {

$commandline = 'exec '.$commandline;
}
} else {
$commandline = $this->replacePlaceholders($commandline, $env);
}

if ('\\' === \DIRECTORY_SEPARATOR) {
$commandline = $this->prepareWindowsCommandLine($commandline, $env);
} elseif (!$this->useFileHandles && $this->isSigchildEnabled()) {

$descriptors[3] = ['pipe', 'w'];


$commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
$commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';



$ptsWorkaround = fopen(__FILE__, 'r');
}

$envPairs = [];
foreach ($env as $k => $v) {
if (false !== $v && false === \in_array($k, ['argc', 'argv', 'ARGC', 'ARGV'], true)) {
$envPairs[] = $k.'='.$v;
}
}

if (!is_dir($this->cwd)) {
throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.', $this->cwd));
}

$this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options);

if (!\is_resource($this->process)) {
throw new RuntimeException('Unable to launch a new process.');
}
$this->status = self::STATUS_STARTED;

if (isset($descriptors[3])) {
$this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]);
}

if ($this->tty) {
return;
}

$this->updateStatus(false);
$this->checkTimeout();
}
















public function restart(callable $callback = null, array $env = []): static
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running.');
}

$process = clone $this;
$process->start($callback, $env);

return $process;
}
















public function wait(callable $callback = null): int
{
$this->requireProcessIsStarted(__FUNCTION__);

$this->updateStatus(false);

if (null !== $callback) {
if (!$this->processPipes->haveReadSupport()) {
$this->stop(0);
throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::wait".');
}
$this->callback = $this->buildCallback($callback);
}

do {
$this->checkTimeout();
$running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
$this->readPipes($running, '\\' !== \DIRECTORY_SEPARATOR || !$running);
} while ($running);

while ($this->isRunning()) {
$this->checkTimeout();
usleep(1000);
}

if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
throw new ProcessSignaledException($this);
}

return $this->exitcode;
}












public function waitUntil(callable $callback): bool
{
$this->requireProcessIsStarted(__FUNCTION__);
$this->updateStatus(false);

if (!$this->processPipes->haveReadSupport()) {
$this->stop(0);
throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::waitUntil".');
}
$callback = $this->buildCallback($callback);

$ready = false;
while (true) {
$this->checkTimeout();
$running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
$output = $this->processPipes->readAndWrite($running, '\\' !== \DIRECTORY_SEPARATOR || !$running);

foreach ($output as $type => $data) {
if (3 !== $type) {
$ready = $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data) || $ready;
} elseif (!isset($this->fallbackStatus['signaled'])) {
$this->fallbackStatus['exitcode'] = (int) $data;
}
}
if ($ready) {
return true;
}
if (!$running) {
return false;
}

usleep(1000);
}
}






public function getPid(): ?int
{
return $this->isRunning() ? $this->processInformation['pid'] : null;
}












public function signal(int $signal): static
{
$this->doSignal($signal, true);

return $this;
}









public function disableOutput(): static
{
if ($this->isRunning()) {
throw new RuntimeException('Disabling output while the process is running is not possible.');
}
if (null !== $this->idleTimeout) {
throw new LogicException('Output cannot be disabled while an idle timeout is set.');
}

$this->outputDisabled = true;

return $this;
}








public function enableOutput(): static
{
if ($this->isRunning()) {
throw new RuntimeException('Enabling output while the process is running is not possible.');
}

$this->outputDisabled = false;

return $this;
}




public function isOutputDisabled(): bool
{
return $this->outputDisabled;
}







public function getOutput(): string
{
$this->readPipesForOutput(__FUNCTION__);

if (false === $ret = stream_get_contents($this->stdout, -1, 0)) {
return '';
}

return $ret;
}










public function getIncrementalOutput(): string
{
$this->readPipesForOutput(__FUNCTION__);

$latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
$this->incrementalOutputOffset = ftell($this->stdout);

if (false === $latest) {
return '';
}

return $latest;
}









public function getIterator(int $flags = 0): \Generator
{
$this->readPipesForOutput(__FUNCTION__, false);

$clearOutput = !(self::ITER_KEEP_OUTPUT & $flags);
$blocking = !(self::ITER_NON_BLOCKING & $flags);
$yieldOut = !(self::ITER_SKIP_OUT & $flags);
$yieldErr = !(self::ITER_SKIP_ERR & $flags);

while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) {
if ($yieldOut) {
$out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);

if (isset($out[0])) {
if ($clearOutput) {
$this->clearOutput();
} else {
$this->incrementalOutputOffset = ftell($this->stdout);
}

yield self::OUT => $out;
}
}

if ($yieldErr) {
$err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);

if (isset($err[0])) {
if ($clearOutput) {
$this->clearErrorOutput();
} else {
$this->incrementalErrorOutputOffset = ftell($this->stderr);
}

yield self::ERR => $err;
}
}

if (!$blocking && !isset($out[0]) && !isset($err[0])) {
yield self::OUT => '';
}

$this->checkTimeout();
$this->readPipesForOutput(__FUNCTION__, $blocking);
}
}






public function clearOutput(): static
{
ftruncate($this->stdout, 0);
fseek($this->stdout, 0);
$this->incrementalOutputOffset = 0;

return $this;
}







public function getErrorOutput(): string
{
$this->readPipesForOutput(__FUNCTION__);

if (false === $ret = stream_get_contents($this->stderr, -1, 0)) {
return '';
}

return $ret;
}











public function getIncrementalErrorOutput(): string
{
$this->readPipesForOutput(__FUNCTION__);

$latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
$this->incrementalErrorOutputOffset = ftell($this->stderr);

if (false === $latest) {
return '';
}

return $latest;
}






public function clearErrorOutput(): static
{
ftruncate($this->stderr, 0);
fseek($this->stderr, 0);
$this->incrementalErrorOutputOffset = 0;

return $this;
}






public function getExitCode(): ?int
{
$this->updateStatus(false);

return $this->exitcode;
}












public function getExitCodeText(): ?string
{
if (null === $exitcode = $this->getExitCode()) {
return null;
}

return self::$exitCodes[$exitcode] ?? 'Unknown error';
}




public function isSuccessful(): bool
{
return 0 === $this->getExitCode();
}








public function hasBeenSignaled(): bool
{
$this->requireProcessIsTerminated(__FUNCTION__);

return $this->processInformation['signaled'];
}









public function getTermSignal(): int
{
$this->requireProcessIsTerminated(__FUNCTION__);

if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal cannot be retrieved.');
}

return $this->processInformation['termsig'];
}








public function hasBeenStopped(): bool
{
$this->requireProcessIsTerminated(__FUNCTION__);

return $this->processInformation['stopped'];
}








public function getStopSignal(): int
{
$this->requireProcessIsTerminated(__FUNCTION__);

return $this->processInformation['stopsig'];
}




public function isRunning(): bool
{
if (self::STATUS_STARTED !== $this->status) {
return false;
}

$this->updateStatus(false);

return $this->processInformation['running'];
}




public function isStarted(): bool
{
return self::STATUS_READY != $this->status;
}




public function isTerminated(): bool
{
$this->updateStatus(false);

return self::STATUS_TERMINATED == $this->status;
}






public function getStatus(): string
{
$this->updateStatus(false);

return $this->status;
}









public function stop(float $timeout = 10, int $signal = null): ?int
{
$timeoutMicro = microtime(true) + $timeout;
if ($this->isRunning()) {

$this->doSignal(15, false);
do {
usleep(1000);
} while ($this->isRunning() && microtime(true) < $timeoutMicro);

if ($this->isRunning()) {


$this->doSignal($signal ?: 9, false);
}
}

if ($this->isRunning()) {
if (isset($this->fallbackStatus['pid'])) {
unset($this->fallbackStatus['pid']);

return $this->stop(0, $signal);
}
$this->close();
}

return $this->exitcode;
}






public function addOutput(string $line)
{
$this->lastOutputTime = microtime(true);

fseek($this->stdout, 0, \SEEK_END);
fwrite($this->stdout, $line);
fseek($this->stdout, $this->incrementalOutputOffset);
}






public function addErrorOutput(string $line)
{
$this->lastOutputTime = microtime(true);

fseek($this->stderr, 0, \SEEK_END);
fwrite($this->stderr, $line);
fseek($this->stderr, $this->incrementalErrorOutputOffset);
}




public function getLastOutputTime(): ?float
{
return $this->lastOutputTime;
}




public function getCommandLine(): string
{
return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline;
}




public function getTimeout(): ?float
{
return $this->timeout;
}




public function getIdleTimeout(): ?float
{
return $this->idleTimeout;
}










public function setTimeout(?float $timeout): static
{
$this->timeout = $this->validateTimeout($timeout);

return $this;
}











public function setIdleTimeout(?float $timeout): static
{
if (null !== $timeout && $this->outputDisabled) {
throw new LogicException('Idle timeout cannot be set while the output is disabled.');
}

$this->idleTimeout = $this->validateTimeout($timeout);

return $this;
}








public function setTty(bool $tty): static
{
if ('\\' === \DIRECTORY_SEPARATOR && $tty) {
throw new RuntimeException('TTY mode is not supported on Windows platform.');
}

if ($tty && !self::isTtySupported()) {
throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.');
}

$this->tty = $tty;

return $this;
}




public function isTty(): bool
{
return $this->tty;
}






public function setPty(bool $bool): static
{
$this->pty = $bool;

return $this;
}




public function isPty(): bool
{
return $this->pty;
}




public function getWorkingDirectory(): ?string
{
if (null === $this->cwd) {


return getcwd() ?: null;
}

return $this->cwd;
}






public function setWorkingDirectory(string $cwd): static
{
$this->cwd = $cwd;

return $this;
}




public function getEnv(): array
{
return $this->env;
}








public function setEnv(array $env): static
{
$this->env = $env;

return $this;
}






public function getInput()
{
return $this->input;
}












public function setInput(mixed $input): static
{
if ($this->isRunning()) {
throw new LogicException('Input cannot be set while the process is running.');
}

$this->input = ProcessUtils::validateInput(__METHOD__, $input);

return $this;
}









public function checkTimeout()
{
if (self::STATUS_STARTED !== $this->status) {
return;
}

if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
$this->stop(0);

throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL);
}

if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
$this->stop(0);

throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE);
}
}




public function getStartTime(): float
{
if (!$this->isStarted()) {
throw new LogicException('Start time is only available after process start.');
}

return $this->starttime;
}









public function setOptions(array $options)
{
if ($this->isRunning()) {
throw new RuntimeException('Setting options while the process is running is not possible.');
}

$defaultOptions = $this->options;
$existingOptions = ['blocking_pipes', 'create_process_group', 'create_new_console'];

foreach ($options as $key => $value) {
if (!\in_array($key, $existingOptions)) {
$this->options = $defaultOptions;
throw new LogicException(sprintf('Invalid option "%s" passed to "%s()". Supported options are "%s".', $key, __METHOD__, implode('", "', $existingOptions)));
}
$this->options[$key] = $value;
}
}




public static function isTtySupported(): bool
{
static $isTtySupported;

if (null === $isTtySupported) {
$isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
}

return $isTtySupported;
}




public static function isPtySupported(): bool
{
static $result;

if (null !== $result) {
return $result;
}

if ('\\' === \DIRECTORY_SEPARATOR) {
return $result = false;
}

return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes);
}




private function getDescriptors(): array
{
if ($this->input instanceof \Iterator) {
$this->input->rewind();
}
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->processPipes = new WindowsPipes($this->input, !$this->outputDisabled || $this->hasCallback);
} else {
$this->processPipes = new UnixPipes($this->isTty(), $this->isPty(), $this->input, !$this->outputDisabled || $this->hasCallback);
}

return $this->processPipes->getDescriptors();
}









protected function buildCallback(callable $callback = null): \Closure
{
if ($this->outputDisabled) {
return function ($type, $data) use ($callback): bool {
return null !== $callback && $callback($type, $data);
};
}

$out = self::OUT;

return function ($type, $data) use ($callback, $out): bool {
if ($out == $type) {
$this->addOutput($data);
} else {
$this->addErrorOutput($data);
}

return null !== $callback && $callback($type, $data);
};
}






protected function updateStatus(bool $blocking)
{
if (self::STATUS_STARTED !== $this->status) {
return;
}

$this->processInformation = proc_get_status($this->process);
$running = $this->processInformation['running'];

$this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running);

if ($this->fallbackStatus && $this->isSigchildEnabled()) {
$this->processInformation = $this->fallbackStatus + $this->processInformation;
}

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




protected function isSigchildEnabled(): bool
{
if (null !== self::$sigchild) {
return self::$sigchild;
}

if (!\function_exists('phpinfo')) {
return self::$sigchild = false;
}

ob_start();
phpinfo(\INFO_GENERAL);

return self::$sigchild = str_contains(ob_get_clean(), '--enable-sigchild');
}









private function readPipesForOutput(string $caller, bool $blocking = false)
{
if ($this->outputDisabled) {
throw new LogicException('Output has been disabled.');
}

$this->requireProcessIsStarted($caller);

$this->updateStatus($blocking);
}






private function validateTimeout(?float $timeout): ?float
{
$timeout = (float) $timeout;

if (0.0 === $timeout) {
$timeout = null;
} elseif ($timeout < 0) {
throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
}

return $timeout;
}







private function readPipes(bool $blocking, bool $close)
{
$result = $this->processPipes->readAndWrite($blocking, $close);

$callback = $this->callback;
foreach ($result as $type => $data) {
if (3 !== $type) {
$callback(self::STDOUT === $type ? self::OUT : self::ERR, $data);
} elseif (!isset($this->fallbackStatus['signaled'])) {
$this->fallbackStatus['exitcode'] = (int) $data;
}
}
}






private function close(): int
{
$this->processPipes->close();
if (\is_resource($this->process)) {
proc_close($this->process);
}
$this->exitcode = $this->processInformation['exitcode'];
$this->status = self::STATUS_TERMINATED;

if (-1 === $this->exitcode) {
if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {

$this->exitcode = 128 + $this->processInformation['termsig'];
} elseif ($this->isSigchildEnabled()) {
$this->processInformation['signaled'] = true;
$this->processInformation['termsig'] = -1;
}
}




$this->callback = null;

return $this->exitcode;
}




private function resetProcessData()
{
$this->starttime = null;
$this->callback = null;
$this->exitcode = null;
$this->fallbackStatus = [];
$this->processInformation = null;
$this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+');
$this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+');
$this->process = null;
$this->latestSignal = null;
$this->status = self::STATUS_READY;
$this->incrementalOutputOffset = 0;
$this->incrementalErrorOutputOffset = 0;
}











private function doSignal(int $signal, bool $throwException): bool
{
if (null === $pid = $this->getPid()) {
if ($throwException) {
throw new LogicException('Cannot send signal on a non running process.');
}

return false;
}

if ('\\' === \DIRECTORY_SEPARATOR) {
exec(sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode);
if ($exitCode && $this->isRunning()) {
if ($throwException) {
throw new RuntimeException(sprintf('Unable to kill the process (%s).', implode(' ', $output)));
}

return false;
}
} else {
if (!$this->isSigchildEnabled()) {
$ok = @proc_terminate($this->process, $signal);
} elseif (\function_exists('posix_kill')) {
$ok = @posix_kill($pid, $signal);
} elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
$ok = false === fgets($pipes[2]);
}
if (!$ok) {
if ($throwException) {
throw new RuntimeException(sprintf('Error while sending signal "%s".', $signal));
}

return false;
}
}

$this->latestSignal = $signal;
$this->fallbackStatus['signaled'] = true;
$this->fallbackStatus['exitcode'] = -1;
$this->fallbackStatus['termsig'] = $this->latestSignal;

return true;
}

private function prepareWindowsCommandLine(string $cmd, array &$env): string
{
$uid = uniqid('', true);
$varCount = 0;
$varCache = [];
$cmd = preg_replace_callback(
'/"(?:(
                [^"%!^]*+
                (?:
                    (?: !LF! | "(?:\^[%!^])?+" )
                    [^"%!^]*+
                )++
            ) | [^"]*+ )"/x',
function ($m) use (&$env, &$varCache, &$varCount, $uid) {
if (!isset($m[1])) {
return $m[0];
}
if (isset($varCache[$m[0]])) {
return $varCache[$m[0]];
}
if (str_contains($value = $m[1], "\0")) {
$value = str_replace("\0", '?', $value);
}
if (false === strpbrk($value, "\"%!\n")) {
return '"'.$value.'"';
}

$value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value);
$value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"';
$var = $uid.++$varCount;

$env[$var] = $value;

return $varCache[$m[0]] = '!'.$var.'!';
},
$cmd
);

$cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')';
foreach ($this->processPipes->getFiles() as $offset => $filename) {
$cmd .= ' '.$offset.'>"'.$filename.'"';
}

return $cmd;
}






private function requireProcessIsStarted(string $functionName)
{
if (!$this->isStarted()) {
throw new LogicException(sprintf('Process must be started before calling "%s()".', $functionName));
}
}






private function requireProcessIsTerminated(string $functionName)
{
if (!$this->isTerminated()) {
throw new LogicException(sprintf('Process must be terminated before calling "%s()".', $functionName));
}
}




private function escapeArgument(?string $argument): string
{
if ('' === $argument || null === $argument) {
return '""';
}
if ('\\' !== \DIRECTORY_SEPARATOR) {
return "'".str_replace("'", "'\\''", $argument)."'";
}
if (str_contains($argument, "\0")) {
$argument = str_replace("\0", '?', $argument);
}
if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) {
return $argument;
}
$argument = preg_replace('/(\\\\+)$/', '$1$1', $argument);

return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
}

private function replacePlaceholders(string $commandline, array $env)
{
return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) {
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": ', $matches[1]).$commandline);
}

return $this->escapeArgument($env[$matches[1]]);
}, $commandline);
}

private function getDefaultEnv(): array
{
$env = getenv();
$env = ('\\' === \DIRECTORY_SEPARATOR ? array_intersect_ukey($env, $_SERVER, 'strcasecmp') : array_intersect_key($env, $_SERVER)) ?: $env;

return $_ENV + ('\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($env, $_ENV, 'strcasecmp') : $env);
}
}
<?php










namespace Symfony\Component\Process\Exception;

use Symfony\Component\Process\Process;






final class ProcessSignaledException extends RuntimeException
{
private $process;

public function __construct(Process $process)
{
$this->process = $process;

parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal()));
}

public function getProcess(): Process
{
return $this->process;
}

public function getSignal(): int
{
return $this->getProcess()->getTermSignal();
}
}
<?php










namespace Symfony\Component\Process\Exception;






class LogicException extends \LogicException implements ExceptionInterface
{
}
<?php










namespace Symfony\Component\Process\Exception;






interface ExceptionInterface extends \Throwable
{
}
<?php










namespace Symfony\Component\Process\Exception;

use Symfony\Component\Process\Process;






class ProcessFailedException extends RuntimeException
{
private $process;

public function __construct(Process $process)
{
if ($process->isSuccessful()) {
throw new InvalidArgumentException('Expected a failed process, but the given process was successful.');
}

$error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s",
$process->getCommandLine(),
$process->getExitCode(),
$process->getExitCodeText(),
$process->getWorkingDirectory()
);

if (!$process->isOutputDisabled()) {
$error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s",
$process->getOutput(),
$process->getErrorOutput()
);
}

parent::__construct($error);

$this->process = $process;
}

public function getProcess()
{
return $this->process;
}
}
<?php










namespace Symfony\Component\Process\Exception;






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










namespace Symfony\Component\Process\Exception;






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










namespace Symfony\Component\Process\Exception;

use Symfony\Component\Process\Process;






class ProcessTimedOutException extends RuntimeException
{
public const TYPE_GENERAL = 1;
public const TYPE_IDLE = 2;

private $process;
private $timeoutType;

public function __construct(Process $process, int $timeoutType)
{
$this->process = $process;
$this->timeoutType = $timeoutType;

parent::__construct(sprintf(
'The process "%s" exceeded the timeout of %s seconds.',
$process->getCommandLine(),
$this->getExceededTimeout()
));
}

public function getProcess()
{
return $this->process;
}

public function isGeneralTimeout()
{
return self::TYPE_GENERAL === $this->timeoutType;
}

public function isIdleTimeout()
{
return self::TYPE_IDLE === $this->timeoutType;
}

public function getExceededTimeout()
{
switch ($this->timeoutType) {
case self::TYPE_GENERAL:
return $this->process->getTimeout();

case self::TYPE_IDLE:
return $this->process->getIdleTimeout();

default:
throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
}
}
}
<?php










namespace Symfony\Component\Finder;























class Glob
{



public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#'): string
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = \strlen($glob);
for ($i = 0; $i < $sizeGlob; ++$i) {
$car = $glob[$i];
if ($firstByte && $strictLeadingDot && '.' !== $car) {
$regex .= '(?=[^\.])';
}

$firstByte = '/' === $car;

if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) {
$car = '[^/]++/';
if (!isset($glob[$i + 3])) {
$car .= '?';
}

if ($strictLeadingDot) {
$car = '(?=[^\.])'.$car;
}

$car = '/(?:'.$car.')*';
$i += 2 + isset($glob[$i + 3]);

if ('/' === $delimiter) {
$car = str_replace('/', '\\/', $car);
}
}

if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
$regex .= $escaping ? '\\{' : '(';
if (!$escaping) {
++$inCurlies;
}
} elseif ('}' === $car && $inCurlies) {
$regex .= $escaping ? '}' : ')';
if (!$escaping) {
--$inCurlies;
}
} elseif (',' === $car && $inCurlies) {
$regex .= $escaping ? ',' : '|';
} elseif ('\\' === $car) {
if ($escaping) {
$regex .= '\\\\';
$escaping = false;
} else {
$escaping = true;
}

continue;
} else {
$regex .= $car;
}
$escaping = false;
}

return $delimiter.'^'.$regex.'$'.$delimiter;
}
}
<?php










namespace Symfony\Component\Finder\Comparator;






class DateComparator extends Comparator
{





public function __construct(string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
}

try {
$date = new \DateTime($matches[2]);
$target = $date->format('U');
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
}

$operator = $matches[1] ?? '==';
if ('since' === $operator || 'after' === $operator) {
$operator = '>';
}

if ('until' === $operator || 'before' === $operator) {
$operator = '<';
}

parent::__construct($target, $operator);
}
}
<?php










namespace Symfony\Component\Finder\Comparator;




class Comparator
{
private string $target;
private string $operator;

public function __construct(string $target, string $operator = '==')
{
if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}

$this->target = $target;
$this->operator = $operator;
}




public function getTarget(): string
{
return $this->target;
}




public function getOperator(): string
{
return $this->operator;
}




public function test(mixed $test): bool
{
switch ($this->operator) {
case '>':
return $test > $this->target;
case '>=':
return $test >= $this->target;
case '<':
return $test < $this->target;
case '<=':
return $test <= $this->target;
case '!=':
return $test != $this->target;
}

return $test == $this->target;
}
}
<?php










namespace Symfony\Component\Finder\Comparator;






















class NumberComparator extends Comparator
{





public function __construct(?string $test)
{
if (null === $test || !preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test ?? 'null'));
}

$target = $matches[2];
if (!is_numeric($target)) {
throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
}
if (isset($matches[3])) {

switch (strtolower($matches[3])) {
case 'k':
$target *= 1000;
break;
case 'ki':
$target *= 1024;
break;
case 'm':
$target *= 1000000;
break;
case 'mi':
$target *= 1024 * 1024;
break;
case 'g':
$target *= 1000000000;
break;
case 'gi':
$target *= 1024 * 1024 * 1024;
break;
}
}

parent::__construct($target, $matches[1] ?: '==');
}
}
<?php










namespace Symfony\Component\Finder;

use Symfony\Component\Finder\Comparator\DateComparator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Iterator\CustomFilterIterator;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
use Symfony\Component\Finder\Iterator\LazyIterator;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Component\Finder\Iterator\SortableIterator;
















class Finder implements \IteratorAggregate, \Countable
{
public const IGNORE_VCS_FILES = 1;
public const IGNORE_DOT_FILES = 2;
public const IGNORE_VCS_IGNORED_FILES = 4;

private int $mode = 0;
private array $names = [];
private array $notNames = [];
private array $exclude = [];
private array $filters = [];
private array $depths = [];
private array $sizes = [];
private bool $followLinks = false;
private bool $reverseSorting = false;
private \Closure|int|false $sort = false;
private int $ignore = 0;
private array $dirs = [];
private array $dates = [];
private array $iterators = [];
private array $contains = [];
private array $notContains = [];
private array $paths = [];
private array $notPaths = [];
private bool $ignoreUnreadableDirs = false;

private static array $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];

public function __construct()
{
$this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
}




public static function create(): static
{
return new static();
}






public function directories(): static
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;

return $this;
}






public function files(): static
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;

return $this;
}

















public function depth(string|int|array $levels): static
{
foreach ((array) $levels as $level) {
$this->depths[] = new Comparator\NumberComparator($level);
}

return $this;
}




















public function date(string|array $dates): static
{
foreach ((array) $dates as $date) {
$this->dates[] = new Comparator\DateComparator($date);
}

return $this;
}

















public function name(string|array $patterns): static
{
$this->names = array_merge($this->names, (array) $patterns);

return $this;
}










public function notName(string|array $patterns): static
{
$this->notNames = array_merge($this->notNames, (array) $patterns);

return $this;
}
















public function contains(string|array $patterns): static
{
$this->contains = array_merge($this->contains, (array) $patterns);

return $this;
}
















public function notContains(string|array $patterns): static
{
$this->notContains = array_merge($this->notContains, (array) $patterns);

return $this;
}


















public function path(string|array $patterns): static
{
$this->paths = array_merge($this->paths, (array) $patterns);

return $this;
}


















public function notPath(string|array $patterns): static
{
$this->notPaths = array_merge($this->notPaths, (array) $patterns);

return $this;
}
















public function size(string|int|array $sizes): static
{
foreach ((array) $sizes as $size) {
$this->sizes[] = new Comparator\NumberComparator($size);
}

return $this;
}














public function exclude(string|array $dirs): static
{
$this->exclude = array_merge($this->exclude, (array) $dirs);

return $this;
}










public function ignoreDotFiles(bool $ignoreDotFiles): static
{
if ($ignoreDotFiles) {
$this->ignore |= static::IGNORE_DOT_FILES;
} else {
$this->ignore &= ~static::IGNORE_DOT_FILES;
}

return $this;
}










public function ignoreVCS(bool $ignoreVCS): static
{
if ($ignoreVCS) {
$this->ignore |= static::IGNORE_VCS_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_FILES;
}

return $this;
}








public function ignoreVCSIgnored(bool $ignoreVCSIgnored): static
{
if ($ignoreVCSIgnored) {
$this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;
}

return $this;
}








public static function addVCSPattern(string|array $pattern)
{
foreach ((array) $pattern as $p) {
self::$vcsPatterns[] = $p;
}

self::$vcsPatterns = array_unique(self::$vcsPatterns);
}












public function sort(\Closure $closure): static
{
$this->sort = $closure;

return $this;
}










public function sortByName(bool $useNaturalSort = false): static
{
$this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;

return $this;
}










public function sortByType(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;

return $this;
}












public function sortByAccessedTime(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;

return $this;
}






public function reverseSorting(): static
{
$this->reverseSorting = true;

return $this;
}














public function sortByChangedTime(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;

return $this;
}












public function sortByModifiedTime(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;

return $this;
}











public function filter(\Closure $closure): static
{
$this->filters[] = $closure;

return $this;
}






public function followLinks(): static
{
$this->followLinks = true;

return $this;
}








public function ignoreUnreadableDirs(bool $ignore = true): static
{
$this->ignoreUnreadableDirs = $ignore;

return $this;
}










public function in(string|array $dirs): static
{
$resolvedDirs = [];

foreach ((array) $dirs as $dir) {
if (is_dir($dir)) {
$resolvedDirs[] = [$this->normalizeDir($dir)];
} elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) {
sort($glob);
$resolvedDirs[] = array_map([$this, 'normalizeDir'], $glob);
} else {
throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir));
}
}

$this->dirs = array_merge($this->dirs, ...$resolvedDirs);

return $this;
}










public function getIterator(): \Iterator
{
if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
}

if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
$iterator = $this->searchInDirectory($this->dirs[0]);

if ($this->sort || $this->reverseSorting) {
$iterator = (new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();
}

return $iterator;
}

$iterator = new \AppendIterator();
foreach ($this->dirs as $dir) {
$iterator->append(new \IteratorIterator(new LazyIterator(function () use ($dir) {
return $this->searchInDirectory($dir);
})));
}

foreach ($this->iterators as $it) {
$iterator->append($it);
}

if ($this->sort || $this->reverseSorting) {
$iterator = (new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();
}

return $iterator;
}










public function append(iterable $iterator): static
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
} elseif ($iterator instanceof \Iterator) {
$this->iterators[] = $iterator;
} elseif (is_iterable($iterator)) {
$it = new \ArrayIterator();
foreach ($iterator as $file) {
$file = $file instanceof \SplFileInfo ? $file : new \SplFileInfo($file);
$it[$file->getPathname()] = $file;
}
$this->iterators[] = $it;
} else {
throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
}

return $this;
}




public function hasResults(): bool
{
foreach ($this->getIterator() as $_) {
return true;
}

return false;
}




public function count(): int
{
return iterator_count($this->getIterator());
}

private function searchInDirectory(string $dir): \Iterator
{
$exclude = $this->exclude;
$notPaths = $this->notPaths;

if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$exclude = array_merge($exclude, self::$vcsPatterns);
}

if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$notPaths[] = '#(^|/)\..+(/|$)#';
}

$minDepth = 0;
$maxDepth = \PHP_INT_MAX;

foreach ($this->depths as $comparator) {
switch ($comparator->getOperator()) {
case '>':
$minDepth = $comparator->getTarget() + 1;
break;
case '>=':
$minDepth = $comparator->getTarget();
break;
case '<':
$maxDepth = $comparator->getTarget() - 1;
break;
case '<=':
$maxDepth = $comparator->getTarget();
break;
default:
$minDepth = $maxDepth = $comparator->getTarget();
}
}

$flags = \RecursiveDirectoryIterator::SKIP_DOTS;

if ($this->followLinks) {
$flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
}

$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);

if ($exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
}

$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);

if ($minDepth > 0 || $maxDepth < \PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
}

if ($this->mode) {
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}

if ($this->names || $this->notNames) {
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}

if ($this->contains || $this->notContains) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}

if ($this->sizes) {
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}

if ($this->dates) {
$iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
}

if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}

if ($this->paths || $notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
}

if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {
$iterator = new Iterator\VcsIgnoredFilterIterator($iterator, $dir);
}

return $iterator;
}






private function normalizeDir(string $dir): string
{
if ('/' === $dir) {
return $dir;
}

$dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);

if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) {
$dir .= '/';
}

return $dir;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;











class DepthRangeFilterIterator extends \FilterIterator
{
private int $minDepth = 0;






public function __construct(\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = \PHP_INT_MAX)
{
$this->minDepth = $minDepth;
$iterator->setMaxDepth(\PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);

parent::__construct($iterator);
}




public function accept(): bool
{
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;

use Symfony\Component\Finder\Comparator\DateComparator;








class DateRangeFilterIterator extends \FilterIterator
{
private array $comparators = [];





public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;

parent::__construct($iterator);
}




public function accept(): bool
{
$fileinfo = $this->current();

if (!file_exists($fileinfo->getPathname())) {
return false;
}

$filedate = $fileinfo->getMTime();
foreach ($this->comparators as $compare) {
if (!$compare->test($filedate)) {
return false;
}
}

return true;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;











abstract class MultiplePcreFilterIterator extends \FilterIterator
{
protected $matchRegexps = [];
protected $noMatchRegexps = [];






public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
foreach ($matchPatterns as $pattern) {
$this->matchRegexps[] = $this->toRegex($pattern);
}

foreach ($noMatchPatterns as $pattern) {
$this->noMatchRegexps[] = $this->toRegex($pattern);
}

parent::__construct($iterator);
}








protected function isAccepted(string $string): bool
{

foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $string)) {
return false;
}
}


if ($this->matchRegexps) {
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $string)) {
return true;
}
}

return false;
}


return true;
}




protected function isRegex(string $str): bool
{
$availableModifiers = 'imsxuADU';

if (\PHP_VERSION_ID >= 80200) {
$availableModifiers .= 'n';
}

if (preg_match('/^(.{3,}?)['.$availableModifiers.']*$/', $str, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);

if ($start === $end) {
return !preg_match('/[*?[:alnum:] \\\\]/', $start);
}

foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) {
if ($start === $delimiters[0] && $end === $delimiters[1]) {
return true;
}
}
}

return false;
}




abstract protected function toRegex(string $str): string;
}
<?php










namespace Symfony\Component\Finder\Iterator;

use Symfony\Component\Finder\Gitignore;

final class VcsIgnoredFilterIterator extends \FilterIterator
{



private $baseDir;




private $gitignoreFilesCache = [];




private $ignoredPathsCache = [];

public function __construct(\Iterator $iterator, string $baseDir)
{
$this->baseDir = $this->normalizePath($baseDir);

parent::__construct($iterator);
}

public function accept(): bool
{
$file = $this->current();

$fileRealPath = $this->normalizePath($file->getRealPath());

return !$this->isIgnored($fileRealPath);
}

private function isIgnored(string $fileRealPath): bool
{
if (is_dir($fileRealPath) && !str_ends_with($fileRealPath, '/')) {
$fileRealPath .= '/';
}

if (isset($this->ignoredPathsCache[$fileRealPath])) {
return $this->ignoredPathsCache[$fileRealPath];
}

$ignored = false;

foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) {
if ($this->isIgnored($parentDirectory)) {

break;
}

$fileRelativePath = substr($fileRealPath, \strlen($parentDirectory) + 1);

if (null === $regexps = $this->readGitignoreFile("{$parentDirectory}/.gitignore")) {
continue;
}

[$exclusionRegex, $inclusionRegex] = $regexps;

if (preg_match($exclusionRegex, $fileRelativePath)) {
$ignored = true;

continue;
}

if (preg_match($inclusionRegex, $fileRelativePath)) {
$ignored = false;
}
}

return $this->ignoredPathsCache[$fileRealPath] = $ignored;
}




private function parentsDirectoryDownward(string $fileRealPath): array
{
$parentDirectories = [];

$parentDirectory = $fileRealPath;

while (true) {
$newParentDirectory = \dirname($parentDirectory);


if ($newParentDirectory === $parentDirectory) {
break;
}

$parentDirectory = $newParentDirectory;

if (0 !== strpos($parentDirectory, $this->baseDir)) {
break;
}

$parentDirectories[] = $parentDirectory;
}

return array_reverse($parentDirectories);
}




private function readGitignoreFile(string $path): ?array
{
if (\array_key_exists($path, $this->gitignoreFilesCache)) {
return $this->gitignoreFilesCache[$path];
}

if (!file_exists($path)) {
return $this->gitignoreFilesCache[$path] = null;
}

if (!is_file($path) || !is_readable($path)) {
throw new \RuntimeException("The \"ignoreVCSIgnored\" option cannot be used by the Finder as the \"{$path}\" file is not readable.");
}

$gitignoreFileContent = file_get_contents($path);

return $this->gitignoreFilesCache[$path] = [
Gitignore::toRegex($gitignoreFileContent),
Gitignore::toRegexMatchingNegatedPatterns($gitignoreFileContent),
];
}

private function normalizePath(string $path): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
return str_replace('\\', '/', $path);
}

return $path;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;









class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator
{
private \Iterator $iterator;
private bool $isRecursive;
private array $excludedDirs = [];
private ?string $excludedPattern = null;





public function __construct(\Iterator $iterator, array $directories)
{
$this->iterator = $iterator;
$this->isRecursive = $iterator instanceof \RecursiveIterator;
$patterns = [];
foreach ($directories as $directory) {
$directory = rtrim($directory, '/');
if (!$this->isRecursive || str_contains($directory, '/')) {
$patterns[] = preg_quote($directory, '#');
} else {
$this->excludedDirs[$directory] = true;
}
}
if ($patterns) {
$this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
}

parent::__construct($iterator);
}




public function accept(): bool
{
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
return false;
}

if ($this->excludedPattern) {
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
$path = str_replace('\\', '/', $path);

return !preg_match($this->excludedPattern, $path);
}

return true;
}

public function hasChildren(): bool
{
return $this->isRecursive && $this->iterator->hasChildren();
}

public function getChildren(): self
{
$children = new self($this->iterator->getChildren(), []);
$children->excludedDirs = $this->excludedDirs;
$children->excludedPattern = $this->excludedPattern;

return $children;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;








class FileTypeFilterIterator extends \FilterIterator
{
public const ONLY_FILES = 1;
public const ONLY_DIRECTORIES = 2;

private int $mode;





public function __construct(\Iterator $iterator, int $mode)
{
$this->mode = $mode;

parent::__construct($iterator);
}




public function accept(): bool
{
$fileinfo = $this->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
return false;
} elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
return false;
}

return true;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;








class SortableIterator implements \IteratorAggregate
{
public const SORT_BY_NONE = 0;
public const SORT_BY_NAME = 1;
public const SORT_BY_TYPE = 2;
public const SORT_BY_ACCESSED_TIME = 3;
public const SORT_BY_CHANGED_TIME = 4;
public const SORT_BY_MODIFIED_TIME = 5;
public const SORT_BY_NAME_NATURAL = 6;

private \Traversable $iterator;
private \Closure|int $sort;







public function __construct(\Traversable $iterator, int|callable $sort, bool $reverseOrder = false)
{
$this->iterator = $iterator;
$order = $reverseOrder ? -1 : 1;

if (self::SORT_BY_NAME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_NAME_NATURAL === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_TYPE === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
if ($a->isDir() && $b->isFile()) {
return -$order;
} elseif ($a->isFile() && $b->isDir()) {
return $order;
}

return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getATime() - $b->getATime());
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getCTime() - $b->getCTime());
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getMTime() - $b->getMTime());
};
} elseif (self::SORT_BY_NONE === $sort) {
$this->sort = $order;
} elseif (\is_callable($sort)) {
$this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : \Closure::fromCallable($sort);
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
}
}

public function getIterator(): \Traversable
{
if (1 === $this->sort) {
return $this->iterator;
}

$array = iterator_to_array($this->iterator, true);

if (-1 === $this->sort) {
$array = array_reverse($array);
} else {
uasort($array, $this->sort);
}

return new \ArrayIterator($array);
}
}
<?php










namespace Symfony\Component\Finder\Iterator;











class CustomFilterIterator extends \FilterIterator
{
private array $filters = [];







public function __construct(\Iterator $iterator, array $filters)
{
foreach ($filters as $filter) {
if (!\is_callable($filter)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
}
$this->filters = $filters;

parent::__construct($iterator);
}




public function accept(): bool
{
$fileinfo = $this->current();

foreach ($this->filters as $filter) {
if (false === $filter($fileinfo)) {
return false;
}
}

return true;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;

use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\SplFileInfo;






class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
private bool $ignoreUnreadableDirs;
private ?bool $rewindable = null;


private string $rootPath;
private string $subPath;
private string $directorySeparator = '/';




public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
}

parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
$this->rootPath = $path;
if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
$this->directorySeparator = \DIRECTORY_SEPARATOR;
}
}




public function current(): SplFileInfo
{


if (!isset($this->subPath)) {
$this->subPath = $this->getSubPath();
}
$subPathname = $this->subPath;
if ('' !== $subPathname) {
$subPathname .= $this->directorySeparator;
}
$subPathname .= $this->getFilename();

if ('/' !== $basePath = $this->rootPath) {
$basePath .= $this->directorySeparator;
}

return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname);
}

public function hasChildren(bool $allowLinks = false): bool
{
$hasChildren = parent::hasChildren($allowLinks);

if (!$hasChildren || !$this->ignoreUnreadableDirs) {
return $hasChildren;
}

try {
parent::getChildren();

return true;
} catch (\UnexpectedValueException $e) {

return false;
}
}




public function getChildren(): \RecursiveDirectoryIterator
{
try {
$children = parent::getChildren();

if ($children instanceof self) {

$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;


$children->rewindable = &$this->rewindable;
$children->rootPath = $this->rootPath;
}

return $children;
} catch (\UnexpectedValueException $e) {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}




public function rewind(): void
{
if (false === $this->isRewindable()) {
return;
}

parent::rewind();
}




public function isRewindable(): bool
{
if (null !== $this->rewindable) {
return $this->rewindable;
}

if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);

if ($infos['seekable']) {
return $this->rewindable = true;
}
}

return $this->rewindable = false;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;









class FilecontentFilterIterator extends MultiplePcreFilterIterator
{



public function accept(): bool
{
if (!$this->matchRegexps && !$this->noMatchRegexps) {
return true;
}

$fileinfo = $this->current();

if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
return false;
}

$content = $fileinfo->getContents();
if (!$content) {
return false;
}

return $this->isAccepted($content);
}






protected function toRegex(string $str): string
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?php










namespace Symfony\Component\Finder\Iterator;






class LazyIterator implements \IteratorAggregate
{
private \Closure $iteratorFactory;

public function __construct(callable $iteratorFactory)
{
$this->iteratorFactory = $iteratorFactory instanceof \Closure ? $iteratorFactory : \Closure::fromCallable($iteratorFactory);
}

public function getIterator(): \Traversable
{
yield from ($this->iteratorFactory)();
}
}
<?php










namespace Symfony\Component\Finder\Iterator;

use Symfony\Component\Finder\Comparator\NumberComparator;








class SizeRangeFilterIterator extends \FilterIterator
{
private array $comparators = [];





public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;

parent::__construct($iterator);
}




public function accept(): bool
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {
return true;
}

$filesize = $fileinfo->getSize();
foreach ($this->comparators as $compare) {
if (!$compare->test($filesize)) {
return false;
}
}

return true;
}
}
<?php










namespace Symfony\Component\Finder\Iterator;

use Symfony\Component\Finder\Glob;








class FilenameFilterIterator extends MultiplePcreFilterIterator
{



public function accept(): bool
{
return $this->isAccepted($this->current()->getFilename());
}









protected function toRegex(string $str): string
{
return $this->isRegex($str) ? $str : Glob::toRegex($str);
}
}
<?php










namespace Symfony\Component\Finder\Iterator;









class PathFilterIterator extends MultiplePcreFilterIterator
{



public function accept(): bool
{
$filename = $this->current()->getRelativePathname();

if ('\\' === \DIRECTORY_SEPARATOR) {
$filename = str_replace('\\', '/', $filename);
}

return $this->isAccepted($filename);
}













protected function toRegex(string $str): string
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?php










namespace Symfony\Component\Finder;







class Gitignore
{





public static function toRegex(string $gitignoreFileContent): string
{
return self::buildRegex($gitignoreFileContent, false);
}

public static function toRegexMatchingNegatedPatterns(string $gitignoreFileContent): string
{
return self::buildRegex($gitignoreFileContent, true);
}

private static function buildRegex(string $gitignoreFileContent, bool $inverted): string
{
$gitignoreFileContent = preg_replace('~(?<!\\\\)#[^\n\r]*~', '', $gitignoreFileContent);
$gitignoreLines = preg_split('~\r\n?|\n~', $gitignoreFileContent);

$res = self::lineToRegex('');
foreach ($gitignoreLines as $line) {
$line = preg_replace('~(?<!\\\\)[ \t]+$~', '', $line);

if ('!' === substr($line, 0, 1)) {
$line = substr($line, 1);
$isNegative = true;
} else {
$isNegative = false;
}

if ('' !== $line) {
if ($isNegative xor $inverted) {
$res = '(?!'.self::lineToRegex($line).'$)'.$res;
} else {
$res = '(?:'.$res.'|'.self::lineToRegex($line).')';
}
}
}

return '~^(?:'.$res.')~s';
}

private static function lineToRegex(string $gitignoreLine): string
{
if ('' === $gitignoreLine) {
return '$f'; 
}

$slashPos = strpos($gitignoreLine, '/');
if (false !== $slashPos && \strlen($gitignoreLine) - 1 !== $slashPos) {
if (0 === $slashPos) {
$gitignoreLine = substr($gitignoreLine, 1);
}
$isAbsolute = true;
} else {
$isAbsolute = false;
}

$regex = preg_quote(str_replace('\\', '', $gitignoreLine), '~');
$regex = preg_replace_callback('~\\\\\[((?:\\\\!)?)([^\[\]]*)\\\\\]~', function (array $matches): string {
return '['.('' !== $matches[1] ? '^' : '').str_replace('\\-', '-', $matches[2]).']';
}, $regex);
$regex = preg_replace('~(?:(?:\\\\\*){2,}(/?))+~', '(?:(?:(?!//).(?<!//))+$1)?', $regex);
$regex = preg_replace('~\\\\\*~', '[^/]*', $regex);
$regex = preg_replace('~\\\\\?~', '[^/]', $regex);

return ($isAbsolute ? '' : '(?:[^/]+/)*')
.$regex
.(!str_ends_with($gitignoreLine, '/') ? '(?:$|/)' : '');
}
}
<?php










namespace Symfony\Component\Finder\Exception;




class AccessDeniedException extends \UnexpectedValueException
{
}
<?php










namespace Symfony\Component\Finder\Exception;




class DirectoryNotFoundException extends \InvalidArgumentException
{
}
<?php










namespace Symfony\Component\Finder;






class SplFileInfo extends \SplFileInfo
{
private string $relativePath;
private string $relativePathname;






public function __construct(string $file, string $relativePath, string $relativePathname)
{
parent::__construct($file);
$this->relativePath = $relativePath;
$this->relativePathname = $relativePathname;
}






public function getRelativePath(): string
{
return $this->relativePath;
}






public function getRelativePathname(): string
{
return $this->relativePathname;
}

public function getFilenameWithoutExtension(): string
{
$filename = $this->getFilename();

return pathinfo($filename, \PATHINFO_FILENAME);
}






public function getContents(): string
{
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
try {
$content = file_get_contents($this->getPathname());
} finally {
restore_error_handler();
}
if (false === $content) {
throw new \RuntimeException($error);
}

return $content;
}
}
<?php










namespace Symfony\Component\Console;

class Terminal
{
private static ?int $width = null;
private static ?int $height = null;
private static ?bool $stty = null;




public function getWidth(): int
{
$width = getenv('COLUMNS');
if (false !== $width) {
return (int) trim($width);
}

if (null === self::$width) {
self::initDimensions();
}

return self::$width ?: 80;
}




public function getHeight(): int
{
$height = getenv('LINES');
if (false !== $height) {
return (int) trim($height);
}

if (null === self::$height) {
self::initDimensions();
}

return self::$height ?: 50;
}




public static function hasSttyAvailable(): bool
{
if (null !== self::$stty) {
return self::$stty;
}


if (!\function_exists('exec')) {
return false;
}

exec('stty 2>&1', $output, $exitcode);

return self::$stty = 0 === $exitcode;
}

private static function initDimensions()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {


self::$width = (int) $matches[1];
self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
} elseif (!self::hasVt100Support() && self::hasSttyAvailable()) {


self::initDimensionsUsingStty();
} elseif (null !== $dimensions = self::getConsoleMode()) {

self::$width = (int) $dimensions[0];
self::$height = (int) $dimensions[1];
}
} else {
self::initDimensionsUsingStty();
}
}




private static function hasVt100Support(): bool
{
return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w'));
}




private static function initDimensionsUsingStty()
{
if ($sttyString = self::getSttyColumns()) {
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {

self::$width = (int) $matches[2];
self::$height = (int) $matches[1];
} elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {

self::$width = (int) $matches[2];
self::$height = (int) $matches[1];
}
}
}






private static function getConsoleMode(): ?array
{
$info = self::readFromProcess('mode CON');

if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
return null;
}

return [(int) $matches[2], (int) $matches[1]];
}




private static function getSttyColumns(): ?string
{
return self::readFromProcess('stty -a | grep columns');
}

private static function readFromProcess(string $command): ?string
{
if (!\function_exists('proc_open')) {
return null;
}

$descriptorspec = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];

$process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (!\is_resource($process)) {
return null;
}

$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

return $info;
}
}
<?php










namespace Symfony\Component\Console;

use Symfony\Component\Console\Exception\InvalidArgumentException;




final class Color
{
private const COLORS = [
'black' => 0,
'red' => 1,
'green' => 2,
'yellow' => 3,
'blue' => 4,
'magenta' => 5,
'cyan' => 6,
'white' => 7,
'default' => 9,
];

private const BRIGHT_COLORS = [
'gray' => 0,
'bright-red' => 1,
'bright-green' => 2,
'bright-yellow' => 3,
'bright-blue' => 4,
'bright-magenta' => 5,
'bright-cyan' => 6,
'bright-white' => 7,
];

private const AVAILABLE_OPTIONS = [
'bold' => ['set' => 1, 'unset' => 22],
'underscore' => ['set' => 4, 'unset' => 24],
'blink' => ['set' => 5, 'unset' => 25],
'reverse' => ['set' => 7, 'unset' => 27],
'conceal' => ['set' => 8, 'unset' => 28],
];

private string $foreground;
private string $background;
private array $options = [];

public function __construct(string $foreground = '', string $background = '', array $options = [])
{
$this->foreground = $this->parseColor($foreground);
$this->background = $this->parseColor($background, true);

foreach ($options as $option) {
if (!isset(self::AVAILABLE_OPTIONS[$option])) {
throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS))));
}

$this->options[$option] = self::AVAILABLE_OPTIONS[$option];
}
}

public function apply(string $text): string
{
return $this->set().$text.$this->unset();
}

public function set(): string
{
$setCodes = [];
if ('' !== $this->foreground) {
$setCodes[] = $this->foreground;
}
if ('' !== $this->background) {
$setCodes[] = $this->background;
}
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
}
if (0 === \count($setCodes)) {
return '';
}

return sprintf("\033[%sm", implode(';', $setCodes));
}

public function unset(): string
{
$unsetCodes = [];
if ('' !== $this->foreground) {
$unsetCodes[] = 39;
}
if ('' !== $this->background) {
$unsetCodes[] = 49;
}
foreach ($this->options as $option) {
$unsetCodes[] = $option['unset'];
}
if (0 === \count($unsetCodes)) {
return '';
}

return sprintf("\033[%sm", implode(';', $unsetCodes));
}

private function parseColor(string $color, bool $background = false): string
{
if ('' === $color) {
return '';
}

if ('#' === $color[0]) {
$color = substr($color, 1);

if (3 === \strlen($color)) {
$color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
}

if (6 !== \strlen($color)) {
throw new InvalidArgumentException(sprintf('Invalid "%s" color.', $color));
}

return ($background ? '4' : '3').$this->convertHexColorToAnsi(hexdec($color));
}

if (isset(self::COLORS[$color])) {
return ($background ? '4' : '3').self::COLORS[$color];
}

if (isset(self::BRIGHT_COLORS[$color])) {
return ($background ? '10' : '9').self::BRIGHT_COLORS[$color];
}

throw new InvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS)))));
}

private function convertHexColorToAnsi(int $color): string
{
$r = ($color >> 16) & 255;
$g = ($color >> 8) & 255;
$b = $color & 255;


if ('truecolor' !== getenv('COLORTERM')) {
return (string) $this->degradeHexColorToAnsi($r, $g, $b);
}

return sprintf('8;2;%d;%d;%d', $r, $g, $b);
}

private function degradeHexColorToAnsi(int $r, int $g, int $b): int
{
if (0 === round($this->getSaturation($r, $g, $b) / 50)) {
return 0;
}

return (round($b / 255) << 2) | (round($g / 255) << 1) | round($r / 255);
}

private function getSaturation(int $r, int $g, int $b): int
{
$r = $r / 255;
$g = $g / 255;
$b = $b / 255;
$v = max($r, $g, $b);

if (0 === $diff = $v - min($r, $g, $b)) {
return 0;
}

return (int) $diff * 100 / $v;
}
}
<?php










namespace Symfony\Component\Console\SignalRegistry;

final class SignalRegistry
{
private array $signalHandlers = [];

public function __construct()
{
if (\function_exists('pcntl_async_signals')) {
pcntl_async_signals(true);
}
}

public function register(int $signal, callable $signalHandler): void
{
if (!isset($this->signalHandlers[$signal])) {
$previousCallback = pcntl_signal_get_handler($signal);

if (\is_callable($previousCallback)) {
$this->signalHandlers[$signal][] = $previousCallback;
}
}

$this->signalHandlers[$signal][] = $signalHandler;

pcntl_signal($signal, [$this, 'handle']);
}

public static function isSupported(): bool
{
if (!\function_exists('pcntl_signal')) {
return false;
}

if (\in_array('pcntl_signal', explode(',', \ini_get('disable_functions')))) {
return false;
}

return true;
}




public function handle(int $signal): void
{
$count = \count($this->signalHandlers[$signal]);

foreach ($this->signalHandlers[$signal] as $i => $signalHandler) {
$hasNext = $i !== $count - 1;
$signalHandler($signal, $hasNext);
}
}
}
<?php










namespace Symfony\Component\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;




class SingleCommandApplication extends Command
{
private string $version = 'UNKNOWN';
private bool $autoExit = true;
private bool $running = false;




public function setVersion(string $version): static
{
$this->version = $version;

return $this;
}






public function setAutoExit(bool $autoExit): static
{
$this->autoExit = $autoExit;

return $this;
}

public function run(InputInterface $input = null, OutputInterface $output = null): int
{
if ($this->running) {
return parent::run($input, $output);
}


$application = new Application($this->getName() ?: 'UNKNOWN', $this->version);
$application->setAutoExit($this->autoExit);

$this->setName($_SERVER['argv'][0]);
$application->add($this);
$application->setDefaultCommand($this->getName(), true);

$this->running = true;
try {
$ret = $application->run($input, $output);
} finally {
$this->running = false;
}

return $ret ?? 1;
}
}
<?php










namespace Symfony\Component\Console\DependencyInjection;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;






class AddConsoleCommandPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$commandServices = $container->findTaggedServiceIds('console.command', true);
$lazyCommandMap = [];
$lazyCommandRefs = [];
$serviceIds = [];

foreach ($commandServices as $id => $tags) {
$definition = $container->getDefinition($id);
$definition->addTag('container.no_preload');
$class = $container->getParameterBag()->resolveValue($definition->getClass());

if (isset($tags[0]['command'])) {
$aliases = $tags[0]['command'];
} else {
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
}
$aliases = str_replace('%', '%%', $class::getDefaultName() ?? '');
}

$aliases = explode('|', $aliases ?? '');
$commandName = array_shift($aliases);

if ($isHidden = '' === $commandName) {
$commandName = array_shift($aliases);
}

if (null === $commandName) {
if (!$definition->isPublic() || $definition->isPrivate() || $definition->hasTag('container.private')) {
$commandId = 'console.command.public_alias.'.$id;
$container->setAlias($commandId, $id)->setPublic(true);
$id = $commandId;
}
$serviceIds[] = $id;

continue;
}

$description = $tags[0]['description'] ?? null;

unset($tags[0]);
$lazyCommandMap[$commandName] = $id;
$lazyCommandRefs[$id] = new TypedReference($id, $class);

foreach ($aliases as $alias) {
$lazyCommandMap[$alias] = $id;
}

foreach ($tags as $tag) {
if (isset($tag['command'])) {
$aliases[] = $tag['command'];
$lazyCommandMap[$tag['command']] = $id;
}

$description = $description ?? $tag['description'] ?? null;
}

$definition->addMethodCall('setName', [$commandName]);

if ($aliases) {
$definition->addMethodCall('setAliases', [$aliases]);
}

if ($isHidden) {
$definition->addMethodCall('setHidden', [true]);
}

if (!$description) {
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
}
$description = str_replace('%', '%%', $class::getDefaultDescription() ?? '');
}

if ($description) {
$definition->addMethodCall('setDescription', [$description]);

$container->register('.'.$id.'.lazy', LazyCommand::class)
->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]);

$lazyCommandRefs[$id] = new Reference('.'.$id.'.lazy');
}
}

$container
->register('console.command_loader', ContainerCommandLoader::class)
->setPublic(true)
->addTag('container.no_preload')
->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);

$container->setParameter('console.command.ids', $serviceIds);
}
}
<?php










namespace Symfony\Component\Console\Question;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;






class Question
{
private string $question;
private ?int $attempts = null;
private bool $hidden = false;
private bool $hiddenFallback = true;
private ?\Closure $autocompleterCallback = null;
private ?\Closure $validator = null;
private string|int|bool|null|float $default;
private ?\Closure $normalizer = null;
private bool $trimmable = true;
private bool $multiline = false;





public function __construct(string $question, string|bool|int|float $default = null)
{
$this->question = $question;
$this->default = $default;
}




public function getQuestion(): string
{
return $this->question;
}




public function getDefault(): string|bool|int|float|null
{
return $this->default;
}




public function isMultiline(): bool
{
return $this->multiline;
}






public function setMultiline(bool $multiline): static
{
$this->multiline = $multiline;

return $this;
}




public function isHidden(): bool
{
return $this->hidden;
}








public function setHidden(bool $hidden): static
{
if ($this->autocompleterCallback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}

$this->hidden = $hidden;

return $this;
}




public function isHiddenFallback(): bool
{
return $this->hiddenFallback;
}






public function setHiddenFallback(bool $fallback): static
{
$this->hiddenFallback = $fallback;

return $this;
}




public function getAutocompleterValues(): ?iterable
{
$callback = $this->getAutocompleterCallback();

return $callback ? $callback('') : null;
}








public function setAutocompleterValues(?iterable $values): static
{
if (\is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);

$callback = static function () use ($values) {
return $values;
};
} elseif ($values instanceof \Traversable) {
$valueCache = null;
$callback = static function () use ($values, &$valueCache) {
return $valueCache ?? $valueCache = iterator_to_array($values, false);
};
} else {
$callback = null;
}

return $this->setAutocompleterCallback($callback);
}




public function getAutocompleterCallback(): ?callable
{
return $this->autocompleterCallback;
}








public function setAutocompleterCallback(callable $callback = null): static
{
if ($this->hidden && null !== $callback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}

$this->autocompleterCallback = null === $callback || $callback instanceof \Closure ? $callback : \Closure::fromCallable($callback);

return $this;
}






public function setValidator(callable $validator = null): static
{
$this->validator = null === $validator || $validator instanceof \Closure ? $validator : \Closure::fromCallable($validator);

return $this;
}




public function getValidator(): ?callable
{
return $this->validator;
}










public function setMaxAttempts(?int $attempts): static
{
if (null !== $attempts && $attempts < 1) {
throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
}

$this->attempts = $attempts;

return $this;
}






public function getMaxAttempts(): ?int
{
return $this->attempts;
}








public function setNormalizer(callable $normalizer): static
{
$this->normalizer = $normalizer instanceof \Closure ? $normalizer : \Closure::fromCallable($normalizer);

return $this;
}






public function getNormalizer(): ?callable
{
return $this->normalizer;
}

protected function isAssoc(array $array)
{
return (bool) \count(array_filter(array_keys($array), 'is_string'));
}

public function isTrimmable(): bool
{
return $this->trimmable;
}




public function setTrimmable(bool $trimmable): static
{
$this->trimmable = $trimmable;

return $this;
}
}
<?php










namespace Symfony\Component\Console\Question;

use Symfony\Component\Console\Exception\InvalidArgumentException;






class ChoiceQuestion extends Question
{
private array $choices;
private bool $multiselect = false;
private string $prompt = ' > ';
private string $errorMessage = 'Value "%s" is invalid';






public function __construct(string $question, array $choices, mixed $default = null)
{
if (!$choices) {
throw new \LogicException('Choice question must have at least 1 choice available.');
}

parent::__construct($question, $default);

$this->choices = $choices;
$this->setValidator($this->getDefaultValidator());
$this->setAutocompleterValues($choices);
}




public function getChoices(): array
{
return $this->choices;
}








public function setMultiselect(bool $multiselect): static
{
$this->multiselect = $multiselect;
$this->setValidator($this->getDefaultValidator());

return $this;
}




public function isMultiselect(): bool
{
return $this->multiselect;
}




public function getPrompt(): string
{
return $this->prompt;
}






public function setPrompt(string $prompt): static
{
$this->prompt = $prompt;

return $this;
}








public function setErrorMessage(string $errorMessage): static
{
$this->errorMessage = $errorMessage;
$this->setValidator($this->getDefaultValidator());

return $this;
}

private function getDefaultValidator(): callable
{
$choices = $this->choices;
$errorMessage = $this->errorMessage;
$multiselect = $this->multiselect;
$isAssoc = $this->isAssoc($choices);

return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
if ($multiselect) {

if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) {
throw new InvalidArgumentException(sprintf($errorMessage, $selected));
}

$selectedChoices = explode(',', (string) $selected);
} else {
$selectedChoices = [$selected];
}

if ($this->isTrimmable()) {
foreach ($selectedChoices as $k => $v) {
$selectedChoices[$k] = trim((string) $v);
}
}

$multiselectChoices = [];
foreach ($selectedChoices as $value) {
$results = [];
foreach ($choices as $key => $choice) {
if ($choice === $value) {
$results[] = $key;
}
}

if (\count($results) > 1) {
throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of "%s".', implode('" or "', $results)));
}

$result = array_search($value, $choices);

if (!$isAssoc) {
if (false !== $result) {
$result = $choices[$result];
} elseif (isset($choices[$value])) {
$result = $choices[$value];
}
} elseif (false === $result && isset($choices[$value])) {
$result = $value;
}

if (false === $result) {
throw new InvalidArgumentException(sprintf($errorMessage, $value));
}


$multiselectChoices[] = $isAssoc ? (string) $result : $result;
}

if ($multiselect) {
return $multiselectChoices;
}

return current($multiselectChoices);
};
}
}
<?php










namespace Symfony\Component\Console\Question;






class ConfirmationQuestion extends Question
{
private string $trueAnswerRegex;






public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i')
{
parent::__construct($question, $default);

$this->trueAnswerRegex = $trueAnswerRegex;
$this->setNormalizer($this->getDefaultNormalizer());
}




private function getDefaultNormalizer(): callable
{
$default = $this->getDefault();
$regex = $this->trueAnswerRegex;

return function ($answer) use ($default, $regex) {
if (\is_bool($answer)) {
return $answer;
}

$answerIsTrue = (bool) preg_match($regex, $answer);
if (false === $default) {
return $answer && $answerIsTrue;
}

return '' === $answer || $answerIsTrue;
};
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\String\UnicodeString;






abstract class Helper implements HelperInterface
{
protected $helperSet = null;




public function setHelperSet(HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}




public function getHelperSet(): ?HelperSet
{
return $this->helperSet;
}





public static function width(?string $string): int
{
$string ?? $string = '';

if (preg_match('//u', $string)) {
return (new UnicodeString($string))->width(false);
}

if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
}

return mb_strwidth($string, $encoding);
}





public static function length(?string $string): int
{
$string ?? $string = '';

if (preg_match('//u', $string)) {
return (new UnicodeString($string))->length();
}

if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
}

return mb_strlen($string, $encoding);
}




public static function substr(?string $string, int $from, int $length = null): string
{
$string ?? $string = '';

if (false === $encoding = mb_detect_encoding($string, null, true)) {
return substr($string, $from, $length);
}

return mb_substr($string, $from, $length, $encoding);
}

public static function formatTime(int|float $secs)
{
static $timeFormats = [
[0, '< 1 sec'],
[1, '1 sec'],
[2, 'secs', 1],
[60, '1 min'],
[120, 'mins', 60],
[3600, '1 hr'],
[7200, 'hrs', 3600],
[86400, '1 day'],
[172800, 'days', 86400],
];

foreach ($timeFormats as $index => $format) {
if ($secs >= $format[0]) {
if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0])
|| $index == \count($timeFormats) - 1
) {
if (2 == \count($format)) {
return $format[1];
}

return floor($secs / $format[2]).' '.$format[1];
}
}
}
}

public static function formatMemory(int $memory)
{
if ($memory >= 1024 * 1024 * 1024) {
return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
}

if ($memory >= 1024 * 1024) {
return sprintf('%.1f MiB', $memory / 1024 / 1024);
}

if ($memory >= 1024) {
return sprintf('%d KiB', $memory / 1024);
}

return sprintf('%d B', $memory);
}

public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string)
{
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);

$string = $formatter->format($string ?? '');

$string = preg_replace("/\033\[[^m]*m/", '', $string ?? '');

$string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? '');
$formatter->setDecorated($isDecorated);

return $string;
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;




class TableCell
{
private string $value;
private array $options = [
'rowspan' => 1,
'colspan' => 1,
'style' => null,
];

public function __construct(string $value = '', array $options = [])
{
$this->value = $value;


if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}

if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) {
throw new InvalidArgumentException('The style option must be an instance of "TableCellStyle".');
}

$this->options = array_merge($this->options, $options);
}




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




public function getColspan(): int
{
return (int) $this->options['colspan'];
}




public function getRowspan(): int
{
return (int) $this->options['rowspan'];
}

public function getStyle(): ?TableCellStyle
{
return $this->options['style'];
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;








class TableStyle
{
private string $paddingChar = ' ';
private string $horizontalOutsideBorderChar = '-';
private string $horizontalInsideBorderChar = '-';
private string $verticalOutsideBorderChar = '|';
private string $verticalInsideBorderChar = '|';
private string $crossingChar = '+';
private string $crossingTopRightChar = '+';
private string $crossingTopMidChar = '+';
private string $crossingTopLeftChar = '+';
private string $crossingMidRightChar = '+';
private string $crossingBottomRightChar = '+';
private string $crossingBottomMidChar = '+';
private string $crossingBottomLeftChar = '+';
private string $crossingMidLeftChar = '+';
private string $crossingTopLeftBottomChar = '+';
private string $crossingTopMidBottomChar = '+';
private string $crossingTopRightBottomChar = '+';
private string $headerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
private string $footerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
private string $cellHeaderFormat = '<info>%s</info>';
private string $cellRowFormat = '%s';
private string $cellRowContentFormat = ' %s ';
private string $borderFormat = '%s';
private int $padType = \STR_PAD_RIGHT;






public function setPaddingChar(string $paddingChar): static
{
if (!$paddingChar) {
throw new LogicException('The padding char must not be empty.');
}

$this->paddingChar = $paddingChar;

return $this;
}




public function getPaddingChar(): string
{
return $this->paddingChar;
}

















public function setHorizontalBorderChars(string $outside, string $inside = null): static
{
$this->horizontalOutsideBorderChar = $outside;
$this->horizontalInsideBorderChar = $inside ?? $outside;

return $this;
}


















public function setVerticalBorderChars(string $outside, string $inside = null): static
{
$this->verticalOutsideBorderChar = $outside;
$this->verticalInsideBorderChar = $inside ?? $outside;

return $this;
}






public function getBorderChars(): array
{
return [
$this->horizontalOutsideBorderChar,
$this->verticalOutsideBorderChar,
$this->horizontalInsideBorderChar,
$this->verticalInsideBorderChar,
];
}
































public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): static
{
$this->crossingChar = $cross;
$this->crossingTopLeftChar = $topLeft;
$this->crossingTopMidChar = $topMid;
$this->crossingTopRightChar = $topRight;
$this->crossingMidRightChar = $midRight;
$this->crossingBottomRightChar = $bottomRight;
$this->crossingBottomMidChar = $bottomMid;
$this->crossingBottomLeftChar = $bottomLeft;
$this->crossingMidLeftChar = $midLeft;
$this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft;
$this->crossingTopMidBottomChar = $topMidBottom ?? $cross;
$this->crossingTopRightBottomChar = $topRightBottom ?? $midRight;

return $this;
}






public function setDefaultCrossingChar(string $char): self
{
return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char);
}




public function getCrossingChar(): string
{
return $this->crossingChar;
}






public function getCrossingChars(): array
{
return [
$this->crossingChar,
$this->crossingTopLeftChar,
$this->crossingTopMidChar,
$this->crossingTopRightChar,
$this->crossingMidRightChar,
$this->crossingBottomRightChar,
$this->crossingBottomMidChar,
$this->crossingBottomLeftChar,
$this->crossingMidLeftChar,
$this->crossingTopLeftBottomChar,
$this->crossingTopMidBottomChar,
$this->crossingTopRightBottomChar,
];
}






public function setCellHeaderFormat(string $cellHeaderFormat): static
{
$this->cellHeaderFormat = $cellHeaderFormat;

return $this;
}




public function getCellHeaderFormat(): string
{
return $this->cellHeaderFormat;
}






public function setCellRowFormat(string $cellRowFormat): static
{
$this->cellRowFormat = $cellRowFormat;

return $this;
}




public function getCellRowFormat(): string
{
return $this->cellRowFormat;
}






public function setCellRowContentFormat(string $cellRowContentFormat): static
{
$this->cellRowContentFormat = $cellRowContentFormat;

return $this;
}




public function getCellRowContentFormat(): string
{
return $this->cellRowContentFormat;
}






public function setBorderFormat(string $borderFormat): static
{
$this->borderFormat = $borderFormat;

return $this;
}




public function getBorderFormat(): string
{
return $this->borderFormat;
}






public function setPadType(int $padType): static
{
if (!\in_array($padType, [\STR_PAD_LEFT, \STR_PAD_RIGHT, \STR_PAD_BOTH], true)) {
throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
}

$this->padType = $padType;

return $this;
}




public function getPadType(): int
{
return $this->padType;
}

public function getHeaderTitleFormat(): string
{
return $this->headerTitleFormat;
}




public function setHeaderTitleFormat(string $format): static
{
$this->headerTitleFormat = $format;

return $this;
}

public function getFooterTitleFormat(): string
{
return $this->footerTitleFormat;
}




public function setFooterTitleFormat(string $format): static
{
$this->footerTitleFormat = $format;

return $this;
}
}
<?php










namespace Symfony\Component\Console\Helper;








class DebugFormatterHelper extends Helper
{
private const COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'];
private array $started = [];
private int $count = -1;




public function start(string $id, string $message, string $prefix = 'RUN'): string
{
$this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)];

return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
}




public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR'): string
{
$message = '';

if ($error) {
if (isset($this->started[$id]['out'])) {
$message .= "\n";
unset($this->started[$id]['out']);
}
if (!isset($this->started[$id]['err'])) {
$message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix);
$this->started[$id]['err'] = true;
}

$message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
} else {
if (isset($this->started[$id]['err'])) {
$message .= "\n";
unset($this->started[$id]['err']);
}
if (!isset($this->started[$id]['out'])) {
$message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix);
$this->started[$id]['out'] = true;
}

$message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
}

return $message;
}




public function stop(string $id, string $message, bool $successful, string $prefix = 'RES'): string
{
$trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';

if ($successful) {
return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
}

$message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);

unset($this->started[$id]['out'], $this->started[$id]['err']);

return $message;
}

private function getBorder(string $id): string
{
return sprintf('<bg=%s> </>', self::COLORS[$this->started[$id]['border']]);
}




public function getName(): string
{
return 'debug_formatter';
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Descriptor\JsonDescriptor;
use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Output\OutputInterface;






class DescriptorHelper extends Helper
{



private array $descriptors = [];

public function __construct()
{
$this
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
;
}










public function describe(OutputInterface $output, ?object $object, array $options = [])
{
$options = array_merge([
'raw_text' => false,
'format' => 'txt',
], $options);

if (!isset($this->descriptors[$options['format']])) {
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
}

$descriptor = $this->descriptors[$options['format']];
$descriptor->describe($output, $object, $options);
}






public function register(string $format, DescriptorInterface $descriptor): static
{
$this->descriptors[$format] = $descriptor;

return $this;
}




public function getName(): string
{
return 'descriptor';
}

public function getFormats(): array
{
return array_keys($this->descriptors);
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;








class HelperSet implements \IteratorAggregate
{

private array $helpers = [];




public function __construct(array $helpers = [])
{
foreach ($helpers as $alias => $helper) {
$this->set($helper, \is_int($alias) ? null : $alias);
}
}

public function set(HelperInterface $helper, string $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
$this->helpers[$alias] = $helper;
}

$helper->setHelperSet($this);
}




public function has(string $name): bool
{
return isset($this->helpers[$name]);
}






public function get(string $name): HelperInterface
{
if (!$this->has($name)) {
throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}

return $this->helpers[$name];
}

public function getIterator(): \Traversable
{
return new \ArrayIterator($this->helpers);
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\OutputInterface;




class ProgressIndicator
{
private const FORMATS = [
'normal' => ' %indicator% %message%',
'normal_no_ansi' => ' %message%',

'verbose' => ' %indicator% %message% (%elapsed:6s%)',
'verbose_no_ansi' => ' %message% (%elapsed:6s%)',

'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
];

private $output;
private int $startTime;
private ?string $format = null;
private ?string $message = null;
private array $indicatorValues;
private int $indicatorCurrent;
private int $indicatorChangeInterval;
private float $indicatorUpdateTime;
private bool $started = false;




private static array $formatters;





public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
{
$this->output = $output;

if (null === $format) {
$format = $this->determineBestFormat();
}

if (null === $indicatorValues) {
$indicatorValues = ['-', '\\', '|', '/'];
}

$indicatorValues = array_values($indicatorValues);

if (2 > \count($indicatorValues)) {
throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
}

$this->format = self::getFormatDefinition($format);
$this->indicatorChangeInterval = $indicatorChangeInterval;
$this->indicatorValues = $indicatorValues;
$this->startTime = time();
}




public function setMessage(?string $message)
{
$this->message = $message;

$this->display();
}




public function start(string $message)
{
if ($this->started) {
throw new LogicException('Progress indicator already started.');
}

$this->message = $message;
$this->started = true;
$this->startTime = time();
$this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
$this->indicatorCurrent = 0;

$this->display();
}




public function advance()
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
}

if (!$this->output->isDecorated()) {
return;
}

$currentTime = $this->getCurrentTimeInMilliseconds();

if ($currentTime < $this->indicatorUpdateTime) {
return;
}

$this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
++$this->indicatorCurrent;

$this->display();
}






public function finish(string $message)
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
}

$this->message = $message;
$this->display();
$this->output->writeln('');
$this->started = false;
}




public static function getFormatDefinition(string $name): ?string
{
return self::FORMATS[$name] ?? null;
}






public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
{
self::$formatters ??= self::initPlaceholderFormatters();

self::$formatters[$name] = $callable;
}




public static function getPlaceholderFormatterDefinition(string $name): ?callable
{
self::$formatters ??= self::initPlaceholderFormatters();

return self::$formatters[$name] ?? null;
}

private function display()
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}

$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
return $formatter($this);
}

return $matches[0];
}, $this->format ?? ''));
}

private function determineBestFormat(): string
{
switch ($this->output->getVerbosity()) {

case OutputInterface::VERBOSITY_VERBOSE:
return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
case OutputInterface::VERBOSITY_DEBUG:
return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
default:
return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
}
}




private function overwrite(string $message)
{
if ($this->output->isDecorated()) {
$this->output->write("\x0D\x1B[2K");
$this->output->write($message);
} else {
$this->output->writeln($message);
}
}

private function getCurrentTimeInMilliseconds(): float
{
return round(microtime(true) * 1000);
}




private static function initPlaceholderFormatters(): array
{
return [
'indicator' => function (self $indicator) {
return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
},
'message' => function (self $indicator) {
return $indicator->message;
},
'elapsed' => function (self $indicator) {
return Helper::formatTime(time() - $indicator->startTime);
},
'memory' => function () {
return Helper::formatMemory(memory_get_usage(true));
},
];
}
}
<?php










namespace Symfony\Component\Console\Helper;






interface HelperInterface
{



public function setHelperSet(HelperSet $helperSet = null);




public function getHelperSet(): ?HelperSet;






public function getName();
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Cursor;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;







final class ProgressBar
{
public const FORMAT_VERBOSE = 'verbose';
public const FORMAT_VERY_VERBOSE = 'very_verbose';
public const FORMAT_DEBUG = 'debug';
public const FORMAT_NORMAL = 'normal';

private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax';
private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax';
private const FORMAT_DEBUG_NOMAX = 'debug_nomax';
private const FORMAT_NORMAL_NOMAX = 'normal_nomax';

private int $barWidth = 28;
private string $barChar;
private string $emptyBarChar = '-';
private string $progressChar = '>';
private ?string $format = null;
private ?string $internalFormat = null;
private ?int $redrawFreq = 1;
private int $writeCount = 0;
private float $lastWriteTime = 0;
private float $minSecondsBetweenRedraws = 0;
private float $maxSecondsBetweenRedraws = 1;
private $output;
private int $step = 0;
private ?int $max = null;
private int $startTime;
private int $stepWidth;
private float $percent = 0.0;
private array $messages = [];
private bool $overwrite = true;
private $terminal;
private ?string $previousMessage = null;
private $cursor;

private static array $formatters;
private static array $formats;




public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}

$this->output = $output;
$this->setMaxSteps($max);
$this->terminal = new Terminal();

if (0 < $minSecondsBetweenRedraws) {
$this->redrawFreq = null;
$this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws;
}

if (!$this->output->isDecorated()) {

$this->overwrite = false;


$this->redrawFreq = null;
}

$this->startTime = time();
$this->cursor = new Cursor($output);
}









public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void
{
self::$formatters ??= self::initPlaceholderFormatters();

self::$formatters[$name] = $callable;
}






public static function getPlaceholderFormatterDefinition(string $name): ?callable
{
self::$formatters ??= self::initPlaceholderFormatters();

return self::$formatters[$name] ?? null;
}









public static function setFormatDefinition(string $name, string $format): void
{
self::$formats ??= self::initFormats();

self::$formats[$name] = $format;
}






public static function getFormatDefinition(string $name): ?string
{
self::$formats ??= self::initFormats();

return self::$formats[$name] ?? null;
}











public function setMessage(string $message, string $name = 'message')
{
$this->messages[$name] = $message;
}

public function getMessage(string $name = 'message')
{
return $this->messages[$name];
}

public function getStartTime(): int
{
return $this->startTime;
}

public function getMaxSteps(): int
{
return $this->max;
}

public function getProgress(): int
{
return $this->step;
}

private function getStepWidth(): int
{
return $this->stepWidth;
}

public function getProgressPercent(): float
{
return $this->percent;
}

public function getBarOffset(): float
{
return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth);
}

public function getEstimated(): float
{
if (!$this->step) {
return 0;
}

return round((time() - $this->startTime) / $this->step * $this->max);
}

public function getRemaining(): float
{
if (!$this->step) {
return 0;
}

return round((time() - $this->startTime) / $this->step * ($this->max - $this->step));
}

public function setBarWidth(int $size)
{
$this->barWidth = max(1, $size);
}

public function getBarWidth(): int
{
return $this->barWidth;
}

public function setBarCharacter(string $char)
{
$this->barChar = $char;
}

public function getBarCharacter(): string
{
return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar);
}

public function setEmptyBarCharacter(string $char)
{
$this->emptyBarChar = $char;
}

public function getEmptyBarCharacter(): string
{
return $this->emptyBarChar;
}

public function setProgressCharacter(string $char)
{
$this->progressChar = $char;
}

public function getProgressCharacter(): string
{
return $this->progressChar;
}

public function setFormat(string $format)
{
$this->format = null;
$this->internalFormat = $format;
}






public function setRedrawFrequency(?int $freq)
{
$this->redrawFreq = null !== $freq ? max(1, $freq) : null;
}

public function minSecondsBetweenRedraws(float $seconds): void
{
$this->minSecondsBetweenRedraws = $seconds;
}

public function maxSecondsBetweenRedraws(float $seconds): void
{
$this->maxSecondsBetweenRedraws = $seconds;
}






public function iterate(iterable $iterable, int $max = null): iterable
{
$this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0));

foreach ($iterable as $key => $value) {
yield $key => $value;

$this->advance();
}

$this->finish();
}






public function start(int $max = null)
{
$this->startTime = time();
$this->step = 0;
$this->percent = 0.0;

if (null !== $max) {
$this->setMaxSteps($max);
}

$this->display();
}






public function advance(int $step = 1)
{
$this->setProgress($this->step + $step);
}




public function setOverwrite(bool $overwrite)
{
$this->overwrite = $overwrite;
}

public function setProgress(int $step)
{
if ($this->max && $step > $this->max) {
$this->max = $step;
} elseif ($step < 0) {
$step = 0;
}

$redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10);
$prevPeriod = (int) ($this->step / $redrawFreq);
$currPeriod = (int) ($step / $redrawFreq);
$this->step = $step;
$this->percent = $this->max ? (float) $this->step / $this->max : 0;
$timeInterval = microtime(true) - $this->lastWriteTime;


if ($this->max === $step) {
$this->display();

return;
}


if ($timeInterval < $this->minSecondsBetweenRedraws) {
return;
}


if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) {
$this->display();
}
}

public function setMaxSteps(int $max)
{
$this->format = null;
$this->max = max(0, $max);
$this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4;
}




public function finish(): void
{
if (!$this->max) {
$this->max = $this->step;
}

if ($this->step === $this->max && !$this->overwrite) {

return;
}

$this->setProgress($this->max);
}




public function display(): void
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}

if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}

$this->overwrite($this->buildLine());
}








public function clear(): void
{
if (!$this->overwrite) {
return;
}

if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}

$this->overwrite('');
}

private function setRealFormat(string $format)
{

if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
$this->format = self::getFormatDefinition($format.'_nomax');
} elseif (null !== self::getFormatDefinition($format)) {
$this->format = self::getFormatDefinition($format);
} else {
$this->format = $format;
}
}




private function overwrite(string $message): void
{
if ($this->previousMessage === $message) {
return;
}

$originalMessage = $message;

if ($this->overwrite) {
if (null !== $this->previousMessage) {
if ($this->output instanceof ConsoleSectionOutput) {
$messageLines = explode("\n", $this->previousMessage);
$lineCount = \count($messageLines);
foreach ($messageLines as $messageLine) {
$messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine));
if ($messageLineLength > $this->terminal->getWidth()) {
$lineCount += floor($messageLineLength / $this->terminal->getWidth());
}
}
$this->output->clear($lineCount);
} else {
$lineCount = substr_count($this->previousMessage, "\n");
for ($i = 0; $i < $lineCount; ++$i) {
$this->cursor->moveToColumn(1);
$this->cursor->clearLine();
$this->cursor->moveUp();
}

$this->cursor->moveToColumn(1);
$this->cursor->clearLine();
}
}
} elseif ($this->step > 0) {
$message = \PHP_EOL.$message;
}

$this->previousMessage = $originalMessage;
$this->lastWriteTime = microtime(true);

$this->output->write($message);
++$this->writeCount;
}

private function determineBestFormat(): string
{
switch ($this->output->getVerbosity()) {

case OutputInterface::VERBOSITY_VERBOSE:
return $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX;
case OutputInterface::VERBOSITY_VERY_VERBOSE:
return $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX;
case OutputInterface::VERBOSITY_DEBUG:
return $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX;
default:
return $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX;
}
}

private static function initPlaceholderFormatters(): array
{
return [
'bar' => function (self $bar, OutputInterface $output) {
$completeBars = $bar->getBarOffset();
$display = str_repeat($bar->getBarCharacter(), $completeBars);
if ($completeBars < $bar->getBarWidth()) {
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter()));
$display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
}

return $display;
},
'elapsed' => function (self $bar) {
return Helper::formatTime(time() - $bar->getStartTime());
},
'remaining' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}

return Helper::formatTime($bar->getRemaining());
},
'estimated' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}

return Helper::formatTime($bar->getEstimated());
},
'memory' => function (self $bar) {
return Helper::formatMemory(memory_get_usage(true));
},
'current' => function (self $bar) {
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT);
},
'max' => function (self $bar) {
return $bar->getMaxSteps();
},
'percent' => function (self $bar) {
return floor($bar->getProgressPercent() * 100);
},
];
}

private static function initFormats(): array
{
return [
self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%',
self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]',

self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%',

self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%',

self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
];
}

private function buildLine(): string
{
\assert(null !== $this->format);

$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
$callback = function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = $formatter($this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
} else {
return $matches[0];
}

if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}

return $text;
};
$line = preg_replace_callback($regex, $callback, $this->format);


$linesLength = array_map(function ($subLine) {
return Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r")));
}, explode("\n", $line));

$linesWidth = max($linesLength);

$terminalWidth = $this->terminal->getWidth();
if ($linesWidth <= $terminalWidth) {
return $line;
}

$this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth);

return preg_replace_callback($regex, $callback, $this->format);
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;










class Table
{
private const SEPARATOR_TOP = 0;
private const SEPARATOR_TOP_BOTTOM = 1;
private const SEPARATOR_MID = 2;
private const SEPARATOR_BOTTOM = 3;
private const BORDER_OUTSIDE = 0;
private const BORDER_INSIDE = 1;

private ?string $headerTitle = null;
private ?string $footerTitle = null;
private array $headers = [];
private array $rows = [];
private bool $horizontal = false;
private array $effectiveColumnWidths = [];
private int $numberOfColumns;
private $output;
private $style;
private array $columnStyles = [];
private array $columnWidths = [];
private array $columnMaxWidths = [];
private bool $rendered = false;

private static array $styles;

public function __construct(OutputInterface $output)
{
$this->output = $output;

self::$styles ??= self::initStyles();

$this->setStyle('default');
}




public static function setStyleDefinition(string $name, TableStyle $style)
{
self::$styles ??= self::initStyles();

self::$styles[$name] = $style;
}




public static function getStyleDefinition(string $name): TableStyle
{
self::$styles ??= self::initStyles();

return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}






public function setStyle(TableStyle|string $name): static
{
$this->style = $this->resolveStyle($name);

return $this;
}




public function getStyle(): TableStyle
{
return $this->style;
}








public function setColumnStyle(int $columnIndex, TableStyle|string $name): static
{
$this->columnStyles[$columnIndex] = $this->resolveStyle($name);

return $this;
}






public function getColumnStyle(int $columnIndex): TableStyle
{
return $this->columnStyles[$columnIndex] ?? $this->getStyle();
}






public function setColumnWidth(int $columnIndex, int $width): static
{
$this->columnWidths[$columnIndex] = $width;

return $this;
}






public function setColumnWidths(array $widths): static
{
$this->columnWidths = [];
foreach ($widths as $index => $width) {
$this->setColumnWidth($index, $width);
}

return $this;
}









public function setColumnMaxWidth(int $columnIndex, int $width): static
{
if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
}

$this->columnMaxWidths[$columnIndex] = $width;

return $this;
}




public function setHeaders(array $headers): static
{
$headers = array_values($headers);
if (!empty($headers) && !\is_array($headers[0])) {
$headers = [$headers];
}

$this->headers = $headers;

return $this;
}

public function setRows(array $rows)
{
$this->rows = [];

return $this->addRows($rows);
}




public function addRows(array $rows): static
{
foreach ($rows as $row) {
$this->addRow($row);
}

return $this;
}




public function addRow(TableSeparator|array $row): static
{
if ($row instanceof TableSeparator) {
$this->rows[] = $row;

return $this;
}

$this->rows[] = array_values($row);

return $this;
}






public function appendRow(TableSeparator|array $row): static
{
if (!$this->output instanceof ConsoleSectionOutput) {
throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
}

if ($this->rendered) {
$this->output->clear($this->calculateRowCount());
}

$this->addRow($row);
$this->render();

return $this;
}




public function setRow(int|string $column, array $row): static
{
$this->rows[$column] = $row;

return $this;
}




public function setHeaderTitle(?string $title): static
{
$this->headerTitle = $title;

return $this;
}




public function setFooterTitle(?string $title): static
{
$this->footerTitle = $title;

return $this;
}




public function setHorizontal(bool $horizontal = true): static
{
$this->horizontal = $horizontal;

return $this;
}














public function render()
{
$divider = new TableSeparator();
if ($this->horizontal) {
$rows = [];
foreach ($this->headers[0] ?? [] as $i => $header) {
$rows[$i] = [$header];
foreach ($this->rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
if (isset($row[$i])) {
$rows[$i][] = $row[$i];
} elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {

} else {
$rows[$i][] = null;
}
}
}
} else {
$rows = array_merge($this->headers, [$divider], $this->rows);
}

$this->calculateNumberOfColumns($rows);

$rowGroups = $this->buildTableRows($rows);
$this->calculateColumnsWidth($rowGroups);

$isHeader = !$this->horizontal;
$isFirstRow = $this->horizontal;
$hasTitle = (bool) $this->headerTitle;

foreach ($rowGroups as $rowGroup) {
$isHeaderSeparatorRendered = false;

foreach ($rowGroup as $row) {
if ($divider === $row) {
$isHeader = false;
$isFirstRow = true;

continue;
}

if ($row instanceof TableSeparator) {
$this->renderRowSeparator();

continue;
}

if (!$row) {
continue;
}

if ($isHeader && !$isHeaderSeparatorRendered) {
$this->renderRowSeparator(
$isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
$hasTitle ? $this->headerTitle : null,
$hasTitle ? $this->style->getHeaderTitleFormat() : null
);
$hasTitle = false;
$isHeaderSeparatorRendered = true;
}

if ($isFirstRow) {
$this->renderRowSeparator(
$isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
$hasTitle ? $this->headerTitle : null,
$hasTitle ? $this->style->getHeaderTitleFormat() : null
);
$isFirstRow = false;
$hasTitle = false;
}

if ($this->horizontal) {
$this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
} else {
$this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
}
}
}
$this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());

$this->cleanup();
$this->rendered = true;
}








private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
{
if (0 === $count = $this->numberOfColumns) {
return;
}

$borders = $this->style->getBorderChars();
if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
return;
}

$crossings = $this->style->getCrossingChars();
if (self::SEPARATOR_MID === $type) {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
} elseif (self::SEPARATOR_TOP === $type) {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
} elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
} else {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
}

$markup = $leftChar;
for ($column = 0; $column < $count; ++$column) {
$markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
$markup .= $column === $count - 1 ? $rightChar : $midChar;
}

if (null !== $title) {
$titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)));
$markupLength = Helper::width($markup);
if ($titleLength > $limit = $markupLength - 4) {
$titleLength = $limit;
$formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, '')));
$formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
}

$titleStart = intdiv($markupLength - $titleLength, 2);
if (false === mb_detect_encoding($markup, null, true)) {
$markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
} else {
$markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
}
}

$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
}




private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
{
$borders = $this->style->getBorderChars();

return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
}








private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
{
$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
$columns = $this->getRowColumns($row);
$last = \count($columns) - 1;
foreach ($columns as $i => $column) {
if ($firstCellFormat && 0 === $i) {
$rowContent .= $this->renderCell($row, $column, $firstCellFormat);
} else {
$rowContent .= $this->renderCell($row, $column, $cellFormat);
}
$rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
}
$this->output->writeln($rowContent);
}




private function renderCell(array $row, int $column, string $cellFormat): string
{
$cell = $row[$column] ?? '';
$width = $this->effectiveColumnWidths[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {

foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
$width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
}
}


if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
$width += \strlen($cell) - mb_strwidth($cell, $encoding);
}

$style = $this->getColumnStyle($column);

if ($cell instanceof TableSeparator) {
return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
}

$width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell));
$content = sprintf($style->getCellRowContentFormat(), $cell);

$padType = $style->getPadType();
if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) {
$isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell);
if ($isNotStyledByTag) {
$cellFormat = $cell->getStyle()->getCellFormat();
if (!\is_string($cellFormat)) {
$tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';');
$cellFormat = '<'.$tag.'>%s</>';
}

if (strstr($content, '</>')) {
$content = str_replace('</>', '', $content);
$width -= 3;
}
if (strstr($content, '<fg=default;bg=default>')) {
$content = str_replace('<fg=default;bg=default>', '', $content);
$width -= \strlen('<fg=default;bg=default>');
}
}

$padType = $cell->getStyle()->getPadByAlign();
}

return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType));
}




private function calculateNumberOfColumns(array $rows)
{
$columns = [0];
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}

$columns[] = $this->getNumberOfColumns($row);
}

$this->numberOfColumns = max($columns);
}

private function buildTableRows(array $rows): TableRows
{

$formatter = $this->output->getFormatter();
$unmergedRows = [];
for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
$rows = $this->fillNextRows($rows, $rowKey);


foreach ($rows[$rowKey] as $column => $cell) {
$colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;

if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
}
if (!strstr($cell ?? '', "\n")) {
continue;
}
$escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
$cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default></>\n", $cell));
foreach ($lines as $lineKey => $line) {
if ($colspan > 1) {
$line = new TableCell($line, ['colspan' => $colspan]);
}
if (0 === $lineKey) {
$rows[$rowKey][$column] = $line;
} else {
if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
$unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
}
$unmergedRows[$rowKey][$lineKey][$column] = $line;
}
}
}
}

return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
foreach ($rows as $rowKey => $row) {
$rowGroup = [$row instanceof TableSeparator ? $row : $this->fillCells($row)];

if (isset($unmergedRows[$rowKey])) {
foreach ($unmergedRows[$rowKey] as $row) {
$rowGroup[] = $row instanceof TableSeparator ? $row : $this->fillCells($row);
}
}
yield $rowGroup;
}
});
}

private function calculateRowCount(): int
{
$numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));

if ($this->headers) {
++$numberOfRows; 
}

if (\count($this->rows) > 0) {
++$numberOfRows; 
}

return $numberOfRows;
}






private function fillNextRows(array $rows, int $line): array
{
$unmergedRows = [];
foreach ($rows[$line] as $column => $cell) {
if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !$cell instanceof \Stringable) {
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
}
if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
$nbLines = $cell->getRowspan() - 1;
$lines = [$cell];
if (strstr($cell, "\n")) {
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
$nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;

$rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
unset($lines[0]);
}


$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
$value = $lines[$unmergedRowKey - $line] ?? '';
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
if ($nbLines === $unmergedRowKey - $line) {
break;
}
}
}
}

foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {

if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
foreach ($unmergedRow as $cellKey => $cell) {

array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
}
} else {
$row = $this->copyRow($rows, $unmergedRowKey - 1);
foreach ($unmergedRow as $column => $cell) {
if (!empty($cell)) {
$row[$column] = $unmergedRow[$column];
}
}
array_splice($rows, $unmergedRowKey, 0, [$row]);
}
}

return $rows;
}




private function fillCells(iterable $row)
{
$newRow = [];

foreach ($row as $column => $cell) {
$newRow[] = $cell;
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {

$newRow[] = '';
}
}
}

return $newRow ?: $row;
}

private function copyRow(array $rows, int $line): array
{
$row = $rows[$line];
foreach ($row as $cellKey => $cellValue) {
$row[$cellKey] = '';
if ($cellValue instanceof TableCell) {
$row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
}
}

return $row;
}




private function getNumberOfColumns(array $row): int
{
$columns = \count($row);
foreach ($row as $column) {
$columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
}

return $columns;
}




private function getRowColumns(array $row): array
{
$columns = range(0, $this->numberOfColumns - 1);
foreach ($row as $cellKey => $cell) {
if ($cell instanceof TableCell && $cell->getColspan() > 1) {

$columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
}
}

return $columns;
}




private function calculateColumnsWidth(iterable $groups)
{
for ($column = 0; $column < $this->numberOfColumns; ++$column) {
$lengths = [];
foreach ($groups as $group) {
foreach ($group as $row) {
if ($row instanceof TableSeparator) {
continue;
}

foreach ($row as $i => $cell) {
if ($cell instanceof TableCell) {
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::width($textContent);
if ($textLength > 0) {
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}
}
}
}

$lengths[] = $this->getCellWidth($row, $column);
}
}

$this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2;
}
}

private function getColumnSeparatorWidth(): int
{
return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
}

private function getCellWidth(array $row, int $column): int
{
$cellWidth = 0;

if (isset($row[$column])) {
$cell = $row[$column];
$cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell));
}

$columnWidth = $this->columnWidths[$column] ?? 0;
$cellWidth = max($cellWidth, $columnWidth);

return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
}




private function cleanup()
{
$this->effectiveColumnWidths = [];
unset($this->numberOfColumns);
}




private static function initStyles(): array
{
$borderless = new TableStyle();
$borderless
->setHorizontalBorderChars('=')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar(' ')
;

$compact = new TableStyle();
$compact
->setHorizontalBorderChars('')
->setVerticalBorderChars('')
->setDefaultCrossingChar('')
->setCellRowContentFormat('%s ')
;

$styleGuide = new TableStyle();
$styleGuide
->setHorizontalBorderChars('-')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar(' ')
->setCellHeaderFormat('%s')
;

$box = (new TableStyle())
->setHorizontalBorderChars('─')
->setVerticalBorderChars('│')
->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
;

$boxDouble = (new TableStyle())
->setHorizontalBorderChars('═', '─')
->setVerticalBorderChars('║', '│')
->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
;

return [
'default' => new TableStyle(),
'borderless' => $borderless,
'compact' => $compact,
'symfony-style-guide' => $styleGuide,
'box' => $box,
'box-double' => $boxDouble,
];
}

private function resolveStyle(TableStyle|string $name): TableStyle
{
if ($name instanceof TableStyle) {
return $name;
}

return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;




class TableCellStyle
{
public const DEFAULT_ALIGN = 'left';

private const TAG_OPTIONS = [
'fg',
'bg',
'options',
];

private const ALIGN_MAP = [
'left' => \STR_PAD_RIGHT,
'center' => \STR_PAD_BOTH,
'right' => \STR_PAD_LEFT,
];

private array $options = [
'fg' => 'default',
'bg' => 'default',
'options' => null,
'align' => self::DEFAULT_ALIGN,
'cellFormat' => null,
];

public function __construct(array $options = [])
{
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}

if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) {
throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP))));
}

$this->options = array_merge($this->options, $options);
}

public function getOptions(): array
{
return $this->options;
}






public function getTagOptions(): array
{
return array_filter(
$this->getOptions(),
function ($key) {
return \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]);
},
\ARRAY_FILTER_USE_KEY
);
}

public function getPadByAlign(): int
{
return self::ALIGN_MAP[$this->getOptions()['align']];
}

public function getCellFormat(): ?string
{
return $this->getOptions()['cellFormat'];
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Formatter\OutputFormatter;






class FormatterHelper extends Helper
{



public function formatSection(string $section, string $message, string $style = 'info'): string
{
return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
}




public function formatBlock(string|array $messages, string $style, bool $large = false): string
{
if (!\is_array($messages)) {
$messages = [$messages];
}

$len = 0;
$lines = [];
foreach ($messages as $message) {
$message = OutputFormatter::escape($message);
$lines[] = sprintf($large ? '  %s  ' : ' %s ', $message);
$len = max(self::width($message) + ($large ? 4 : 2), $len);
}

$messages = $large ? [str_repeat(' ', $len)] : [];
for ($i = 0; isset($lines[$i]); ++$i) {
$messages[] = $lines[$i].str_repeat(' ', $len - self::width($lines[$i]));
}
if ($large) {
$messages[] = str_repeat(' ', $len);
}

for ($i = 0; isset($messages[$i]); ++$i) {
$messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style);
}

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




public function truncate(string $message, int $length, string $suffix = '...'): string
{
$computedLength = $length - self::width($suffix);

if ($computedLength > self::width($message)) {
return $message;
}

return self::substr($message, 0, $length).$suffix;
}




public function getName(): string
{
return 'formatter';
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;






class SymfonyQuestionHelper extends QuestionHelper
{



protected function writePrompt(OutputInterface $output, Question $question)
{
$text = OutputFormatter::escapeTrailingBackslash($question->getQuestion());
$default = $question->getDefault();

if ($question->isMultiline()) {
$text .= sprintf(' (press %s to continue)', $this->getEofShortcut());
}

switch (true) {
case null === $default:
$text = sprintf(' <info>%s</info>:', $text);

break;

case $question instanceof ConfirmationQuestion:
$text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');

break;

case $question instanceof ChoiceQuestion && $question->isMultiselect():
$choices = $question->getChoices();
$default = explode(',', $default);

foreach ($default as $key => $value) {
$default[$key] = $choices[trim($value)];
}

$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(implode(', ', $default)));

break;

case $question instanceof ChoiceQuestion:
$choices = $question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$default] ?? $default));

break;

default:
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default));
}

$output->writeln($text);

$prompt = ' > ';

if ($question instanceof ChoiceQuestion) {
$output->writeln($this->formatChoiceQuestionChoices($question, 'comment'));

$prompt = $question->getPrompt();
}

$output->write($prompt);
}




protected function writeError(OutputInterface $output, \Exception $error)
{
if ($output instanceof SymfonyStyle) {
$output->newLine();
$output->error($error->getMessage());

return;
}

parent::writeError($output, $error);
}

private function getEofShortcut(): string
{
if ('Windows' === \PHP_OS_FAMILY) {
return '<comment>Ctrl+Z</comment> then <comment>Enter</comment>';
}

return '<comment>Ctrl+D</comment>';
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;




final class Dumper
{
private $output;
private $dumper;
private $cloner;
private \Closure $handler;

public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
{
$this->output = $output;
$this->dumper = $dumper;
$this->cloner = $cloner;

if (class_exists(CliDumper::class)) {
$this->handler = function ($var): string {
$dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
$dumper->setColors($this->output->isDecorated());

return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
};
} else {
$this->handler = function ($var): string {
switch (true) {
case null === $var:
return 'null';
case true === $var:
return 'true';
case false === $var:
return 'false';
case \is_string($var):
return '"'.$var.'"';
default:
return rtrim(print_r($var, true));
}
};
}
}

public function __invoke(mixed $var): string
{
return ($this->handler)($var);
}
}
<?php










namespace Symfony\Component\Console\Helper;




class TableRows implements \IteratorAggregate
{
private \Closure $generator;

public function __construct(\Closure $generator)
{
$this->generator = $generator;
}

public function getIterator(): \Traversable
{
return ($this->generator)();
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Cursor;
use Symfony\Component\Console\Exception\MissingInputException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StreamableInputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;

use function Symfony\Component\String\s;






class QuestionHelper extends Helper
{



private $inputStream;

private static bool $stty = true;
private static bool $stdinIsInteractive;








public function ask(InputInterface $input, OutputInterface $output, Question $question): mixed
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}

if (!$input->isInteractive()) {
return $this->getDefaultAnswer($question);
}

if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
$this->inputStream = $stream;
}

try {
if (!$question->getValidator()) {
return $this->doAsk($output, $question);
}

$interviewer = function () use ($output, $question) {
return $this->doAsk($output, $question);
};

return $this->validateAttempts($interviewer, $output, $question);
} catch (MissingInputException $exception) {
$input->setInteractive(false);

if (null === $fallbackOutput = $this->getDefaultAnswer($question)) {
throw $exception;
}

return $fallbackOutput;
}
}




public function getName(): string
{
return 'question';
}




public static function disableStty()
{
self::$stty = false;
}






private function doAsk(OutputInterface $output, Question $question): mixed
{
$this->writePrompt($output, $question);

$inputStream = $this->inputStream ?: \STDIN;
$autocomplete = $question->getAutocompleterCallback();

if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
$ret = false;
if ($question->isHidden()) {
try {
$hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
$ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
} catch (RuntimeException $e) {
if (!$question->isHiddenFallback()) {
throw $e;
}
}
}

if (false === $ret) {
$ret = $this->readInput($inputStream, $question);
if (false === $ret) {
throw new MissingInputException('Aborted.');
}
if ($question->isTrimmable()) {
$ret = trim($ret);
}
}
} else {
$autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
$ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
}

if ($output instanceof ConsoleSectionOutput) {
$output->addContent($ret);
}

$ret = \strlen($ret) > 0 ? $ret : $question->getDefault();

if ($normalizer = $question->getNormalizer()) {
return $normalizer($ret);
}

return $ret;
}

private function getDefaultAnswer(Question $question): mixed
{
$default = $question->getDefault();

if (null === $default) {
return $default;
}

if ($validator = $question->getValidator()) {
return \call_user_func($question->getValidator(), $default);
} elseif ($question instanceof ChoiceQuestion) {
$choices = $question->getChoices();

if (!$question->isMultiselect()) {
return $choices[$default] ?? $default;
}

$default = explode(',', $default);
foreach ($default as $k => $v) {
$v = $question->isTrimmable() ? trim($v) : $v;
$default[$k] = $choices[$v] ?? $v;
}
}

return $default;
}




protected function writePrompt(OutputInterface $output, Question $question)
{
$message = $question->getQuestion();

if ($question instanceof ChoiceQuestion) {
$output->writeln(array_merge([
$question->getQuestion(),
], $this->formatChoiceQuestionChoices($question, 'info')));

$message = $question->getPrompt();
}

$output->write($message);
}




protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag): array
{
$messages = [];

$maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices())));

foreach ($choices as $key => $value) {
$padding = str_repeat(' ', $maxWidth - self::width($key));

$messages[] = sprintf("  [<$tag>%s$padding</$tag>] %s", $key, $value);
}

return $messages;
}




protected function writeError(OutputInterface $output, \Exception $error)
{
if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
} else {
$message = '<error>'.$error->getMessage().'</error>';
}

$output->writeln($message);
}






private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
{
$cursor = new Cursor($output, $inputStream);

$fullChoice = '';
$ret = '';

$i = 0;
$ofs = -1;
$matches = $autocomplete($ret);
$numMatches = \count($matches);

$sttyMode = shell_exec('stty -g');
$isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null);
$r = [$inputStream];
$w = [];


shell_exec('stty -icanon -echo');


$output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));


while (!feof($inputStream)) {
while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) {

$r = [$inputStream];
}
$c = fread($inputStream, 1);


if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
shell_exec('stty '.$sttyMode);
throw new MissingInputException('Aborted.');
} elseif ("\177" === $c) { 
if (0 === $numMatches && 0 !== $i) {
--$i;
$cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));

$fullChoice = self::substr($fullChoice, 0, $i);
}

if (0 === $i) {
$ofs = -1;
$matches = $autocomplete($ret);
$numMatches = \count($matches);
} else {
$numMatches = 0;
}


$ret = self::substr($ret, 0, $i);
} elseif ("\033" === $c) {

$c .= fread($inputStream, 2);


if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}

if (0 === $numMatches) {
continue;
}

$ofs += ('A' === $c[2]) ? -1 : 1;
$ofs = ($numMatches + $ofs) % $numMatches;
}
} elseif (\ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
$ret = (string) $matches[$ofs];

$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
$output->write($remainingCharacters);
$fullChoice .= $remainingCharacters;
$i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);

$matches = array_filter(
$autocomplete($ret),
function ($match) use ($ret) {
return '' === $ret || str_starts_with($match, $ret);
}
);
$numMatches = \count($matches);
$ofs = -1;
}

if ("\n" === $c) {
$output->write($c);
break;
}

$numMatches = 0;
}

continue;
} else {
if ("\x80" <= $c) {
$c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
}

$output->write($c);
$ret .= $c;
$fullChoice .= $c;
++$i;

$tempRet = $ret;

if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
}

$numMatches = 0;
$ofs = 0;

foreach ($autocomplete($ret) as $value) {

if (str_starts_with($value, $tempRet)) {
$matches[$numMatches++] = $value;
}
}
}

$cursor->clearLineAfter();

if ($numMatches > 0 && -1 !== $ofs) {
$cursor->savePosition();

$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
$cursor->restorePosition();
}
}


shell_exec('stty '.$sttyMode);

return $fullChoice;
}

private function mostRecentlyEnteredValue(string $entered): string
{

if (!str_contains($entered, ',')) {
return $entered;
}

$choices = explode(',', $entered);
if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) {
return $lastChoice;
}

return $entered;
}









private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';


if ('phar:' === substr(__FILE__, 0, 5)) {
$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
copy($exe, $tmpExe);
$exe = $tmpExe;
}

$sExec = shell_exec('"'.$exe.'"');
$value = $trimmable ? rtrim($sExec) : $sExec;
$output->writeln('');

if (isset($tmpExe)) {
unlink($tmpExe);
}

return $value;
}

if (self::$stty && Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
} elseif ($this->isInteractiveInput($inputStream)) {
throw new RuntimeException('Unable to hide the response.');
}

$value = fgets($inputStream, 4096);

if (self::$stty && Terminal::hasSttyAvailable()) {
shell_exec('stty '.$sttyMode);
}

if (false === $value) {
throw new MissingInputException('Aborted.');
}
if ($trimmable) {
$value = trim($value);
}
$output->writeln('');

return $value;
}








private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question): mixed
{
$error = null;
$attempts = $question->getMaxAttempts();

while (null === $attempts || $attempts--) {
if (null !== $error) {
$this->writeError($output, $error);
}

try {
return $question->getValidator()($interviewer());
} catch (RuntimeException $e) {
throw $e;
} catch (\Exception $error) {
}
}

throw $error;
}

private function isInteractiveInput($inputStream): bool
{
if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
return false;
}

if (isset(self::$stdinIsInteractive)) {
return self::$stdinIsInteractive;
}

if (\function_exists('stream_isatty')) {
return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r'));
}

if (\function_exists('posix_isatty')) {
return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r'));
}

if (!\function_exists('exec')) {
return self::$stdinIsInteractive = true;
}

exec('stty 2> /dev/null', $output, $status);

return self::$stdinIsInteractive = 1 !== $status;
}







private function readInput($inputStream, Question $question): string|false
{
if (!$question->isMultiline()) {
$cp = $this->setIOCodepage();
$ret = fgets($inputStream, 4096);

return $this->resetIOCodepage($cp, $ret);
}

$multiLineStreamReader = $this->cloneInputStream($inputStream);
if (null === $multiLineStreamReader) {
return false;
}

$ret = '';
$cp = $this->setIOCodepage();
while (false !== ($char = fgetc($multiLineStreamReader))) {
if (\PHP_EOL === "{$ret}{$char}") {
break;
}
$ret .= $char;
}

return $this->resetIOCodepage($cp, $ret);
}

private function setIOCodepage(): int
{
if (\function_exists('sapi_windows_cp_set')) {
$cp = sapi_windows_cp_get();
sapi_windows_cp_set(sapi_windows_cp_get('oem'));

return $cp;
}

return 0;
}




private function resetIOCodepage(int $cp, string|false $input): string|false
{
if (0 !== $cp) {
sapi_windows_cp_set($cp);

if (false !== $input && '' !== $input) {
$input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input);
}
}

return $input;
}









private function cloneInputStream($inputStream)
{
$streamMetaData = stream_get_meta_data($inputStream);
$seekable = $streamMetaData['seekable'] ?? false;
$mode = $streamMetaData['mode'] ?? 'rb';
$uri = $streamMetaData['uri'] ?? null;

if (null === $uri) {
return null;
}

$cloneStream = fopen($uri, $mode);



if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) {
$offset = ftell($inputStream);
rewind($inputStream);
stream_copy_to_stream($inputStream, $cloneStream);
fseek($inputStream, $offset);
fseek($cloneStream, $offset);
}

return $cloneStream;
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;








class ProcessHelper extends Helper
{







public function run(OutputInterface $output, array|Process $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
{
if (!class_exists(Process::class)) {
throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
}

if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}

$formatter = $this->getHelperSet()->get('debug_formatter');

if ($cmd instanceof Process) {
$cmd = [$cmd];
}

if (\is_string($cmd[0] ?? null)) {
$process = new Process($cmd);
$cmd = [];
} elseif (($cmd[0] ?? null) instanceof Process) {
$process = $cmd[0];
unset($cmd[0]);
} else {
throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
}

if ($verbosity <= $output->getVerbosity()) {
$output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
}

if ($output->isDebug()) {
$callback = $this->wrapCallback($output, $process, $callback);
}

$process->run($callback, $cmd);

if ($verbosity <= $output->getVerbosity()) {
$message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
$output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
}

if (!$process->isSuccessful() && null !== $error) {
$output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
}

return $process;
}















public function mustRun(OutputInterface $output, array|Process $cmd, string $error = null, callable $callback = null): Process
{
$process = $this->run($output, $cmd, $error, $callback);

if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}

return $process;
}




public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}

$formatter = $this->getHelperSet()->get('debug_formatter');

return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
$output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));

if (null !== $callback) {
$callback($type, $buffer);
}
};
}

private function escapeString(string $str): string
{
return str_replace('<', '\\<', $str);
}




public function getName(): string
{
return 'process';
}
}
<?php










namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputInterface;






abstract class InputAwareHelper extends Helper implements InputAwareInterface
{
protected $input;




public function setInput(InputInterface $input)
{
$this->input = $input;
}
}
<?php










namespace Symfony\Component\Console\Helper;






class TableSeparator extends TableCell
{
public function __construct(array $options = [])
{
parent::__construct('', $options);
}
}
<?php










namespace Symfony\Component\Console\CommandLoader;

use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;






class ContainerCommandLoader implements CommandLoaderInterface
{
private $container;
private array $commandMap;




public function __construct(ContainerInterface $container, array $commandMap)
{
$this->container = $container;
$this->commandMap = $commandMap;
}




public function get(string $name): Command
{
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}

return $this->container->get($this->commandMap[$name]);
}




public function has(string $name): bool
{
return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
}




public function getNames(): array
{
return array_keys($this->commandMap);
}
}
<?php










namespace Symfony\Component\Console\CommandLoader;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;






class FactoryCommandLoader implements CommandLoaderInterface
{
private array $factories;




public function __construct(array $factories)
{
$this->factories = $factories;
}




public function has(string $name): bool
{
return isset($this->factories[$name]);
}




public function get(string $name): Command
{
if (!isset($this->factories[$name])) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}

$factory = $this->factories[$name];

return $factory();
}




public function getNames(): array
{
return array_keys($this->factories);
}
}
<?php










namespace Symfony\Component\Console\CommandLoader;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;




interface CommandLoaderInterface
{





public function get(string $name): Command;




public function has(string $name): bool;




public function getNames(): array;
}
<?php










namespace Symfony\Component\Console\Formatter;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Contracts\Service\ResetInterface;




class OutputFormatterStyleStack implements ResetInterface
{



private array $styles = [];

private $emptyStyle;

public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle();
$this->reset();
}




public function reset()
{
$this->styles = [];
}




public function push(OutputFormatterStyleInterface $style)
{
$this->styles[] = $style;
}






public function pop(OutputFormatterStyleInterface $style = null): OutputFormatterStyleInterface
{
if (empty($this->styles)) {
return $this->emptyStyle;
}

if (null === $style) {
return array_pop($this->styles);
}

foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
if ($style->apply('') === $stackedStyle->apply('')) {
$this->styles = \array_slice($this->styles, 0, $index);

return $stackedStyle;
}
}

throw new InvalidArgumentException('Incorrectly nested style tag found.');
}




public function getCurrent(): OutputFormatterStyleInterface
{
if (empty($this->styles)) {
return $this->emptyStyle;
}

return $this->styles[\count($this->styles) - 1];
}




public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle): static
{
$this->emptyStyle = $emptyStyle;

return $this;
}

public function getEmptyStyle(): OutputFormatterStyleInterface
{
return $this->emptyStyle;
}
}
<?php










namespace Symfony\Component\Console\Formatter;






interface OutputFormatterInterface
{



public function setDecorated(bool $decorated);




public function isDecorated(): bool;




public function setStyle(string $name, OutputFormatterStyleInterface $style);




public function hasStyle(string $name): bool;






public function getStyle(string $name): OutputFormatterStyleInterface;




public function format(?string $message): ?string;
}
<?php










namespace Symfony\Component\Console\Formatter;




final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
{



public function apply(string $text): string
{
return $text;
}




public function setBackground(string $color = null): void
{

}




public function setForeground(string $color = null): void
{

}




public function setOption(string $option): void
{

}




public function setOptions(array $options): void
{

}




public function unsetOption(string $option): void
{

}
}
<?php










namespace Symfony\Component\Console\Formatter;






interface OutputFormatterStyleInterface
{



public function setForeground(string $color = null);




public function setBackground(string $color = null);




public function setOption(string $option);




public function unsetOption(string $option);




public function setOptions(array $options);




public function apply(string $text): string;
}
<?php










namespace Symfony\Component\Console\Formatter;






interface WrappableOutputFormatterInterface extends OutputFormatterInterface
{



public function formatAndWrap(?string $message, int $width);
}
<?php










namespace Symfony\Component\Console\Formatter;




final class NullOutputFormatter implements OutputFormatterInterface
{
private $style;




public function format(?string $message): ?string
{
return null;
}




public function getStyle(string $name): OutputFormatterStyleInterface
{

return $this->style ?? $this->style = new NullOutputFormatterStyle();
}




public function hasStyle(string $name): bool
{
return false;
}




public function isDecorated(): bool
{
return false;
}




public function setDecorated(bool $decorated): void
{

}




public function setStyle(string $name, OutputFormatterStyleInterface $style): void
{

}
}
<?php










namespace Symfony\Component\Console\Formatter;

use Symfony\Component\Console\Exception\InvalidArgumentException;







class OutputFormatter implements WrappableOutputFormatterInterface
{
private bool $decorated;
private array $styles = [];
private $styleStack;

public function __clone()
{
$this->styleStack = clone $this->styleStack;
foreach ($this->styles as $key => $value) {
$this->styles[$key] = clone $value;
}
}




public static function escape(string $text): string
{
$text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text);

return self::escapeTrailingBackslash($text);
}






public static function escapeTrailingBackslash(string $text): string
{
if (str_ends_with($text, '\\')) {
$len = \strlen($text);
$text = rtrim($text, '\\');
$text = str_replace("\0", '', $text);
$text .= str_repeat("\0", $len - \strlen($text));
}

return $text;
}






public function __construct(bool $decorated = false, array $styles = [])
{
$this->decorated = $decorated;

$this->setStyle('error', new OutputFormatterStyle('white', 'red'));
$this->setStyle('info', new OutputFormatterStyle('green'));
$this->setStyle('comment', new OutputFormatterStyle('yellow'));
$this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));

foreach ($styles as $name => $style) {
$this->setStyle($name, $style);
}

$this->styleStack = new OutputFormatterStyleStack();
}




public function setDecorated(bool $decorated)
{
$this->decorated = $decorated;
}




public function isDecorated(): bool
{
return $this->decorated;
}




public function setStyle(string $name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
}




public function hasStyle(string $name): bool
{
return isset($this->styles[strtolower($name)]);
}




public function getStyle(string $name): OutputFormatterStyleInterface
{
if (!$this->hasStyle($name)) {
throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name));
}

return $this->styles[strtolower($name)];
}




public function format(?string $message): ?string
{
return $this->formatAndWrap($message, 0);
}




public function formatAndWrap(?string $message, int $width)
{
if (null === $message) {
return '';
}

$offset = 0;
$output = '';
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
$closeTagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];

if (0 != $pos && '\\' == $message[$pos - 1]) {
continue;
}


$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
$offset = $pos + \strlen($text);


if ($open = '/' != $text[1]) {
$tag = $matches[1][$i][0];
} else {
$tag = $matches[3][$i][0] ?? '';
}

if (!$open && !$tag) {

$this->styleStack->pop();
} elseif (null === $style = $this->createStyleFromString($tag)) {
$output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
} elseif ($open) {
$this->styleStack->push($style);
} else {
$this->styleStack->pop($style);
}
}

$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);

return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
}

public function getStyleStack(): OutputFormatterStyleStack
{
return $this->styleStack;
}




private function createStyleFromString(string $string): ?OutputFormatterStyleInterface
{
if (isset($this->styles[$string])) {
return $this->styles[$string];
}

if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) {
return null;
}

$style = new OutputFormatterStyle();
foreach ($matches as $match) {
array_shift($match);
$match[0] = strtolower($match[0]);

if ('fg' == $match[0]) {
$style->setForeground(strtolower($match[1]));
} elseif ('bg' == $match[0]) {
$style->setBackground(strtolower($match[1]));
} elseif ('href' === $match[0]) {
$url = preg_replace('{\\\\([<>])}', '$1', $match[1]);
$style->setHref($url);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
$options = array_shift($options);
foreach ($options as $option) {
$style->setOption($option);
}
} else {
return null;
}
}

return $style;
}




private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
{
if ('' === $text) {
return '';
}

if (!$width) {
return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
}

if (!$currentLineLength && '' !== $current) {
$text = ltrim($text);
}

if ($currentLineLength) {
$prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
$text = substr($text, $i);
} else {
$prefix = '';
}

preg_match('~(\\n)$~', $text, $matches);
$text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
$text = rtrim($text, "\n").($matches[1] ?? '');

if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
$text = "\n".$text;
}

$lines = explode("\n", $text);

foreach ($lines as $line) {
$currentLineLength += \strlen($line);
if ($width <= $currentLineLength) {
$currentLineLength = 0;
}
}

if ($this->isDecorated()) {
foreach ($lines as $i => $line) {
$lines[$i] = $this->styleStack->getCurrent()->apply($line);
}
}

return implode("\n", $lines);
}
}
<?php










namespace Symfony\Component\Console\Formatter;

use Symfony\Component\Console\Color;






class OutputFormatterStyle implements OutputFormatterStyleInterface
{
private $color;
private string $foreground;
private string $background;
private array $options;
private ?string $href = null;
private bool $handlesHrefGracefully;







public function __construct(string $foreground = null, string $background = null, array $options = [])
{
$this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options);
}




public function setForeground(string $color = null)
{
$this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options);
}




public function setBackground(string $color = null)
{
$this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options);
}

public function setHref(string $url): void
{
$this->href = $url;
}




public function setOption(string $option)
{
$this->options[] = $option;
$this->color = new Color($this->foreground, $this->background, $this->options);
}




public function unsetOption(string $option)
{
$pos = array_search($option, $this->options);
if (false !== $pos) {
unset($this->options[$pos]);
}

$this->color = new Color($this->foreground, $this->background, $this->options);
}




public function setOptions(array $options)
{
$this->color = new Color($this->foreground, $this->background, $this->options = $options);
}




public function apply(string $text): string
{
$this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);

if (null !== $this->href && $this->handlesHrefGracefully) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
}

return $this->color->apply($text);
}
}
<?php










namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;






final class ConsoleErrorEvent extends ConsoleEvent
{
private \Throwable $error;
private int $exitCode;

public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null)
{
parent::__construct($command, $input, $output);

$this->error = $error;
}

public function getError(): \Throwable
{
return $this->error;
}

public function setError(\Throwable $error): void
{
$this->error = $error;
}

public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;

$r = new \ReflectionProperty($this->error, 'code');
$r->setAccessible(true);
$r->setValue($this->error, $this->exitCode);
}

public function getExitCode(): int
{
return $this->exitCode ?? (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1);
}
}
<?php










namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;






final class ConsoleTerminateEvent extends ConsoleEvent
{
private int $exitCode;

public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $exitCode)
{
parent::__construct($command, $input, $output);

$this->setExitCode($exitCode);
}

public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
}

public function getExitCode(): int
{
return $this->exitCode;
}
}
<?php










namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;




final class ConsoleSignalEvent extends ConsoleEvent
{
private int $handlingSignal;

public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal)
{
parent::__construct($command, $input, $output);
$this->handlingSignal = $handlingSignal;
}

public function getHandlingSignal(): int
{
return $this->handlingSignal;
}
}
<?php










namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Contracts\EventDispatcher\Event;






class ConsoleEvent extends Event
{
protected $command;

private $input;
private $output;

public function __construct(?Command $command, InputInterface $input, OutputInterface $output)
{
$this->command = $command;
$this->input = $input;
$this->output = $output;
}




public function getCommand(): ?Command
{
return $this->command;
}




public function getInput(): InputInterface
{
return $this->input;
}




public function getOutput(): OutputInterface
{
return $this->output;
}
}
<?php










namespace Symfony\Component\Console\Event;






final class ConsoleCommandEvent extends ConsoleEvent
{



public const RETURN_CODE_DISABLED = 113;




private bool $commandShouldRun = true;




public function disableCommand(): bool
{
return $this->commandShouldRun = false;
}

public function enableCommand(): bool
{
return $this->commandShouldRun = true;
}




public function commandShouldRun(): bool
{
return $this->commandShouldRun;
}
}
<?php










namespace Symfony\Component\Console\EventListener;

use Psr\Log\LoggerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;





class ErrorListener implements EventSubscriberInterface
{
private $logger;

public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger;
}

public function onConsoleError(ConsoleErrorEvent $event)
{
if (null === $this->logger) {
return;
}

$error = $event->getError();

if (!$inputString = $this->getInputString($event)) {
$this->logger->critical('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]);

return;
}

$this->logger->critical('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]);
}

public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
if (null === $this->logger) {
return;
}

$exitCode = $event->getExitCode();

if (0 === $exitCode) {
return;
}

if (!$inputString = $this->getInputString($event)) {
$this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]);

return;
}

$this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]);
}

public static function getSubscribedEvents(): array
{
return [
ConsoleEvents::ERROR => ['onConsoleError', -128],
ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128],
];
}

private static function getInputString(ConsoleEvent $event): ?string
{
$commandName = $event->getCommand() ? $event->getCommand()->getName() : null;
$input = $event->getInput();

if ($input instanceof \Stringable) {
if ($commandName) {
return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input);
}

return (string) $input;
}

return $commandName;
}
}
<?php










namespace Symfony\Component\Console;

use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleSignalEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;






final class ConsoleEvents
{







public const COMMAND = 'console.command';







public const SIGNAL = 'console.signal';







public const TERMINATE = 'console.terminate';









public const ERROR = 'console.error';






public const ALIASES = [
ConsoleCommandEvent::class => self::COMMAND,
ConsoleErrorEvent::class => self::ERROR,
ConsoleSignalEvent::class => self::SIGNAL,
ConsoleTerminateEvent::class => self::TERMINATE,
];
}
<?php










namespace Symfony\Component\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\CompleteCommand;
use Symfony\Component\Console\Command\DumpCompletionCommand;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Command\SignalableCommandInterface;
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleSignalEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\NamespaceNotFoundException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SignalRegistry\SignalRegistry;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Service\ResetInterface;
















class Application implements ResetInterface
{
private array $commands = [];
private bool $wantHelps = false;
private $runningCommand = null;
private string $name;
private string $version;
private $commandLoader = null;
private bool $catchExceptions = true;
private bool $autoExit = true;
private $definition;
private $helperSet;
private $dispatcher = null;
private $terminal;
private string $defaultCommand;
private bool $singleCommand = false;
private bool $initialized = false;
private $signalRegistry;
private array $signalsToDispatchEvent = [];

public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
{
$this->name = $name;
$this->version = $version;
$this->terminal = new Terminal();
$this->defaultCommand = 'list';
if (\defined('SIGINT') && SignalRegistry::isSupported()) {
$this->signalRegistry = new SignalRegistry();
$this->signalsToDispatchEvent = [\SIGINT, \SIGTERM, \SIGUSR1, \SIGUSR2];
}
}




public function setDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}

public function setCommandLoader(CommandLoaderInterface $commandLoader)
{
$this->commandLoader = $commandLoader;
}

public function getSignalRegistry(): SignalRegistry
{
if (!$this->signalRegistry) {
throw new RuntimeException('Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}

return $this->signalRegistry;
}

public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent)
{
$this->signalsToDispatchEvent = $signalsToDispatchEvent;
}








public function run(InputInterface $input = null, OutputInterface $output = null): int
{
if (\function_exists('putenv')) {
@putenv('LINES='.$this->terminal->getHeight());
@putenv('COLUMNS='.$this->terminal->getWidth());
}

if (null === $input) {
$input = new ArgvInput();
}

if (null === $output) {
$output = new ConsoleOutput();
}

$renderException = function (\Throwable $e) use ($output) {
if ($output instanceof ConsoleOutputInterface) {
$this->renderThrowable($e, $output->getErrorOutput());
} else {
$this->renderThrowable($e, $output);
}
};
if ($phpHandler = set_exception_handler($renderException)) {
restore_exception_handler();
if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
$errorHandler = true;
} elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
$phpHandler[0]->setExceptionHandler($errorHandler);
}
}

$this->configureIO($input, $output);

try {
$exitCode = $this->doRun($input, $output);
} catch (\Exception $e) {
if (!$this->catchExceptions) {
throw $e;
}

$renderException($e);

$exitCode = $e->getCode();
if (is_numeric($exitCode)) {
$exitCode = (int) $exitCode;
if ($exitCode <= 0) {
$exitCode = 1;
}
} else {
$exitCode = 1;
}
} finally {


if (!$phpHandler) {
if (set_exception_handler($renderException) === $renderException) {
restore_exception_handler();
}
restore_exception_handler();
} elseif (!$errorHandler) {
$finalHandler = $phpHandler[0]->setExceptionHandler(null);
if ($finalHandler !== $renderException) {
$phpHandler[0]->setExceptionHandler($finalHandler);
}
}
}

if ($this->autoExit) {
if ($exitCode > 255) {
$exitCode = 255;
}

exit($exitCode);
}

return $exitCode;
}






public function doRun(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(['--version', '-V'], true)) {
$output->writeln($this->getLongVersion());

return 0;
}

try {

$input->bind($this->getDefinition());
} catch (ExceptionInterface $e) {

}

$name = $this->getCommandName($input);
if (true === $input->hasParameterOption(['--help', '-h'], true)) {
if (!$name) {
$name = 'help';
$input = new ArrayInput(['command_name' => $this->defaultCommand]);
} else {
$this->wantHelps = true;
}
}

if (!$name) {
$name = $this->defaultCommand;
$definition = $this->getDefinition();
$definition->setArguments(array_merge(
$definition->getArguments(),
[
'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
]
));
}

try {
$this->runningCommand = null;

$command = $this->find($name);
} catch (\Throwable $e) {
if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);

if (0 === $event->getExitCode()) {
return 0;
}

$e = $event->getError();
}

throw $e;
}

$alternative = $alternatives[0];

$style = new SymfonyStyle($input, $output);
$output->writeln('');
$formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
$output->writeln($formattedBlock);
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);

return $event->getExitCode();
}

return 1;
}

$command = $this->find($alternative);
}

if ($command instanceof LazyCommand) {
$command = $command->getCommand();
}

$this->runningCommand = $command;
$exitCode = $this->doRunCommand($command, $input, $output);
$this->runningCommand = null;

return $exitCode;
}




public function reset()
{
}

public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}




public function getHelperSet(): HelperSet
{
return $this->helperSet ??= $this->getDefaultHelperSet();
}

public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
}




public function getDefinition(): InputDefinition
{
$this->definition ??= $this->getDefaultInputDefinition();

if ($this->singleCommand) {
$inputDefinition = $this->definition;
$inputDefinition->setArguments();

return $inputDefinition;
}

return $this->definition;
}




public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if (
CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType()
&& 'command' === $input->getCompletionName()
) {
$commandNames = [];
foreach ($this->all() as $name => $command) {

if ($command->isHidden() || $command->getName() !== $name) {
continue;
}
$commandNames[] = $command->getName();
foreach ($command->getAliases() as $name) {
$commandNames[] = $name;
}
}
$suggestions->suggestValues(array_filter($commandNames));

return;
}

if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) {
$suggestions->suggestOptions($this->getDefinition()->getOptions());

return;
}
}




public function getHelp(): string
{
return $this->getLongVersion();
}




public function areExceptionsCaught(): bool
{
return $this->catchExceptions;
}




public function setCatchExceptions(bool $boolean)
{
$this->catchExceptions = $boolean;
}




public function isAutoExitEnabled(): bool
{
return $this->autoExit;
}




public function setAutoExit(bool $boolean)
{
$this->autoExit = $boolean;
}




public function getName(): string
{
return $this->name;
}




public function setName(string $name)
{
$this->name = $name;
}




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




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






public function getLongVersion()
{
if ('UNKNOWN' !== $this->getName()) {
if ('UNKNOWN' !== $this->getVersion()) {
return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
}

return $this->getName();
}

return 'Console Tool';
}




public function register(string $name): Command
{
return $this->add(new Command($name));
}








public function addCommands(array $commands)
{
foreach ($commands as $command) {
$this->add($command);
}
}









public function add(Command $command)
{
$this->init();

$command->setApplication($this);

if (!$command->isEnabled()) {
$command->setApplication(null);

return null;
}

if (!$command instanceof LazyCommand) {

$command->getDefinition();
}

if (!$command->getName()) {
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
}

$this->commands[$command->getName()] = $command;

foreach ($command->getAliases() as $alias) {
$this->commands[$alias] = $command;
}

return $command;
}








public function get(string $name)
{
$this->init();

if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}


if (!isset($this->commands[$name])) {
throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name));
}

$command = $this->commands[$name];

if ($this->wantHelps) {
$this->wantHelps = false;

$helpCommand = $this->get('help');
$helpCommand->setCommand($command);

return $helpCommand;
}

return $command;
}




public function has(string $name): bool
{
$this->init();

return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name)));
}








public function getNamespaces(): array
{
$namespaces = [];
foreach ($this->all() as $command) {
if ($command->isHidden()) {
continue;
}

$namespaces[] = $this->extractAllNamespaces($command->getName());

foreach ($command->getAliases() as $alias) {
$namespaces[] = $this->extractAllNamespaces($alias);
}
}

return array_values(array_unique(array_filter(array_merge([], ...$namespaces))));
}






public function findNamespace(string $namespace): string
{
$allNamespaces = $this->getNamespaces();
$expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*';
$namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);

if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);

if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n    ";
} else {
$message .= "\n\nDid you mean one of these?\n    ";
}

$message .= implode("\n    ", $alternatives);
}

throw new NamespaceNotFoundException($message, $alternatives);
}

$exact = \in_array($namespace, $namespaces, true);
if (\count($namespaces) > 1 && !$exact) {
throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
}

return $exact ? $namespace : reset($namespaces);
}











public function find(string $name)
{
$this->init();

$aliases = [];

foreach ($this->commands as $command) {
foreach ($command->getAliases() as $alias) {
if (!$this->has($alias)) {
$this->commands[$alias] = $command;
}
}
}

if ($this->has($name)) {
return $this->get($name);
}

$allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
$expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*';
$commands = preg_grep('{^'.$expr.'}', $allCommands);

if (empty($commands)) {
$commands = preg_grep('{^'.$expr.'}i', $allCommands);
}


if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) {

$this->findNamespace(substr($name, 0, $pos));
}

$message = sprintf('Command "%s" is not defined.', $name);

if ($alternatives = $this->findAlternatives($name, $allCommands)) {

$alternatives = array_filter($alternatives, function ($name) {
return !$this->get($name)->isHidden();
});

if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n    ";
} else {
$message .= "\n\nDid you mean one of these?\n    ";
}
$message .= implode("\n    ", $alternatives);
}

throw new CommandNotFoundException($message, array_values($alternatives));
}


if (\count($commands) > 1) {
$commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
$commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) {
if (!$commandList[$nameOrAlias] instanceof Command) {
$commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias);
}

$commandName = $commandList[$nameOrAlias]->getName();

$aliases[$nameOrAlias] = $commandName;

return $commandName === $nameOrAlias || !\in_array($commandName, $commands);
}));
}

if (\count($commands) > 1) {
$usableWidth = $this->terminal->getWidth() - 10;
$abbrevs = array_values($commands);
$maxLen = 0;
foreach ($abbrevs as $abbrev) {
$maxLen = max(Helper::width($abbrev), $maxLen);
}
$abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) {
if ($commandList[$cmd]->isHidden()) {
unset($commands[array_search($cmd, $commands)]);

return false;
}

$abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();

return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
}, array_values($commands));

if (\count($commands) > 1) {
$suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));

throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands));
}
}

$command = $this->get(reset($commands));

if ($command->isHidden()) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}

return $command;
}








public function all(string $namespace = null)
{
$this->init();

if (null === $namespace) {
if (!$this->commandLoader) {
return $this->commands;
}

$commands = $this->commands;
foreach ($this->commandLoader->getNames() as $name) {
if (!isset($commands[$name]) && $this->has($name)) {
$commands[$name] = $this->get($name);
}
}

return $commands;
}

$commands = [];
foreach ($this->commands as $name => $command) {
if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
$commands[$name] = $command;
}
}

if ($this->commandLoader) {
foreach ($this->commandLoader->getNames() as $name) {
if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) {
$commands[$name] = $this->get($name);
}
}
}

return $commands;
}






public static function getAbbreviations(array $names): array
{
$abbrevs = [];
foreach ($names as $name) {
for ($len = \strlen($name); $len > 0; --$len) {
$abbrev = substr($name, 0, $len);
$abbrevs[$abbrev][] = $name;
}
}

return $abbrevs;
}

public function renderThrowable(\Throwable $e, OutputInterface $output): void
{
$output->writeln('', OutputInterface::VERBOSITY_QUIET);

$this->doRenderThrowable($e, $output);

if (null !== $this->runningCommand) {
$output->writeln(sprintf('<info>%s</info>', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
}

protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void
{
do {
$message = trim($e->getMessage());
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$class = get_debug_type($e);
$title = sprintf('  [%s%s]  ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
$len = Helper::width($title);
} else {
$len = 0;
}

if (str_contains($message, "@anonymous\0")) {
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $message);
}

$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX;
$lines = [];
foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) {
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {

$lineLength = Helper::width($line) + 4;
$lines[] = [$line, $lineLength];

$len = max($lineLength, $len);
}
}

$messages = [];
if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
}
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::width($title))));
}
foreach ($lines as $line) {
$messages[] = sprintf('<error>  %s  %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
}
$messages[] = $emptyLine;
$messages[] = '';

$output->writeln($messages, OutputInterface::VERBOSITY_QUIET);

if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);


$trace = $e->getTrace();

array_unshift($trace, [
'function' => '',
'file' => $e->getFile() ?: 'n/a',
'line' => $e->getLine() ?: 'n/a',
'args' => [],
]);

for ($i = 0, $count = \count($trace); $i < $count; ++$i) {
$class = $trace[$i]['class'] ?? '';
$type = $trace[$i]['type'] ?? '';
$function = $trace[$i]['function'] ?? '';
$file = $trace[$i]['file'] ?? 'n/a';
$line = $trace[$i]['line'] ?? 'n/a';

$output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
}

$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
} while ($e = $e->getPrevious());
}




protected function configureIO(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(['--ansi'], true)) {
$output->setDecorated(true);
} elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
$output->setDecorated(false);
}

if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
$input->setInteractive(false);
}

switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
case -1:
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
break;
case 1:
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
break;
case 2:
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
break;
case 3:
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
break;
default:
$shellVerbosity = 0;
break;
}

if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
$shellVerbosity = -1;
} else {
if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
$shellVerbosity = 3;
} elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
$shellVerbosity = 2;
} elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
$shellVerbosity = 1;
}
}

if (-1 === $shellVerbosity) {
$input->setInteractive(false);
}

if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY='.$shellVerbosity);
}
$_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
}









protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
foreach ($command->getHelperSet() as $helper) {
if ($helper instanceof InputAwareInterface) {
$helper->setInput($input);
}
}

if ($this->signalsToDispatchEvent) {
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];

if ($commandSignals || null !== $this->dispatcher) {
if (!$this->signalRegistry) {
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}

if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');

foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
shell_exec('stty '.$sttyMode);
});
}
}
}

if (null !== $this->dispatcher) {
foreach ($this->signalsToDispatchEvent as $signal) {
$event = new ConsoleSignalEvent($command, $input, $output, $signal);

$this->signalRegistry->register($signal, function ($signal, $hasNext) use ($event) {
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);


if (!$hasNext) {
if (!\in_array($signal, [\SIGUSR1, \SIGUSR2], true)) {
exit(0);
}
}
});
}
}

foreach ($commandSignals as $signal) {
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
}
}

if (null === $this->dispatcher) {
return $command->run($input, $output);
}


try {
$command->mergeApplicationDefinition();
$input->bind($command->getDefinition());
} catch (ExceptionInterface $e) {

}

$event = new ConsoleCommandEvent($command, $input, $output);
$e = null;

try {
$this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);

if ($event->commandShouldRun()) {
$exitCode = $command->run($input, $output);
} else {
$exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
}
} catch (\Throwable $e) {
$event = new ConsoleErrorEvent($input, $output, $e, $command);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
$e = $event->getError();

if (0 === $exitCode = $event->getExitCode()) {
$e = null;
}
}

$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
$this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);

if (null !== $e) {
throw $e;
}

return $event->getExitCode();
}




protected function getCommandName(InputInterface $input): ?string
{
return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
}




protected function getDefaultInputDefinition(): InputDefinition
{
return new InputDefinition([
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the <info>'.$this->defaultCommand.'</info> command'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
]);
}






protected function getDefaultCommands(): array
{
return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()];
}




protected function getDefaultHelperSet(): HelperSet
{
return new HelperSet([
new FormatterHelper(),
new DebugFormatterHelper(),
new ProcessHelper(),
new QuestionHelper(),
]);
}




private function getAbbreviationSuggestions(array $abbrevs): string
{
return '    '.implode("\n    ", $abbrevs);
}






public function extractNamespace(string $name, int $limit = null): string
{
$parts = explode(':', $name, -1);

return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit));
}







private function findAlternatives(string $name, iterable $collection): array
{
$threshold = 1e3;
$alternatives = [];

$collectionParts = [];
foreach ($collection as $item) {
$collectionParts[$item] = explode(':', $item);
}

foreach (explode(':', $name) as $i => $subname) {
foreach ($collectionParts as $collectionName => $parts) {
$exists = isset($alternatives[$collectionName]);
if (!isset($parts[$i]) && $exists) {
$alternatives[$collectionName] += $threshold;
continue;
} elseif (!isset($parts[$i])) {
continue;
}

$lev = levenshtein($subname, $parts[$i]);
if ($lev <= \strlen($subname) / 3 || '' !== $subname && str_contains($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
} elseif ($exists) {
$alternatives[$collectionName] += $threshold;
}
}
}

foreach ($collection as $item) {
$lev = levenshtein($name, $item);
if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) {
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
}
}

$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE);

return array_keys($alternatives);
}






public function setDefaultCommand(string $commandName, bool $isSingleCommand = false): static
{
$this->defaultCommand = explode('|', ltrim($commandName, '|'))[0];

if ($isSingleCommand) {

$this->find($commandName);

$this->singleCommand = true;
}

return $this;
}




public function isSingleCommand(): bool
{
return $this->singleCommand;
}

private function splitStringByWidth(string $string, int $width): array
{



if (false === $encoding = mb_detect_encoding($string, null, true)) {
return str_split($string, $width);
}

$utf8String = mb_convert_encoding($string, 'utf8', $encoding);
$lines = [];
$line = '';

$offset = 0;
while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) {
$offset += \strlen($m[0]);

foreach (preg_split('//u', $m[0]) as $char) {

if (mb_strwidth($line.$char, 'utf8') <= $width) {
$line .= $char;
continue;
}

$lines[] = str_pad($line, $width);
$line = $char;
}
}

$lines[] = \count($lines) ? str_pad($line, $width) : $line;

mb_convert_variables($encoding, 'utf8', $lines);

return $lines;
}






private function extractAllNamespaces(string $name): array
{

$parts = explode(':', $name, -1);
$namespaces = [];

foreach ($parts as $part) {
if (\count($namespaces)) {
$namespaces[] = end($namespaces).':'.$part;
} else {
$namespaces[] = $part;
}
}

return $namespaces;
}

private function init()
{
if ($this->initialized) {
return;
}
$this->initialized = true;

foreach ($this->getDefaultCommands() as $command) {
$this->add($command);
}
}
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Output\OutputInterface;






interface DescriptorInterface
{
public function describe(OutputInterface $output, object $object, array $options = []);
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






abstract class Descriptor implements DescriptorInterface
{



protected $output;




public function describe(OutputInterface $output, object $object, array $options = [])
{
$this->output = $output;

switch (true) {
case $object instanceof InputArgument:
$this->describeInputArgument($object, $options);
break;
case $object instanceof InputOption:
$this->describeInputOption($object, $options);
break;
case $object instanceof InputDefinition:
$this->describeInputDefinition($object, $options);
break;
case $object instanceof Command:
$this->describeCommand($object, $options);
break;
case $object instanceof Application:
$this->describeApplication($object, $options);
break;
default:
throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object)));
}
}




protected function write(string $content, bool $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}




abstract protected function describeInputArgument(InputArgument $argument, array $options = []);




abstract protected function describeInputOption(InputOption $option, array $options = []);




abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []);




abstract protected function describeCommand(Command $command, array $options = []);




abstract protected function describeApplication(Application $application, array $options = []);
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;








class TextDescriptor extends Descriptor
{



protected function describeInputArgument(InputArgument $argument, array $options = [])
{
if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
} else {
$default = '';
}

$totalWidth = $options['total_width'] ?? Helper::width($argument->getName());
$spacingWidth = $totalWidth - \strlen($argument->getName());

$this->writeText(sprintf('  <info>%s</info>  %s%s%s',
$argument->getName(),
str_repeat(' ', $spacingWidth),

preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()),
$default
), $options);
}




protected function describeInputOption(InputOption $option, array $options = [])
{
if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
} else {
$default = '';
}

$value = '';
if ($option->acceptValue()) {
$value = '='.strtoupper($option->getName());

if ($option->isValueOptional()) {
$value = '['.$value.']';
}
}

$totalWidth = $options['total_width'] ?? $this->calculateTotalWidthForOptions([$option]);
$synopsis = sprintf('%s%s',
$option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : '    ',
sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value)
);

$spacingWidth = $totalWidth - Helper::width($synopsis);

$this->writeText(sprintf('  <info>%s</info>  %s%s%s%s',
$synopsis,
str_repeat(' ', $spacingWidth),

preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()),
$default,
$option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
), $options);
}




protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
$totalWidth = max($totalWidth, Helper::width($argument->getName()));
}

if ($definition->getArguments()) {
$this->writeText('<comment>Arguments:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getArguments() as $argument) {
$this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
$this->writeText("\n");
}
}

if ($definition->getArguments() && $definition->getOptions()) {
$this->writeText("\n");
}

if ($definition->getOptions()) {
$laterOptions = [];

$this->writeText('<comment>Options:</comment>', $options);
foreach ($definition->getOptions() as $option) {
if (\strlen($option->getShortcut() ?? '') > 1) {
$laterOptions[] = $option;
continue;
}
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
foreach ($laterOptions as $option) {
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
}
}




protected function describeCommand(Command $command, array $options = [])
{
$command->mergeApplicationDefinition(false);

if ($description = $command->getDescription()) {
$this->writeText('<comment>Description:</comment>', $options);
$this->writeText("\n");
$this->writeText('  '.$description);
$this->writeText("\n\n");
}

$this->writeText('<comment>Usage:</comment>', $options);
foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
$this->writeText("\n");
$this->writeText('  '.OutputFormatter::escape($usage), $options);
}
$this->writeText("\n");

$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
$this->writeText("\n");
}

$help = $command->getProcessedHelp();
if ($help && $help !== $description) {
$this->writeText("\n");
$this->writeText('<comment>Help:</comment>', $options);
$this->writeText("\n");
$this->writeText('  '.str_replace("\n", "\n  ", $help), $options);
$this->writeText("\n");
}
}




protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);

if (isset($options['raw_text']) && $options['raw_text']) {
$width = $this->getColumnWidth($description->getCommands());

foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText("\n");
}
} else {
if ('' != $help = $application->getHelp()) {
$this->writeText("$help\n\n", $options);
}

$this->writeText("<comment>Usage:</comment>\n", $options);
$this->writeText("  command [options] [arguments]\n\n", $options);

$this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);

$this->writeText("\n");
$this->writeText("\n");

$commands = $description->getCommands();
$namespaces = $description->getNamespaces();
if ($describedNamespace && $namespaces) {

$describedNamespaceInfo = reset($namespaces);
foreach ($describedNamespaceInfo['commands'] as $name) {
$commands[$name] = $description->getCommand($name);
}
}


$width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) {
return array_intersect($namespace['commands'], array_keys($commands));
}, array_values($namespaces)))));

if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}

foreach ($namespaces as $namespace) {
$namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) {
return isset($commands[$name]);
});

if (!$namespace['commands']) {
continue;
}

if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->writeText("\n");
$this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
}

foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$spacingWidth = $width - Helper::width($name);
$command = $commands[$name];
$commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : '';
$this->writeText(sprintf('  <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options);
}
}

$this->writeText("\n");
}
}




private function writeText(string $content, array $options = [])
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
isset($options['raw_output']) ? !$options['raw_output'] : true
);
}




private function getCommandAliasesText(Command $command): string
{
$text = '';
$aliases = $command->getAliases();

if ($aliases) {
$text = '['.implode('|', $aliases).'] ';
}

return $text;
}




private function formatDefaultValue(mixed $default): string
{
if (\INF === $default) {
return 'INF';
}

if (\is_string($default)) {
$default = OutputFormatter::escape($default);
} elseif (\is_array($default)) {
foreach ($default as $key => $value) {
if (\is_string($value)) {
$default[$key] = OutputFormatter::escape($value);
}
}
}

return str_replace('\\\\', '\\', json_encode($default, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
}




private function getColumnWidth(array $commands): int
{
$widths = [];

foreach ($commands as $command) {
if ($command instanceof Command) {
$widths[] = Helper::width($command->getName());
foreach ($command->getAliases() as $alias) {
$widths[] = Helper::width($alias);
}
} else {
$widths[] = Helper::width($command);
}
}

return $widths ? max($widths) + 2 : 0;
}




private function calculateTotalWidthForOptions(array $options): int
{
$totalWidth = 0;
foreach ($options as $option) {

$nameLength = 1 + max(Helper::width($option->getShortcut()), 1) + 4 + Helper::width($option->getName());
if ($option->isNegatable()) {
$nameLength += 6 + Helper::width($option->getName()); 
} elseif ($option->acceptValue()) {
$valueLength = 1 + Helper::width($option->getName()); 
$valueLength += $option->isValueOptional() ? 2 : 0; 

$nameLength += $valueLength;
}
$totalWidth = max($totalWidth, $nameLength);
}

return $totalWidth;
}
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;








class XmlDescriptor extends Descriptor
{
public function getInputDefinitionDocument(InputDefinition $definition): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($definitionXML = $dom->createElement('definition'));

$definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
foreach ($definition->getArguments() as $argument) {
$this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
}

$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($definition->getOptions() as $option) {
$this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
}

return $dom;
}

public function getCommandDocument(Command $command, bool $short = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($commandXML = $dom->createElement('command'));

$commandXML->setAttribute('id', $command->getName());
$commandXML->setAttribute('name', $command->getName());
$commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0);

$commandXML->appendChild($usagesXML = $dom->createElement('usages'));

$commandXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));

if ($short) {
foreach ($command->getAliases() as $usage) {
$usagesXML->appendChild($dom->createElement('usage', $usage));
}
} else {
$command->mergeApplicationDefinition(false);

foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) {
$usagesXML->appendChild($dom->createElement('usage', $usage));
}

$commandXML->appendChild($helpXML = $dom->createElement('help'));
$helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));

$definitionXML = $this->getInputDefinitionDocument($command->getDefinition());
$this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
}

return $dom;
}

public function getApplicationDocument(Application $application, string $namespace = null, bool $short = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($rootXml = $dom->createElement('symfony'));

if ('UNKNOWN' !== $application->getName()) {
$rootXml->setAttribute('name', $application->getName());
if ('UNKNOWN' !== $application->getVersion()) {
$rootXml->setAttribute('version', $application->getVersion());
}
}

$rootXml->appendChild($commandsXML = $dom->createElement('commands'));

$description = new ApplicationDescription($application, $namespace, true);

if ($namespace) {
$commandsXML->setAttribute('namespace', $namespace);
}

foreach ($description->getCommands() as $command) {
$this->appendDocument($commandsXML, $this->getCommandDocument($command, $short));
}

if (!$namespace) {
$rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));

foreach ($description->getNamespaces() as $namespaceDescription) {
$namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
$namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);

foreach ($namespaceDescription['commands'] as $name) {
$namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
$commandXML->appendChild($dom->createTextNode($name));
}
}
}

return $dom;
}




protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->writeDocument($this->getInputArgumentDocument($argument));
}




protected function describeInputOption(InputOption $option, array $options = [])
{
$this->writeDocument($this->getInputOptionDocument($option));
}




protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$this->writeDocument($this->getInputDefinitionDocument($definition));
}




protected function describeCommand(Command $command, array $options = [])
{
$this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false));
}




protected function describeApplication(Application $application, array $options = [])
{
$this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false));
}




private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
{
foreach ($importedParent->childNodes as $childNode) {
$parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
}
}




private function writeDocument(\DOMDocument $dom)
{
$dom->formatOutput = true;
$this->write($dom->saveXML());
}

private function getInputArgumentDocument(InputArgument $argument): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');

$dom->appendChild($objectXML = $dom->createElement('argument'));
$objectXML->setAttribute('name', $argument->getName());
$objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
$objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));

$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : []));
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}

return $dom;
}

private function getInputOptionDocument(InputOption $option): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');

$dom->appendChild($objectXML = $dom->createElement('option'));
$objectXML->setAttribute('name', '--'.$option->getName());
$pos = strpos($option->getShortcut() ?? '', '|');
if (false !== $pos) {
$objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
$objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut()));
} else {
$objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
}
$objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
$objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
$objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));

if ($option->acceptValue()) {
$defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : []));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));

if (!empty($defaults)) {
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
}

if ($option->isNegatable()) {
$dom->appendChild($objectXML = $dom->createElement('option'));
$objectXML->setAttribute('name', '--no-'.$option->getName());
$objectXML->setAttribute('shortcut', '');
$objectXML->setAttribute('accept_value', 0);
$objectXML->setAttribute('is_value_required', 0);
$objectXML->setAttribute('is_multiple', 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode('Negate the "--'.$option->getName().'" option'));
}

return $dom;
}
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;








class MarkdownDescriptor extends Descriptor
{



public function describe(OutputInterface $output, object $object, array $options = [])
{
$decorated = $output->isDecorated();
$output->setDecorated(false);

parent::describe($output, $object, $options);

$output->setDecorated($decorated);
}




protected function write(string $content, bool $decorated = true)
{
parent::write($content, $decorated);
}




protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->write(
'#### `'.($argument->getName() ?: '<none>')."`\n\n"
.($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
.'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
.'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
);
}




protected function describeInputOption(InputOption $option, array $options = [])
{
$name = '--'.$option->getName();
if ($option->isNegatable()) {
$name .= '|--no-'.$option->getName();
}
if ($option->getShortcut()) {
$name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
}

$this->write(
'#### `'.$name.'`'."\n\n"
.($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
.'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
.'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
);
}




protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
if ($showArguments = \count($definition->getArguments()) > 0) {
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
if (null !== $describeInputArgument = $this->describeInputArgument($argument)) {
$this->write($describeInputArgument);
}
}
}

if (\count($definition->getOptions()) > 0) {
if ($showArguments) {
$this->write("\n\n");
}

$this->write('### Options');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
if (null !== $describeInputOption = $this->describeInputOption($option)) {
$this->write($describeInputOption);
}
}
}
}




protected function describeCommand(Command $command, array $options = [])
{
if ($options['short'] ?? false) {
$this->write(
'`'.$command->getName()."`\n"
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce($command->getAliases(), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
);

return;
}

$command->mergeApplicationDefinition(false);

$this->write(
'`'.$command->getName()."`\n"
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
);

if ($help = $command->getProcessedHelp()) {
$this->write("\n");
$this->write($help);
}

$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->write("\n\n");
$this->describeInputDefinition($definition);
}
}




protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
$title = $this->getApplicationTitle($application);

$this->write($title."\n".str_repeat('=', Helper::width($title)));

foreach ($description->getNamespaces() as $namespace) {
if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->write("\n\n");
$this->write('**'.$namespace['id'].':**');
}

$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) use ($description) {
return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName()));
}, $namespace['commands'])));
}

foreach ($description->getCommands() as $command) {
$this->write("\n\n");
if (null !== $describeCommand = $this->describeCommand($command, $options)) {
$this->write($describeCommand);
}
}
}

private function getApplicationTitle(Application $application): string
{
if ('UNKNOWN' !== $application->getName()) {
if ('UNKNOWN' !== $application->getVersion()) {
return sprintf('%s %s', $application->getName(), $application->getVersion());
}

return $application->getName();
}

return 'Console Tool';
}
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;








class JsonDescriptor extends Descriptor
{



protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->writeData($this->getInputArgumentData($argument), $options);
}




protected function describeInputOption(InputOption $option, array $options = [])
{
$this->writeData($this->getInputOptionData($option), $options);
if ($option->isNegatable()) {
$this->writeData($this->getInputOptionData($option, true), $options);
}
}




protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$this->writeData($this->getInputDefinitionData($definition), $options);
}




protected function describeCommand(Command $command, array $options = [])
{
$this->writeData($this->getCommandData($command, $options['short'] ?? false), $options);
}




protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace, true);
$commands = [];

foreach ($description->getCommands() as $command) {
$commands[] = $this->getCommandData($command, $options['short'] ?? false);
}

$data = [];
if ('UNKNOWN' !== $application->getName()) {
$data['application']['name'] = $application->getName();
if ('UNKNOWN' !== $application->getVersion()) {
$data['application']['version'] = $application->getVersion();
}
}

$data['commands'] = $commands;

if ($describedNamespace) {
$data['namespace'] = $describedNamespace;
} else {
$data['namespaces'] = array_values($description->getNamespaces());
}

$this->writeData($data, $options);
}




private function writeData(array $data, array $options)
{
$flags = $options['json_encoding'] ?? 0;

$this->write(json_encode($data, $flags));
}

private function getInputArgumentData(InputArgument $argument): array
{
return [
'name' => $argument->getName(),
'is_required' => $argument->isRequired(),
'is_array' => $argument->isArray(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()),
'default' => \INF === $argument->getDefault() ? 'INF' : $argument->getDefault(),
];
}

private function getInputOptionData(InputOption $option, bool $negated = false): array
{
return $negated ? [
'name' => '--no-'.$option->getName(),
'shortcut' => '',
'accept_value' => false,
'is_value_required' => false,
'is_multiple' => false,
'description' => 'Negate the "--'.$option->getName().'" option',
'default' => false,
] : [
'name' => '--'.$option->getName(),
'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '',
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(),
];
}

private function getInputDefinitionData(InputDefinition $definition): array
{
$inputArguments = [];
foreach ($definition->getArguments() as $name => $argument) {
$inputArguments[$name] = $this->getInputArgumentData($argument);
}

$inputOptions = [];
foreach ($definition->getOptions() as $name => $option) {
$inputOptions[$name] = $this->getInputOptionData($option);
if ($option->isNegatable()) {
$inputOptions['no-'.$name] = $this->getInputOptionData($option, true);
}
}

return ['arguments' => $inputArguments, 'options' => $inputOptions];
}

private function getCommandData(Command $command, bool $short = false): array
{
$data = [
'name' => $command->getName(),
'description' => $command->getDescription(),
];

if ($short) {
$data += [
'usage' => $command->getAliases(),
];
} else {
$command->mergeApplicationDefinition(false);

$data += [
'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()),
'help' => $command->getProcessedHelp(),
'definition' => $this->getInputDefinitionData($command->getDefinition()),
];
}

$data['hidden'] = $command->isHidden();

return $data;
}
}
<?php










namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;






class ApplicationDescription
{
public const GLOBAL_NAMESPACE = '_global';

private $application;
private ?string $namespace;
private bool $showHidden;
private array $namespaces;




private array $commands;




private array $aliases = [];

public function __construct(Application $application, string $namespace = null, bool $showHidden = false)
{
$this->application = $application;
$this->namespace = $namespace;
$this->showHidden = $showHidden;
}

public function getNamespaces(): array
{
if (!isset($this->namespaces)) {
$this->inspectApplication();
}

return $this->namespaces;
}




public function getCommands(): array
{
if (!isset($this->commands)) {
$this->inspectApplication();
}

return $this->commands;
}




public function getCommand(string $name): Command
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}

return $this->commands[$name] ?? $this->aliases[$name];
}

private function inspectApplication()
{
$this->commands = [];
$this->namespaces = [];

$all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
foreach ($this->sortCommands($all) as $namespace => $commands) {
$names = [];


foreach ($commands as $name => $command) {
if (!$command->getName() || (!$this->showHidden && $command->isHidden())) {
continue;
}

if ($command->getName() === $name) {
$this->commands[$name] = $command;
} else {
$this->aliases[$name] = $command;
}

$names[] = $name;
}

$this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
}
}

private function sortCommands(array $commands): array
{
$namespacedCommands = [];
$globalCommands = [];
$sortedCommands = [];
foreach ($commands as $name => $command) {
$key = $this->application->extractNamespace($name, 1);
if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) {
$globalCommands[$name] = $command;
} else {
$namespacedCommands[$key][$name] = $command;
}
}

if ($globalCommands) {
ksort($globalCommands);
$sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands;
}

if ($namespacedCommands) {
ksort($namespacedCommands, \SORT_STRING);
foreach ($namespacedCommands as $key => $commandsSet) {
ksort($commandsSet);
$sortedCommands[$key] = $commandsSet;
}
}

return $sortedCommands;
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\SemaphoreStore;






trait LockableTrait
{
private $lock = null;




private function lock(string $name = null, bool $blocking = false): bool
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');
}

if (null !== $this->lock) {
throw new LogicException('A lock is already in place.');
}

if (SemaphoreStore::isSupported()) {
$store = new SemaphoreStore();
} else {
$store = new FlockStore();
}

$this->lock = (new LockFactory($store))->createLock($name ?: $this->getName());
if (!$this->lock->acquire($blocking)) {
$this->lock = null;

return false;
}

return true;
}




private function release()
{
if ($this->lock) {
$this->lock->release();
$this->lock = null;
}
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Completion\Output\BashCompletionOutput;
use Symfony\Component\Console\Completion\Output\CompletionOutputInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






final class CompleteCommand extends Command
{
protected static $defaultName = '|_complete';
protected static $defaultDescription = 'Internal command to provide shell completion suggestions';

private $completionOutputs;

private $isDebug = false;




public function __construct(array $completionOutputs = [])
{

$this->completionOutputs = $completionOutputs + ['bash' => BashCompletionOutput::class];

parent::__construct();
}

protected function configure(): void
{
$this
->addOption('shell', 's', InputOption::VALUE_REQUIRED, 'The shell type ("'.implode('", "', array_keys($this->completionOutputs)).'")')
->addOption('input', 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'An array of input tokens (e.g. COMP_WORDS or argv)')
->addOption('current', 'c', InputOption::VALUE_REQUIRED, 'The index of the "input" array that the cursor is in (e.g. COMP_CWORD)')
->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'The version of the completion script')
;
}

protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOLEAN);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
try {











$shell = $input->getOption('shell');
if (!$shell) {
throw new \RuntimeException('The "--shell" option must be set.');
}

if (!$completionOutput = $this->completionOutputs[$shell] ?? false) {
throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs))));
}

$completionInput = $this->createCompletionInput($input);
$suggestions = new CompletionSuggestions();

$this->log([
'',
'<comment>'.date('Y-m-d H:i:s').'</>',
'<info>Input:</> <comment>("|" indicates the cursor position)</>',
'  '.(string) $completionInput,
'<info>Command:</>',
'  '.(string) implode(' ', $_SERVER['argv']),
'<info>Messages:</>',
]);

$command = $this->findCommand($completionInput, $output);
if (null === $command) {
$this->log('  No command found, completing using the Application class.');

$this->getApplication()->complete($completionInput, $suggestions);
} elseif (
$completionInput->mustSuggestArgumentValuesFor('command')
&& $command->getName() !== $completionInput->getCompletionValue()
&& !\in_array($completionInput->getCompletionValue(), $command->getAliases(), true)
) {
$this->log('  No command found, completing using the Application class.');


$suggestions->suggestValues(array_filter(array_merge([$command->getName()], $command->getAliases())));
} else {
$command->mergeApplicationDefinition();
$completionInput->bind($command->getDefinition());

if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) {
$this->log('  Completing option names for the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> command.');

$suggestions->suggestOptions($command->getDefinition()->getOptions());
} else {
$this->log([
'  Completing using the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> class.',
'  Completing <comment>'.$completionInput->getCompletionType().'</> for <comment>'.$completionInput->getCompletionName().'</>',
]);
if (null !== $compval = $completionInput->getCompletionValue()) {
$this->log('  Current value: <comment>'.$compval.'</>');
}

$command->complete($completionInput, $suggestions);
}
}


$completionOutput = new $completionOutput();

$this->log('<info>Suggestions:</>');
if ($options = $suggestions->getOptionSuggestions()) {
$this->log('  --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options)));
} elseif ($values = $suggestions->getValueSuggestions()) {
$this->log('  '.implode(' ', $values));
} else {
$this->log('  <comment>No suggestions were provided</>');
}

$completionOutput->write($suggestions, $output);
} catch (\Throwable $e) {
$this->log([
'<error>Error!</error>',
(string) $e,
]);

if ($output->isDebug()) {
throw $e;
}

return self::FAILURE;
}

return self::SUCCESS;
}

private function createCompletionInput(InputInterface $input): CompletionInput
{
$currentIndex = $input->getOption('current');
if (!$currentIndex || !ctype_digit($currentIndex)) {
throw new \RuntimeException('The "--current" option must be set and it must be an integer.');
}

$completionInput = CompletionInput::fromTokens($input->getOption('input'), (int) $currentIndex);

try {
$completionInput->bind($this->getApplication()->getDefinition());
} catch (ExceptionInterface $e) {
}

return $completionInput;
}

private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command
{
try {
$inputName = $completionInput->getFirstArgument();
if (null === $inputName) {
return null;
}

return $this->getApplication()->find($inputName);
} catch (CommandNotFoundException $e) {
}

return null;
}

private function log($messages): void
{
if (!$this->isDebug) {
return;
}

$commandName = basename($_SERVER['argv'][0]);
file_put_contents(sys_get_temp_dir().'/sf_'.$commandName.'.log', implode(\PHP_EOL, (array) $messages).\PHP_EOL, \FILE_APPEND);
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Descriptor\ApplicationDescription;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






class ListCommand extends Command
{



protected function configure()
{
$this
->setName('list')
->setDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'),
])
->setDescription('List commands')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command lists all commands:

  <info>%command.full_name%</info>

You can also display the commands for a specific namespace:

  <info>%command.full_name% test</info>

You can also output the information in other formats by using the <comment>--format</comment> option:

  <info>%command.full_name% --format=xml</info>

It's also possible to get raw list of commands (useful for embedding command runner):

  <info>%command.full_name% --raw</info>
EOF
)
;
}




protected function execute(InputInterface $input, OutputInterface $output): int
{
$helper = new DescriptorHelper();
$helper->describe($output, $this->getApplication(), [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
'short' => $input->getOption('short'),
]);

return 0;
}

public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('namespace')) {
$descriptor = new ApplicationDescription($this->getApplication());
$suggestions->suggestValues(array_keys($descriptor->getNamespaces()));

return;
}

if ($input->mustSuggestOptionValuesFor('format')) {
$helper = new DescriptorHelper();
$suggestions->suggestValues($helper->getFormats());
}
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;




final class LazyCommand extends Command
{
private $command;
private ?bool $isEnabled;

public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true)
{
$this->setName($name)
->setAliases($aliases)
->setHidden($isHidden)
->setDescription($description);

$this->command = $commandFactory;
$this->isEnabled = $isEnabled;
}

public function ignoreValidationErrors(): void
{
$this->getCommand()->ignoreValidationErrors();
}

public function setApplication(Application $application = null): void
{
if ($this->command instanceof parent) {
$this->command->setApplication($application);
}

parent::setApplication($application);
}

public function setHelperSet(HelperSet $helperSet): void
{
if ($this->command instanceof parent) {
$this->command->setHelperSet($helperSet);
}

parent::setHelperSet($helperSet);
}

public function isEnabled(): bool
{
return $this->isEnabled ?? $this->getCommand()->isEnabled();
}

public function run(InputInterface $input, OutputInterface $output): int
{
return $this->getCommand()->run($input, $output);
}

public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->getCommand()->complete($input, $suggestions);
}

public function setCode(callable $code): static
{
$this->getCommand()->setCode($code);

return $this;
}




public function mergeApplicationDefinition(bool $mergeArgs = true): void
{
$this->getCommand()->mergeApplicationDefinition($mergeArgs);
}

public function setDefinition(array|InputDefinition $definition): static
{
$this->getCommand()->setDefinition($definition);

return $this;
}

public function getDefinition(): InputDefinition
{
return $this->getCommand()->getDefinition();
}

public function getNativeDefinition(): InputDefinition
{
return $this->getCommand()->getNativeDefinition();
}

public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null): static
{
$this->getCommand()->addArgument($name, $mode, $description, $default);

return $this;
}

public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null): static
{
$this->getCommand()->addOption($name, $shortcut, $mode, $description, $default);

return $this;
}

public function setProcessTitle(string $title): static
{
$this->getCommand()->setProcessTitle($title);

return $this;
}

public function setHelp(string $help): static
{
$this->getCommand()->setHelp($help);

return $this;
}

public function getHelp(): string
{
return $this->getCommand()->getHelp();
}

public function getProcessedHelp(): string
{
return $this->getCommand()->getProcessedHelp();
}

public function getSynopsis(bool $short = false): string
{
return $this->getCommand()->getSynopsis($short);
}

public function addUsage(string $usage): static
{
$this->getCommand()->addUsage($usage);

return $this;
}

public function getUsages(): array
{
return $this->getCommand()->getUsages();
}

public function getHelper(string $name): mixed
{
return $this->getCommand()->getHelper($name);
}

public function getCommand(): parent
{
if (!$this->command instanceof \Closure) {
return $this->command;
}

$command = $this->command = ($this->command)();
$command->setApplication($this->getApplication());

if (null !== $this->getHelperSet()) {
$command->setHelperSet($this->getHelperSet());
}

$command->setName($this->getName())
->setAliases($this->getAliases())
->setHidden($this->isHidden())
->setDescription($this->getDescription());


$command->getDefinition();

return $command;
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;






final class DumpCompletionCommand extends Command
{
protected static $defaultName = 'completion';
protected static $defaultDescription = 'Dump the shell completion script';

public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('shell')) {
$suggestions->suggestValues($this->getSupportedShells());
}
}

protected function configure()
{
$fullCommand = $_SERVER['PHP_SELF'];
$commandName = basename($fullCommand);
$fullCommand = @realpath($fullCommand) ?: $fullCommand;

$this
->setHelp(<<<EOH
The <info>%command.name%</> command dumps the shell completion script required
to use shell autocompletion (currently only bash completion is supported).

<comment>Static installation
-------------------</>

Dump the script to a global completion file and restart your shell:

    <info>%command.full_name% bash | sudo tee /etc/bash_completion.d/{$commandName}</>

Or dump the script to a local file and source it:

    <info>%command.full_name% bash > completion.sh</>

    <comment># source the file whenever you use the project</>
    <info>source completion.sh</>

    <comment># or add this line at the end of your "~/.bashrc" file:</>
    <info>source /path/to/completion.sh</>

<comment>Dynamic installation
--------------------</>

Add this to the end of your shell configuration file (e.g. <info>"~/.bashrc"</>):

    <info>eval "$({$fullCommand} completion bash)"</>
EOH
)
->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given')
->addOption('debug', null, InputOption::VALUE_NONE, 'Tail the completion debug log')
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$commandName = basename($_SERVER['argv'][0]);

if ($input->getOption('debug')) {
$this->tailDebugLog($commandName, $output);

return self::SUCCESS;
}

$shell = $input->getArgument('shell') ?? self::guessShell();
$completionFile = __DIR__.'/../Resources/completion.'.$shell;
if (!file_exists($completionFile)) {
$supportedShells = $this->getSupportedShells();

if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
if ($shell) {
$output->writeln(sprintf('<error>Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").</>', $shell, implode('", "', $supportedShells)));
} else {
$output->writeln(sprintf('<error>Shell not detected, Symfony shell completion only supports "%s").</>', implode('", "', $supportedShells)));
}

return self::INVALID;
}

$output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, $this->getApplication()->getVersion()], file_get_contents($completionFile)));

return self::SUCCESS;
}

private static function guessShell(): string
{
return basename($_SERVER['SHELL'] ?? '');
}

private function tailDebugLog(string $commandName, OutputInterface $output): void
{
$debugFile = sys_get_temp_dir().'/sf_'.$commandName.'.log';
if (!file_exists($debugFile)) {
touch($debugFile);
}
$process = new Process(['tail', '-f', $debugFile], null, null, null, 0);
$process->run(function (string $type, string $line) use ($output): void {
$output->write($line);
});
}




private function getSupportedShells(): array
{
return array_map(function ($f) {
return pathinfo($f, \PATHINFO_EXTENSION);
}, glob(__DIR__.'/../Resources/completion.*'));
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Descriptor\ApplicationDescription;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






class HelpCommand extends Command
{
private $command;




protected function configure()
{
$this->ignoreValidationErrors();

$this
->setName('help')
->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
])
->setDescription('Display help for a command')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command displays help for a given command:

  <info>%command.full_name% list</info>

You can also output the help in other formats by using the <comment>--format</comment> option:

  <info>%command.full_name% --format=xml list</info>

To display the list of available commands, please use the <info>list</info> command.
EOF
)
;
}

public function setCommand(Command $command)
{
$this->command = $command;
}




protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->command ??= $this->getApplication()->find($input->getArgument('command_name'));

$helper = new DescriptorHelper();
$helper->describe($output, $this->command, [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
]);

unset($this->command);

return 0;
}

public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('command_name')) {
$descriptor = new ApplicationDescription($this->getApplication());
$suggestions->suggestValues(array_keys($descriptor->getCommands()));

return;
}

if ($input->mustSuggestOptionValuesFor('format')) {
$helper = new DescriptorHelper();
$suggestions->suggestValues($helper->getFormats());
}
}
}
<?php










namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






class Command
{

public const SUCCESS = 0;
public const FAILURE = 1;
public const INVALID = 2;




protected static $defaultName;




protected static $defaultDescription;

private $application = null;
private ?string $name = null;
private ?string $processTitle = null;
private array $aliases = [];
private $definition;
private bool $hidden = false;
private string $help = '';
private string $description = '';
private $fullDefinition = null;
private bool $ignoreValidationErrors = false;
private ?\Closure $code = null;
private array $synopsis = [];
private array $usages = [];
private $helperSet = null;

public static function getDefaultName(): ?string
{
$class = static::class;

if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) {
return $attribute[0]->newInstance()->name;
}

$r = new \ReflectionProperty($class, 'defaultName');

return $class === $r->class ? static::$defaultName : null;
}

public static function getDefaultDescription(): ?string
{
$class = static::class;

if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) {
return $attribute[0]->newInstance()->description;
}

$r = new \ReflectionProperty($class, 'defaultDescription');

return $class === $r->class ? static::$defaultDescription : null;
}






public function __construct(string $name = null)
{
$this->definition = new InputDefinition();

if (null === $name && null !== $name = static::getDefaultName()) {
$aliases = explode('|', $name);

if ('' === $name = array_shift($aliases)) {
$this->setHidden(true);
$name = array_shift($aliases);
}

$this->setAliases($aliases);
}

if (null !== $name) {
$this->setName($name);
}

if ('' === $this->description) {
$this->setDescription(static::getDefaultDescription() ?? '');
}

$this->configure();
}






public function ignoreValidationErrors()
{
$this->ignoreValidationErrors = true;
}

public function setApplication(Application $application = null)
{
$this->application = $application;
if ($application) {
$this->setHelperSet($application->getHelperSet());
} else {
$this->helperSet = null;
}

$this->fullDefinition = null;
}

public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}




public function getHelperSet(): ?HelperSet
{
return $this->helperSet;
}




public function getApplication(): ?Application
{
return $this->application;
}









public function isEnabled()
{
return true;
}




protected function configure()
{
}















protected function execute(InputInterface $input, OutputInterface $output)
{
throw new LogicException('You must override the execute() method in the concrete command class.');
}








protected function interact(InputInterface $input, OutputInterface $output)
{
}











protected function initialize(InputInterface $input, OutputInterface $output)
{
}















public function run(InputInterface $input, OutputInterface $output): int
{

$this->mergeApplicationDefinition();


try {
$input->bind($this->getDefinition());
} catch (ExceptionInterface $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
}
}

$this->initialize($input, $output);

if (null !== $this->processTitle) {
if (\function_exists('cli_set_process_title')) {
if (!@cli_set_process_title($this->processTitle)) {
if ('Darwin' === \PHP_OS) {
$output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
} else {
cli_set_process_title($this->processTitle);
}
}
} elseif (\function_exists('setproctitle')) {
setproctitle($this->processTitle);
} elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
$output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
}
}

if ($input->isInteractive()) {
$this->interact($input, $output);
}




if ($input->hasArgument('command') && null === $input->getArgument('command')) {
$input->setArgument('command', $this->getName());
}

$input->validate();

if ($this->code) {
$statusCode = ($this->code)($input, $output);
} else {
$statusCode = $this->execute($input, $output);

if (!\is_int($statusCode)) {
throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode)));
}
}

return is_numeric($statusCode) ? (int) $statusCode : 0;
}




public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
}















public function setCode(callable $code): static
{
if ($code instanceof \Closure) {
$r = new \ReflectionFunction($code);
if (null === $r->getClosureThis()) {
set_error_handler(static function () {});
try {
if ($c = \Closure::bind($code, $this)) {
$code = $c;
}
} finally {
restore_error_handler();
}
}
} else {
$code = \Closure::fromCallable($code);
}

$this->code = $code;

return $this;
}










public function mergeApplicationDefinition(bool $mergeArgs = true)
{
if (null === $this->application) {
return;
}

$this->fullDefinition = new InputDefinition();
$this->fullDefinition->setOptions($this->definition->getOptions());
$this->fullDefinition->addOptions($this->application->getDefinition()->getOptions());

if ($mergeArgs) {
$this->fullDefinition->setArguments($this->application->getDefinition()->getArguments());
$this->fullDefinition->addArguments($this->definition->getArguments());
} else {
$this->fullDefinition->setArguments($this->definition->getArguments());
}
}






public function setDefinition(array|InputDefinition $definition): static
{
if ($definition instanceof InputDefinition) {
$this->definition = $definition;
} else {
$this->definition->setDefinition($definition);
}

$this->fullDefinition = null;

return $this;
}




public function getDefinition(): InputDefinition
{
return $this->fullDefinition ?? $this->getNativeDefinition();
}









public function getNativeDefinition(): InputDefinition
{
return $this->definition ?? throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
}











public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null): static
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
if (null !== $this->fullDefinition) {
$this->fullDefinition->addArgument(new InputArgument($name, $mode, $description, $default));
}

return $this;
}












public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null): static
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
if (null !== $this->fullDefinition) {
$this->fullDefinition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
}

return $this;
}













public function setName(string $name): static
{
$this->validateName($name);

$this->name = $name;

return $this;
}









public function setProcessTitle(string $title): static
{
$this->processTitle = $title;

return $this;
}




public function getName(): ?string
{
return $this->name;
}






public function setHidden(bool $hidden = true): static
{
$this->hidden = $hidden;

return $this;
}




public function isHidden(): bool
{
return $this->hidden;
}






public function setDescription(string $description): static
{
$this->description = $description;

return $this;
}




public function getDescription(): string
{
return $this->description;
}






public function setHelp(string $help): static
{
$this->help = $help;

return $this;
}




public function getHelp(): string
{
return $this->help;
}





public function getProcessedHelp(): string
{
$name = $this->name;
$isSingleCommand = $this->application && $this->application->isSingleCommand();

$placeholders = [
'%command.name%',
'%command.full_name%',
];
$replacements = [
$name,
$isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
];

return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
}










public function setAliases(iterable $aliases): static
{
$list = [];

foreach ($aliases as $alias) {
$this->validateName($alias);
$list[] = $alias;
}

$this->aliases = \is_array($aliases) ? $aliases : $list;

return $this;
}




public function getAliases(): array
{
return $this->aliases;
}






public function getSynopsis(bool $short = false): string
{
$key = $short ? 'short' : 'long';

if (!isset($this->synopsis[$key])) {
$this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
}

return $this->synopsis[$key];
}






public function addUsage(string $usage): static
{
if (!str_starts_with($usage, $this->name)) {
$usage = sprintf('%s %s', $this->name, $usage);
}

$this->usages[] = $usage;

return $this;
}




public function getUsages(): array
{
return $this->usages;
}







public function getHelper(string $name): mixed
{
if (null === $this->helperSet) {
throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
}

return $this->helperSet->get($name);
}








private function validateName(string $name)
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
}
}
}
<?php










namespace Symfony\Component\Console\Command;






interface SignalableCommandInterface
{



public function getSubscribedSignals(): array;




public function handleSignal(int $signal): void;
}
<?php










namespace Symfony\Component\Console;

use Symfony\Component\Console\Output\OutputInterface;




final class Cursor
{
private $output;
private $input;




public function __construct(OutputInterface $output, $input = null)
{
$this->output = $output;
$this->input = $input ?? (\defined('STDIN') ? \STDIN : fopen('php://input', 'r+'));
}




public function moveUp(int $lines = 1): static
{
$this->output->write(sprintf("\x1b[%dA", $lines));

return $this;
}




public function moveDown(int $lines = 1): static
{
$this->output->write(sprintf("\x1b[%dB", $lines));

return $this;
}




public function moveRight(int $columns = 1): static
{
$this->output->write(sprintf("\x1b[%dC", $columns));

return $this;
}




public function moveLeft(int $columns = 1): static
{
$this->output->write(sprintf("\x1b[%dD", $columns));

return $this;
}




public function moveToColumn(int $column): static
{
$this->output->write(sprintf("\x1b[%dG", $column));

return $this;
}




public function moveToPosition(int $column, int $row): static
{
$this->output->write(sprintf("\x1b[%d;%dH", $row + 1, $column));

return $this;
}




public function savePosition(): static
{
$this->output->write("\x1b7");

return $this;
}




public function restorePosition(): static
{
$this->output->write("\x1b8");

return $this;
}




public function hide(): static
{
$this->output->write("\x1b[?25l");

return $this;
}




public function show(): static
{
$this->output->write("\x1b[?25h\x1b[?0c");

return $this;
}






public function clearLine(): static
{
$this->output->write("\x1b[2K");

return $this;
}




public function clearLineAfter(): self
{
$this->output->write("\x1b[K");

return $this;
}






public function clearOutput(): static
{
$this->output->write("\x1b[0J");

return $this;
}






public function clearScreen(): static
{
$this->output->write("\x1b[2J");

return $this;
}




public function getCurrentPosition(): array
{
static $isTtySupported;

if (null === $isTtySupported && \function_exists('proc_open')) {
$isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
}

if (!$isTtySupported) {
return [1, 1];
}

$sttyMode = shell_exec('stty -g');
shell_exec('stty -icanon -echo');

@fwrite($this->input, "\033[6n");

$code = trim(fread($this->input, 1024));

shell_exec(sprintf('stty %s', $sttyMode));

sscanf($code, "\033[%d;%dR", $row, $col);

return [$col, $row];
}
}
<?php










namespace Symfony\Component\Console\Attribute;




#[\Attribute(\Attribute::TARGET_CLASS)]
class AsCommand
{
public function __construct(
public string $name,
public ?string $description = null,
array $aliases = [],
bool $hidden = false,
) {
if (!$hidden && !$aliases) {
return;
}

$name = explode('|', $name);
$name = array_merge($name, $aliases);

if ($hidden && '' !== $name[0]) {
array_unshift($name, '');
}

$this->name = implode('|', $name);
}
}
<?php










namespace Symfony\Component\Console\Tester;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;







class CommandTester
{
use TesterTrait;

private $command;

public function __construct(Command $command)
{
$this->command = $command;
}
















public function execute(array $input, array $options = []): int
{


if (!isset($input['command'])
&& (null !== $application = $this->command->getApplication())
&& $application->getDefinition()->hasArgument('command')
) {
$input = array_merge(['command' => $this->command->getName()], $input);
}

$this->input = new ArrayInput($input);

$this->input->setStream(self::createStream($this->inputs));

if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}

if (!isset($options['decorated'])) {
$options['decorated'] = false;
}

$this->initOutput($options);

return $this->statusCode = $this->command->run($this->input, $this->output);
}
}
<?php










namespace Symfony\Component\Console\Tester;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;






class CommandCompletionTester
{
private $command;

public function __construct(Command $command)
{
$this->command = $command;
}




public function complete(array $input): array
{
$currentIndex = \count($input);
if ('' === end($input)) {
array_pop($input);
}
array_unshift($input, $this->command->getName());

$completionInput = CompletionInput::fromTokens($input, $currentIndex);
$completionInput->bind($this->command->getDefinition());
$suggestions = new CompletionSuggestions();

$this->command->complete($completionInput, $suggestions);

$options = [];
foreach ($suggestions->getOptionSuggestions() as $option) {
$options[] = '--'.$option->getName();
}

return array_map('strval', array_merge($options, $suggestions->getValueSuggestions()));
}
}
<?php










namespace Symfony\Component\Console\Tester;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;











class ApplicationTester
{
use TesterTrait;

private $application;

public function __construct(Application $application)
{
$this->application = $application;
}













public function run(array $input, array $options = []): int
{
$prevShellVerbosity = getenv('SHELL_VERBOSITY');

try {
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}

if ($this->inputs) {
$this->input->setStream(self::createStream($this->inputs));
}

$this->initOutput($options);

return $this->statusCode = $this->application->run($this->input, $this->output);
} finally {


if (false === $prevShellVerbosity) {
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY');
}
unset($_ENV['SHELL_VERBOSITY']);
unset($_SERVER['SHELL_VERBOSITY']);
} else {
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY='.$prevShellVerbosity);
}
$_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity;
}
}
}
}
<?php










namespace Symfony\Component\Console\Tester\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Console\Command\Command;

final class CommandIsSuccessful extends Constraint
{



public function toString(): string
{
return 'is successful';
}




protected function matches($other): bool
{
return Command::SUCCESS === $other;
}




protected function failureDescription($other): string
{
return 'the command '.$this->toString();
}




protected function additionalFailureDescription($other): string
{
$mapping = [
Command::FAILURE => 'Command failed.',
Command::INVALID => 'Command was invalid.',
];

return $mapping[$other] ?? sprintf('Command returned exit status %d.', $other);
}
}
<?php










namespace Symfony\Component\Console\Tester;

use PHPUnit\Framework\Assert;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Tester\Constraint\CommandIsSuccessful;




trait TesterTrait
{
private $output;
private array $inputs = [];
private bool $captureStreamsIndependently = false;
private $input;
private int $statusCode;






public function getDisplay(bool $normalize = false): string
{
if (!isset($this->output)) {
throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?');
}

rewind($this->output->getStream());

$display = stream_get_contents($this->output->getStream());

if ($normalize) {
$display = str_replace(\PHP_EOL, "\n", $display);
}

return $display;
}






public function getErrorOutput(bool $normalize = false): string
{
if (!$this->captureStreamsIndependently) {
throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.');
}

rewind($this->output->getErrorOutput()->getStream());

$display = stream_get_contents($this->output->getErrorOutput()->getStream());

if ($normalize) {
$display = str_replace(\PHP_EOL, "\n", $display);
}

return $display;
}




public function getInput(): InputInterface
{
return $this->input;
}




public function getOutput(): OutputInterface
{
return $this->output;
}






public function getStatusCode(): int
{
return $this->statusCode ?? throw new \RuntimeException('Status code not initialized, did you execute the command before requesting the status code?');
}

public function assertCommandIsSuccessful(string $message = ''): void
{
Assert::assertThat($this->statusCode, new CommandIsSuccessful(), $message);
}









public function setInputs(array $inputs): static
{
$this->inputs = $inputs;

return $this;
}










private function initOutput(array $options)
{
$this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
if (!$this->captureStreamsIndependently) {
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
} else {
$this->output = new ConsoleOutput(
$options['verbosity'] ?? ConsoleOutput::VERBOSITY_NORMAL,
$options['decorated'] ?? null
);

$errorOutput = new StreamOutput(fopen('php://memory', 'w', false));
$errorOutput->setFormatter($this->output->getFormatter());
$errorOutput->setVerbosity($this->output->getVerbosity());
$errorOutput->setDecorated($this->output->isDecorated());

$reflectedOutput = new \ReflectionObject($this->output);
$strErrProperty = $reflectedOutput->getProperty('stderr');
$strErrProperty->setAccessible(true);
$strErrProperty->setValue($this->output, $errorOutput);

$reflectedParent = $reflectedOutput->getParentClass();
$streamProperty = $reflectedParent->getProperty('stream');
$streamProperty->setAccessible(true);
$streamProperty->setValue($this->output, fopen('php://memory', 'w', false));
}
}




private static function createStream(array $inputs)
{
$stream = fopen('php://memory', 'r+', false);

foreach ($inputs as $input) {
fwrite($stream, $input.\PHP_EOL);
}

rewind($stream);

return $stream;
}
}
<?php










namespace Symfony\Component\Console\Style;






interface StyleInterface
{



public function title(string $message);




public function section(string $message);




public function listing(array $elements);




public function text(string|array $message);




public function success(string|array $message);




public function error(string|array $message);




public function warning(string|array $message);




public function note(string|array $message);




public function caution(string|array $message);




public function table(array $headers, array $rows);




public function ask(string $question, string $default = null, callable $validator = null): mixed;




public function askHidden(string $question, callable $validator = null): mixed;




public function confirm(string $question, bool $default = true): bool;




public function choice(string $question, array $choices, mixed $default = null): mixed;




public function newLine(int $count = 1);




public function progressStart(int $max = 0);




public function progressAdvance(int $step = 1);




public function progressFinish();
}
<?php










namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;






abstract class OutputStyle implements OutputInterface, StyleInterface
{
private $output;

public function __construct(OutputInterface $output)
{
$this->output = $output;
}




public function newLine(int $count = 1)
{
$this->output->write(str_repeat(\PHP_EOL, $count));
}

public function createProgressBar(int $max = 0): ProgressBar
{
return new ProgressBar($this->output, $max);
}




public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
$this->output->write($messages, $newline, $type);
}




public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL)
{
$this->output->writeln($messages, $type);
}




public function setVerbosity(int $level)
{
$this->output->setVerbosity($level);
}




public function getVerbosity(): int
{
return $this->output->getVerbosity();
}




public function setDecorated(bool $decorated)
{
$this->output->setDecorated($decorated);
}




public function isDecorated(): bool
{
return $this->output->isDecorated();
}




public function setFormatter(OutputFormatterInterface $formatter)
{
$this->output->setFormatter($formatter);
}




public function getFormatter(): OutputFormatterInterface
{
return $this->output->getFormatter();
}




public function isQuiet(): bool
{
return $this->output->isQuiet();
}




public function isVerbose(): bool
{
return $this->output->isVerbose();
}




public function isVeryVerbose(): bool
{
return $this->output->isVeryVerbose();
}




public function isDebug(): bool
{
return $this->output->isDebug();
}

protected function getErrorOutput()
{
if (!$this->output instanceof ConsoleOutputInterface) {
return $this->output;
}

return $this->output->getErrorOutput();
}
}
<?php










namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\TrimmedBufferOutput;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;






class SymfonyStyle extends OutputStyle
{
public const MAX_LINE_LENGTH = 120;

private $input;
private $output;
private $questionHelper;
private $progressBar;
private int $lineLength;
private $bufferedOutput;

public function __construct(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter());

$width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
$this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);

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




public function block(string|array $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true)
{
$messages = \is_array($messages) ? array_values($messages) : [$messages];

$this->autoPrependBlock();
$this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape));
$this->newLine();
}




public function title(string $message)
{
$this->autoPrependBlock();
$this->writeln([
sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
sprintf('<comment>%s</>', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))),
]);
$this->newLine();
}




public function section(string $message)
{
$this->autoPrependBlock();
$this->writeln([
sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
sprintf('<comment>%s</>', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))),
]);
$this->newLine();
}




public function listing(array $elements)
{
$this->autoPrependText();
$elements = array_map(function ($element) {
return sprintf(' * %s', $element);
}, $elements);

$this->writeln($elements);
$this->newLine();
}




public function text(string|array $message)
{
$this->autoPrependText();

$messages = \is_array($message) ? array_values($message) : [$message];
foreach ($messages as $message) {
$this->writeln(sprintf(' %s', $message));
}
}




public function comment(string|array $message)
{
$this->block($message, null, null, '<fg=default;bg=default> // </>', false, false);
}




public function success(string|array $message)
{
$this->block($message, 'OK', 'fg=black;bg=green', ' ', true);
}




public function error(string|array $message)
{
$this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true);
}




public function warning(string|array $message)
{
$this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true);
}




public function note(string|array $message)
{
$this->block($message, 'NOTE', 'fg=yellow', ' ! ');
}




public function info(string|array $message)
{
$this->block($message, 'INFO', 'fg=green', ' ', true);
}




public function caution(string|array $message)
{
$this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true);
}




public function table(array $headers, array $rows)
{
$this->createTable()
->setHeaders($headers)
->setRows($rows)
->render()
;

$this->newLine();
}




public function horizontalTable(array $headers, array $rows)
{
$this->createTable()
->setHorizontal(true)
->setHeaders($headers)
->setRows($rows)
->render()
;

$this->newLine();
}









public function definitionList(string|array|TableSeparator ...$list)
{
$headers = [];
$row = [];
foreach ($list as $value) {
if ($value instanceof TableSeparator) {
$headers[] = $value;
$row[] = $value;
continue;
}
if (\is_string($value)) {
$headers[] = new TableCell($value, ['colspan' => 2]);
$row[] = null;
continue;
}
if (!\is_array($value)) {
throw new InvalidArgumentException('Value should be an array, string, or an instance of TableSeparator.');
}
$headers[] = key($value);
$row[] = current($value);
}

$this->horizontalTable($headers, [$row]);
}




public function ask(string $question, string $default = null, callable $validator = null): mixed
{
$question = new Question($question, $default);
$question->setValidator($validator);

return $this->askQuestion($question);
}




public function askHidden(string $question, callable $validator = null): mixed
{
$question = new Question($question);

$question->setHidden(true);
$question->setValidator($validator);

return $this->askQuestion($question);
}




public function confirm(string $question, bool $default = true): bool
{
return $this->askQuestion(new ConfirmationQuestion($question, $default));
}




public function choice(string $question, array $choices, mixed $default = null): mixed
{
if (null !== $default) {
$values = array_flip($choices);
$default = $values[$default] ?? $default;
}

return $this->askQuestion(new ChoiceQuestion($question, $choices, $default));
}




public function progressStart(int $max = 0)
{
$this->progressBar = $this->createProgressBar($max);
$this->progressBar->start();
}




public function progressAdvance(int $step = 1)
{
$this->getProgressBar()->advance($step);
}




public function progressFinish()
{
$this->getProgressBar()->finish();
$this->newLine(2);
unset($this->progressBar);
}




public function createProgressBar(int $max = 0): ProgressBar
{
$progressBar = parent::createProgressBar($max);

if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) {
$progressBar->setEmptyBarCharacter('░'); 
$progressBar->setProgressCharacter('');
$progressBar->setBarCharacter('▓'); 
}

return $progressBar;
}




public function progressIterate(iterable $iterable, int $max = null): iterable
{
yield from $this->createProgressBar()->iterate($iterable, $max);

$this->newLine(2);
}

public function askQuestion(Question $question): mixed
{
if ($this->input->isInteractive()) {
$this->autoPrependBlock();
}

$this->questionHelper ??= new SymfonyQuestionHelper();

$answer = $this->questionHelper->ask($this->input, $this, $question);

if ($this->input->isInteractive()) {
$this->newLine();
$this->bufferedOutput->write("\n");
}

return $answer;
}




public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}

foreach ($messages as $message) {
parent::writeln($message, $type);
$this->writeBuffer($message, true, $type);
}
}




public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}

foreach ($messages as $message) {
parent::write($message, $newline, $type);
$this->writeBuffer($message, $newline, $type);
}
}




public function newLine(int $count = 1)
{
parent::newLine($count);
$this->bufferedOutput->write(str_repeat("\n", $count));
}




public function getErrorStyle(): self
{
return new self($this->input, $this->getErrorOutput());
}

public function createTable(): Table
{
$output = $this->output instanceof ConsoleOutputInterface ? $this->output->section() : $this->output;
$style = clone Table::getStyleDefinition('symfony-style-guide');
$style->setCellHeaderFormat('<info>%s</info>');

return (new Table($output))->setStyle($style);
}

private function getProgressBar(): ProgressBar
{
return $this->progressBar
?? throw new RuntimeException('The ProgressBar is not started.');
}

private function autoPrependBlock(): void
{
$chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);

if (!isset($chars[0])) {
$this->newLine(); 

return;
}

$this->newLine(2 - substr_count($chars, "\n"));
}

private function autoPrependText(): void
{
$fetched = $this->bufferedOutput->fetch();

if (!str_ends_with($fetched, "\n")) {
$this->newLine();
}
}

private function writeBuffer(string $message, bool $newLine, int $type): void
{

$this->bufferedOutput->write($message, $newLine, $type);
}

private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
{
$indentLength = 0;
$prefixLength = Helper::width(Helper::removeDecoration($this->getFormatter(), $prefix));
$lines = [];

if (null !== $type) {
$type = sprintf('[%s] ', $type);
$indentLength = \strlen($type);
$lineIndentation = str_repeat(' ', $indentLength);
}


foreach ($messages as $key => $message) {
if ($escape) {
$message = OutputFormatter::escape($message);
}

$decorationLength = Helper::width($message) - Helper::width(Helper::removeDecoration($this->getFormatter(), $message));
$messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength);
$messageLines = explode(\PHP_EOL, wordwrap($message, $messageLineLength, \PHP_EOL, true));
foreach ($messageLines as $messageLine) {
$lines[] = $messageLine;
}

if (\count($messages) > 1 && $key < \count($messages) - 1) {
$lines[] = '';
}
}

$firstLineIndex = 0;
if ($padding && $this->isDecorated()) {
$firstLineIndex = 1;
array_unshift($lines, '');
$lines[] = '';
}

foreach ($lines as $i => &$line) {
if (null !== $type) {
$line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line;
}

$line = $prefix.$line;
$line .= str_repeat(' ', max($this->lineLength - Helper::width(Helper::removeDecoration($this->getFormatter(), $line)), 0));

if ($style) {
$line = sprintf('<%s>%s</>', $style, $line);
}
}

return $lines;
}
}
<?php










namespace Symfony\Component\Console\Exception;






class MissingInputException extends RuntimeException implements ExceptionInterface
{
}
<?php










namespace Symfony\Component\Console\Exception;




class LogicException extends \LogicException implements ExceptionInterface
{
}
<?php










namespace Symfony\Component\Console\Exception;






class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php










namespace Symfony\Component\Console\Exception;






class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
{
private array $alternatives;







public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null)
{
parent::__construct($message, $code, $previous);

$this->alternatives = $alternatives;
}




public function getAlternatives(): array
{
return $this->alternatives;
}
}
<?php










namespace Symfony\Component\Console\Exception;






interface ExceptionInterface extends \Throwable
{
}
<?php










namespace Symfony\Component\Console\Exception;






class NamespaceNotFoundException extends CommandNotFoundException
{
}
<?php










namespace Symfony\Component\Console\Exception;




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










namespace Symfony\Component\Console\Exception;




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










namespace Symfony\Component\Console\Completion;






class Suggestion
{
private string $value;

public function __construct(string $value)
{
$this->value = $value;
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->getValue();
}
}
<?php










namespace Symfony\Component\Console\Completion;

use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;









final class CompletionInput extends ArgvInput
{
public const TYPE_ARGUMENT_VALUE = 'argument_value';
public const TYPE_OPTION_VALUE = 'option_value';
public const TYPE_OPTION_NAME = 'option_name';
public const TYPE_NONE = 'none';

private $tokens;
private $currentIndex;
private $completionType;
private $completionName = null;
private $completionValue = '';






public static function fromString(string $inputStr, int $currentIndex): self
{
preg_match_all('/(?<=^|\s)([\'"]?)(.+?)(?<!\\\\)\1(?=$|\s)/', $inputStr, $tokens);

return self::fromTokens($tokens[0], $currentIndex);
}







public static function fromTokens(array $tokens, int $currentIndex): self
{
$input = new self($tokens);
$input->tokens = $tokens;
$input->currentIndex = $currentIndex;

return $input;
}




public function bind(InputDefinition $definition): void
{
parent::bind($definition);

$relevantToken = $this->getRelevantToken();
if ('-' === $relevantToken[0]) {

[$optionToken, $optionValue] = explode('=', $relevantToken, 2) + ['', ''];

$option = $this->getOptionFromToken($optionToken);
if (null === $option && !$this->isCursorFree()) {
$this->completionType = self::TYPE_OPTION_NAME;
$this->completionValue = $relevantToken;

return;
}

if (null !== $option && $option->acceptValue()) {
$this->completionType = self::TYPE_OPTION_VALUE;
$this->completionName = $option->getName();
$this->completionValue = $optionValue ?: (!str_starts_with($optionToken, '--') ? substr($optionToken, 2) : '');

return;
}
}

$previousToken = $this->tokens[$this->currentIndex - 1];
if ('-' === $previousToken[0] && '' !== trim($previousToken, '-')) {

$previousOption = $this->getOptionFromToken($previousToken);
if (null !== $previousOption && $previousOption->acceptValue()) {
$this->completionType = self::TYPE_OPTION_VALUE;
$this->completionName = $previousOption->getName();
$this->completionValue = $relevantToken;

return;
}
}


$this->completionType = self::TYPE_ARGUMENT_VALUE;

foreach ($this->definition->getArguments() as $argumentName => $argument) {
if (!isset($this->arguments[$argumentName])) {
break;
}

$argumentValue = $this->arguments[$argumentName];
$this->completionName = $argumentName;
if (\is_array($argumentValue)) {
$this->completionValue = $argumentValue ? $argumentValue[array_key_last($argumentValue)] : null;
} else {
$this->completionValue = $argumentValue;
}
}

if ($this->currentIndex >= \count($this->tokens)) {
if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) {
$this->completionName = $argumentName;
$this->completionValue = '';
} else {

$this->completionType = self::TYPE_NONE;
$this->completionName = null;
$this->completionValue = '';
}
}
}











public function getCompletionType(): string
{
return $this->completionType;
}






public function getCompletionName(): ?string
{
return $this->completionName;
}




public function getCompletionValue(): string
{
return $this->completionValue;
}

public function mustSuggestOptionValuesFor(string $optionName): bool
{
return self::TYPE_OPTION_VALUE === $this->getCompletionType() && $optionName === $this->getCompletionName();
}

public function mustSuggestArgumentValuesFor(string $argumentName): bool
{
return self::TYPE_ARGUMENT_VALUE === $this->getCompletionType() && $argumentName === $this->getCompletionName();
}

protected function parseToken(string $token, bool $parseOptions): bool
{
try {
return parent::parseToken($token, $parseOptions);
} catch (RuntimeException $e) {

}

return $parseOptions;
}

private function getOptionFromToken(string $optionToken): ?InputOption
{
$optionName = ltrim($optionToken, '-');
if (!$optionName) {
return null;
}

if ('-' === ($optionToken[1] ?? ' ')) {

return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null;
}


return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null;
}




private function getRelevantToken(): string
{
return $this->tokens[$this->isCursorFree() ? $this->currentIndex - 1 : $this->currentIndex];
}




private function isCursorFree(): bool
{
$nrOfTokens = \count($this->tokens);
if ($this->currentIndex > $nrOfTokens) {
throw new \LogicException('Current index is invalid, it must be the number of input tokens or one more.');
}

return $this->currentIndex >= $nrOfTokens;
}

public function __toString()
{
$str = '';
foreach ($this->tokens as $i => $token) {
$str .= $token;

if ($this->currentIndex === $i) {
$str .= '|';
}

$str .= ' ';
}

if ($this->currentIndex > $i) {
$str .= '|';
}

return rtrim($str);
}
}
<?php










namespace Symfony\Component\Console\Completion;

use Symfony\Component\Console\Input\InputOption;






final class CompletionSuggestions
{
private $valueSuggestions = [];
private $optionSuggestions = [];






public function suggestValue(string|Suggestion $value): static
{
$this->valueSuggestions[] = !$value instanceof Suggestion ? new Suggestion($value) : $value;

return $this;
}








public function suggestValues(array $values): static
{
foreach ($values as $value) {
$this->suggestValue($value);
}

return $this;
}






public function suggestOption(InputOption $option): static
{
$this->optionSuggestions[] = $option;

return $this;
}








public function suggestOptions(array $options): static
{
foreach ($options as $option) {
$this->suggestOption($option);
}

return $this;
}




public function getOptionSuggestions(): array
{
return $this->optionSuggestions;
}




public function getValueSuggestions(): array
{
return $this->valueSuggestions;
}
}
<?php










namespace Symfony\Component\Console\Completion\Output;

use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Output\OutputInterface;




class BashCompletionOutput implements CompletionOutputInterface
{
public function write(CompletionSuggestions $suggestions, OutputInterface $output): void
{
$values = $suggestions->getValueSuggestions();
foreach ($suggestions->getOptionSuggestions() as $option) {
$values[] = '--'.$option->getName();
if ($option->isNegatable()) {
$values[] = '--no-'.$option->getName();
}
}
$output->writeln(implode("\n", $values));
}
}
<?php










namespace Symfony\Component\Console\Completion\Output;

use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Output\OutputInterface;






interface CompletionOutputInterface
{
public function write(CompletionSuggestions $suggestions, OutputInterface $output): void;
}
<?php










namespace Symfony\Component\Console\Logger;

use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;








class ConsoleLogger extends AbstractLogger
{
public const INFO = 'info';
public const ERROR = 'error';

private $output;
private array $verbosityLevelMap = [
LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
];
private array $formatLevelMap = [
LogLevel::EMERGENCY => self::ERROR,
LogLevel::ALERT => self::ERROR,
LogLevel::CRITICAL => self::ERROR,
LogLevel::ERROR => self::ERROR,
LogLevel::WARNING => self::INFO,
LogLevel::NOTICE => self::INFO,
LogLevel::INFO => self::INFO,
LogLevel::DEBUG => self::INFO,
];
private bool $errored = false;

public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = [])
{
$this->output = $output;
$this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
$this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
}




public function log($level, $message, array $context = []): void
{
if (!isset($this->verbosityLevelMap[$level])) {
throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
}

$output = $this->output;


if (self::ERROR === $this->formatLevelMap[$level]) {
if ($this->output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->errored = true;
}



if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
$output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
}
}




public function hasErrored(): bool
{
return $this->errored;
}






private function interpolate(string $message, array $context): string
{
if (!str_contains($message, '{')) {
return $message;
}

$replacements = [];
foreach ($context as $key => $val) {
if (null === $val || \is_scalar($val) || $val instanceof \Stringable) {
$replacements["{{$key}}"] = $val;
} elseif ($val instanceof \DateTimeInterface) {
$replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
} elseif (\is_object($val)) {
$replacements["{{$key}}"] = '[object '.\get_class($val).']';
} else {
$replacements["{{$key}}"] = '['.\gettype($val).']';
}
}

return strtr($message, $replacements);
}
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;













class InputDefinition
{
private array $arguments = [];
private int $requiredCount = 0;
private $lastArrayArgument = null;
private $lastOptionalArgument = null;
private array $options = [];
private array $negations = [];
private array $shortcuts = [];




public function __construct(array $definition = [])
{
$this->setDefinition($definition);
}




public function setDefinition(array $definition)
{
$arguments = [];
$options = [];
foreach ($definition as $item) {
if ($item instanceof InputOption) {
$options[] = $item;
} else {
$arguments[] = $item;
}
}

$this->setArguments($arguments);
$this->setOptions($options);
}






public function setArguments(array $arguments = [])
{
$this->arguments = [];
$this->requiredCount = 0;
$this->lastOptionalArgument = null;
$this->lastArrayArgument = null;
$this->addArguments($arguments);
}






public function addArguments(?array $arguments = [])
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
$this->addArgument($argument);
}
}
}




public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}

if (null !== $this->lastArrayArgument) {
throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName()));
}

if ($argument->isRequired() && null !== $this->lastOptionalArgument) {
throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName()));
}

if ($argument->isArray()) {
$this->lastArrayArgument = $argument;
}

if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->lastOptionalArgument = $argument;
}

$this->arguments[$argument->getName()] = $argument;
}






public function getArgument(string|int $name): InputArgument
{
if (!$this->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}

$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;

return $arguments[$name];
}




public function hasArgument(string|int $name): bool
{
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;

return isset($arguments[$name]);
}






public function getArguments(): array
{
return $this->arguments;
}




public function getArgumentCount(): int
{
return null !== $this->lastArrayArgument ? \PHP_INT_MAX : \count($this->arguments);
}




public function getArgumentRequiredCount(): int
{
return $this->requiredCount;
}




public function getArgumentDefaults(): array
{
$values = [];
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}

return $values;
}






public function setOptions(array $options = [])
{
$this->options = [];
$this->shortcuts = [];
$this->negations = [];
$this->addOptions($options);
}






public function addOptions(array $options = [])
{
foreach ($options as $option) {
$this->addOption($option);
}
}




public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if (isset($this->negations[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}

if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
}
}
}

$this->options[$option->getName()] = $option;
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
$this->shortcuts[$shortcut] = $option->getName();
}
}

if ($option->isNegatable()) {
$negatedName = 'no-'.$option->getName();
if (isset($this->options[$negatedName])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName));
}
$this->negations[$negatedName] = $option->getName();
}
}






public function getOption(string $name): InputOption
{
if (!$this->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}

return $this->options[$name];
}







public function hasOption(string $name): bool
{
return isset($this->options[$name]);
}






public function getOptions(): array
{
return $this->options;
}




public function hasShortcut(string $name): bool
{
return isset($this->shortcuts[$name]);
}




public function hasNegation(string $name): bool
{
return isset($this->negations[$name]);
}




public function getOptionForShortcut(string $shortcut): InputOption
{
return $this->getOption($this->shortcutToName($shortcut));
}




public function getOptionDefaults(): array
{
$values = [];
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}

return $values;
}








public function shortcutToName(string $shortcut): string
{
if (!isset($this->shortcuts[$shortcut])) {
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}

return $this->shortcuts[$shortcut];
}








public function negationToName(string $negation): string
{
if (!isset($this->negations[$negation])) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation));
}

return $this->negations[$negation];
}




public function getSynopsis(bool $short = false): string
{
$elements = [];

if ($short && $this->getOptions()) {
$elements[] = '[options]';
} elseif (!$short) {
foreach ($this->getOptions() as $option) {
$value = '';
if ($option->acceptValue()) {
$value = sprintf(
' %s%s%s',
$option->isValueOptional() ? '[' : '',
strtoupper($option->getName()),
$option->isValueOptional() ? ']' : ''
);
}

$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : '';
$elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation);
}
}

if (\count($elements) && $this->getArguments()) {
$elements[] = '[--]';
}

$tail = '';
foreach ($this->getArguments() as $argument) {
$element = '<'.$argument->getName().'>';
if ($argument->isArray()) {
$element .= '...';
}

if (!$argument->isRequired()) {
$element = '['.$element;
$tail .= ']';
}

$elements[] = $element;
}

return implode(' ', $elements).$tail;
}
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;






interface InputInterface
{



public function getFirstArgument(): ?string;












public function hasParameterOption(string|array $values, bool $onlyParams = false): bool;















public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false);






public function bind(InputDefinition $definition);






public function validate();






public function getArguments(): array;








public function getArgument(string $name);






public function setArgument(string $name, mixed $value);




public function hasArgument(string $name): bool;






public function getOptions(): array;








public function getOption(string $name);






public function setOption(string $name, mixed $value);




public function hasOption(string $name): bool;




public function isInteractive(): bool;




public function setInteractive(bool $interactive);
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;






class InputOption
{



public const VALUE_NONE = 1;




public const VALUE_REQUIRED = 2;




public const VALUE_OPTIONAL = 4;




public const VALUE_IS_ARRAY = 8;




public const VALUE_NEGATABLE = 16;

private string $name;
private string|array|null $shortcut;
private int $mode;
private string|int|bool|array|null|float $default;
private string $description;








public function __construct(string $name, string|array $shortcut = null, int $mode = null, string $description = '', string|bool|int|float|array $default = null)
{
if (str_starts_with($name, '--')) {
$name = substr($name, 2);
}

if (empty($name)) {
throw new InvalidArgumentException('An option name cannot be empty.');
}

if (empty($shortcut)) {
$shortcut = null;
}

if (null !== $shortcut) {
if (\is_array($shortcut)) {
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
$shortcuts = array_filter($shortcuts);
$shortcut = implode('|', $shortcuts);

if (empty($shortcut)) {
throw new InvalidArgumentException('An option shortcut cannot be empty.');
}
}

if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) {
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}

$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;

if ($this->isArray() && !$this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
if ($this->isNegatable() && $this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_NEGATABLE if the option also accepts a value.');
}

$this->setDefault($default);
}




public function getShortcut(): ?string
{
return $this->shortcut;
}




public function getName(): string
{
return $this->name;
}






public function acceptValue(): bool
{
return $this->isValueRequired() || $this->isValueOptional();
}






public function isValueRequired(): bool
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}






public function isValueOptional(): bool
{
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}






public function isArray(): bool
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}

public function isNegatable(): bool
{
return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode);
}

public function setDefault(string|bool|int|float|array $default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
}

if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array option must be an array.');
}
}

$this->default = $this->acceptValue() || $this->isNegatable() ? $default : false;
}




public function getDefault(): string|bool|int|float|array|null
{
return $this->default;
}




public function getDescription(): string
{
return $this->description;
}




public function equals(self $option): bool
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
&& $option->getDefault() === $this->getDefault()
&& $option->isNegatable() === $this->isNegatable()
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional()
;
}
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;






class InputArgument
{
public const REQUIRED = 1;
public const OPTIONAL = 2;
public const IS_ARRAY = 4;

private string $name;
private int $mode;
private string|int|bool|array|null|float $default;
private string $description;









public function __construct(string $name, int $mode = null, string $description = '', string|bool|int|float|array $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif ($mode > 7 || $mode < 1) {
throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}

$this->name = $name;
$this->mode = $mode;
$this->description = $description;

$this->setDefault($default);
}




public function getName(): string
{
return $this->name;
}






public function isRequired(): bool
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}






public function isArray(): bool
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}






public function setDefault(string|bool|int|float|array $default = null)
{
if ($this->isRequired() && null !== $default) {
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}

if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array argument must be an array.');
}
}

$this->default = $default;
}




public function getDefault(): string|bool|int|float|array|null
{
return $this->default;
}




public function getDescription(): string
{
return $this->description;
}
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;










class StringInput extends ArgvInput
{
public const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
public const REGEX_UNQUOTED_STRING = '([^\s\\\\]+?)';
public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';




public function __construct(string $input)
{
parent::__construct([]);

$this->setTokens($this->tokenize($input));
}






private function tokenize(string $input): array
{
$tokens = [];
$length = \strlen($input);
$cursor = 0;
$token = null;
while ($cursor < $length) {
if ('\\' === $input[$cursor]) {
$token .= $input[++$cursor] ?? '';
++$cursor;
continue;
}

if (preg_match('/\s+/A', $input, $match, 0, $cursor)) {
if (null !== $token) {
$tokens[] = $token;
$token = null;
}
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) {
$token .= $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$token .= stripcslashes(substr($match[0], 1, -1));
} elseif (preg_match('/'.self::REGEX_UNQUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$token .= $match[1];
} else {

throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10)));
}

$cursor += \strlen($match[0]);
}

if (null !== $token) {
$tokens[] = $token;
}

return $tokens;
}
}
<?php










namespace Symfony\Component\Console\Input;







interface StreamableInputInterface extends InputInterface
{







public function setStream($stream);






public function getStream();
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;












abstract class Input implements InputInterface, StreamableInputInterface
{
protected $definition;
protected $stream;
protected $options = [];
protected $arguments = [];
protected $interactive = true;

public function __construct(InputDefinition $definition = null)
{
if (null === $definition) {
$this->definition = new InputDefinition();
} else {
$this->bind($definition);
$this->validate();
}
}




public function bind(InputDefinition $definition)
{
$this->arguments = [];
$this->options = [];
$this->definition = $definition;

$this->parse();
}




abstract protected function parse();




public function validate()
{
$definition = $this->definition;
$givenArguments = $this->arguments;

$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
});

if (\count($missingArguments) > 0) {
throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
}
}




public function isInteractive(): bool
{
return $this->interactive;
}




public function setInteractive(bool $interactive)
{
$this->interactive = $interactive;
}




public function getArguments(): array
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}




public function getArgument(string $name): mixed
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}

return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault();
}




public function setArgument(string $name, mixed $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}

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




public function hasArgument(string $name): bool
{
return $this->definition->hasArgument($name);
}




public function getOptions(): array
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}




public function getOption(string $name): mixed
{
if ($this->definition->hasNegation($name)) {
if (null === $value = $this->getOption($this->definition->negationToName($name))) {
return $value;
}

return !$value;
}

if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}

return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}




public function setOption(string $name, mixed $value)
{
if ($this->definition->hasNegation($name)) {
$this->options[$this->definition->negationToName($name)] = !$value;

return;
} elseif (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}

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




public function hasOption(string $name): bool
{
return $this->definition->hasOption($name) || $this->definition->hasNegation($name);
}




public function escapeToken(string $token): string
{
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}




public function setStream($stream)
{
$this->stream = $stream;
}




public function getStream()
{
return $this->stream;
}
}
<?php










namespace Symfony\Component\Console\Input;







interface InputAwareInterface
{



public function setInput(InputInterface $input);
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;










class ArrayInput extends Input
{
private array $parameters;

public function __construct(array $parameters, InputDefinition $definition = null)
{
$this->parameters = $parameters;

parent::__construct($definition);
}




public function getFirstArgument(): ?string
{
foreach ($this->parameters as $param => $value) {
if ($param && \is_string($param) && '-' === $param[0]) {
continue;
}

return $value;
}

return null;
}




public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
{
$values = (array) $values;

foreach ($this->parameters as $k => $v) {
if (!\is_int($k)) {
$v = $k;
}

if ($onlyParams && '--' === $v) {
return false;
}

if (\in_array($v, $values)) {
return true;
}
}

return false;
}




public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
{
$values = (array) $values;

foreach ($this->parameters as $k => $v) {
if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) {
return $default;
}

if (\is_int($k)) {
if (\in_array($v, $values)) {
return true;
}
} elseif (\in_array($k, $values)) {
return $v;
}
}

return $default;
}




public function __toString(): string
{
$params = [];
foreach ($this->parameters as $param => $val) {
if ($param && \is_string($param) && '-' === $param[0]) {
$glue = ('-' === $param[1]) ? '=' : ' ';
if (\is_array($val)) {
foreach ($val as $v) {
$params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : '');
}
} else {
$params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : '');
}
} else {
$params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val);
}
}

return implode(' ', $params);
}




protected function parse()
{
foreach ($this->parameters as $key => $value) {
if ('--' === $key) {
return;
}
if (str_starts_with($key, '--')) {
$this->addLongOption(substr($key, 2), $value);
} elseif (str_starts_with($key, '-')) {
$this->addShortOption(substr($key, 1), $value);
} else {
$this->addArgument($key, $value);
}
}
}






private function addShortOption(string $shortcut, mixed $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
}

$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}







private function addLongOption(string $name, mixed $value)
{
if (!$this->definition->hasOption($name)) {
if (!$this->definition->hasNegation($name)) {
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
}

$optionName = $this->definition->negationToName($name);
$this->options[$optionName] = false;

return;
}

$option = $this->definition->getOption($name);

if (null === $value) {
if ($option->isValueRequired()) {
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
}

if (!$option->isValueOptional()) {
$value = true;
}
}

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






private function addArgument(string|int $name, mixed $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}

$this->arguments[$name] = $value;
}
}
<?php










namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\RuntimeException;


























class ArgvInput extends Input
{
private array $tokens;
private array $parsed;

public function __construct(array $argv = null, InputDefinition $definition = null)
{
$argv = $argv ?? $_SERVER['argv'] ?? [];


array_shift($argv);

$this->tokens = $argv;

parent::__construct($definition);
}

protected function setTokens(array $tokens)
{
$this->tokens = $tokens;
}




protected function parse()
{
$parseOptions = true;
$this->parsed = $this->tokens;
while (null !== $token = array_shift($this->parsed)) {
$parseOptions = $this->parseToken($token, $parseOptions);
}
}

protected function parseToken(string $token, bool $parseOptions): bool
{
if ($parseOptions && '' == $token) {
$this->parseArgument($token);
} elseif ($parseOptions && '--' == $token) {
return false;
} elseif ($parseOptions && str_starts_with($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}

return $parseOptions;
}




private function parseShortOption(string $token)
{
$name = substr($token, 1);

if (\strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {

$this->addShortOption($name[0], substr($name, 1));
} else {
$this->parseShortOptionSet($name);
}
} else {
$this->addShortOption($name, null);
}
}






private function parseShortOptionSet(string $name)
{
$len = \strlen($name);
for ($i = 0; $i < $len; ++$i) {
if (!$this->definition->hasShortcut($name[$i])) {
$encoding = mb_detect_encoding($name, null, true);
throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
}

$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptValue()) {
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));

break;
} else {
$this->addLongOption($option->getName(), null);
}
}
}




private function parseLongOption(string $token)
{
$name = substr($token, 2);

if (false !== $pos = strpos($name, '=')) {
if ('' === $value = substr($name, $pos + 1)) {
array_unshift($this->parsed, $value);
}
$this->addLongOption(substr($name, 0, $pos), $value);
} else {
$this->addLongOption($name, null);
}
}






private function parseArgument(string $token)
{
$c = \count($this->arguments);


if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;


} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
$arg = $this->definition->getArgument($c - 1);
$this->arguments[$arg->getName()][] = $token;


} else {
$all = $this->definition->getArguments();
$symfonyCommandName = null;
if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) {
$symfonyCommandName = $this->arguments['command'] ?? null;
unset($all[$key]);
}

if (\count($all)) {
if ($symfonyCommandName) {
$message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all)));
} else {
$message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)));
}
} elseif ($symfonyCommandName) {
$message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token);
} else {
$message = sprintf('No arguments expected, got "%s".', $token);
}

throw new RuntimeException($message);
}
}






private function addShortOption(string $shortcut, mixed $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
}

$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}






private function addLongOption(string $name, mixed $value)
{
if (!$this->definition->hasOption($name)) {
if (!$this->definition->hasNegation($name)) {
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}

$optionName = $this->definition->negationToName($name);
if (null !== $value) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
$this->options[$optionName] = false;

return;
}

$option = $this->definition->getOption($name);

if (null !== $value && !$option->acceptValue()) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}

if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {


$next = array_shift($this->parsed);
if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) {
$value = $next;
} else {
array_unshift($this->parsed, $next);
}
}

if (null === $value) {
if ($option->isValueRequired()) {
throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}

if (!$option->isArray() && !$option->isValueOptional()) {
$value = true;
}
}

if ($option->isArray()) {
$this->options[$name][] = $value;
} else {
$this->options[$name] = $value;
}
}




public function getFirstArgument(): ?string
{
$isOption = false;
foreach ($this->tokens as $i => $token) {
if ($token && '-' === $token[0]) {
if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) {
continue;
}



$name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {

} elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
$isOption = true;
}

continue;
}

if ($isOption) {
$isOption = false;
continue;
}

return $token;
}

return null;
}




public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
{
$values = (array) $values;

foreach ($this->tokens as $token) {
if ($onlyParams && '--' === $token) {
return false;
}
foreach ($values as $value) {



$leading = str_starts_with($value, '--') ? $value.'=' : $value;
if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) {
return true;
}
}
}

return false;
}




public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
{
$values = (array) $values;
$tokens = $this->tokens;

while (0 < \count($tokens)) {
$token = array_shift($tokens);
if ($onlyParams && '--' === $token) {
return $default;
}

foreach ($values as $value) {
if ($token === $value) {
return array_shift($tokens);
}



$leading = str_starts_with($value, '--') ? $value.'=' : $value;
if ('' !== $leading && str_starts_with($token, $leading)) {
return substr($token, \strlen($leading));
}
}
}

return $default;
}




public function __toString(): string
{
$tokens = array_map(function ($token) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1].$this->escapeToken($match[2]);
}

if ($token && '-' !== $token[0]) {
return $this->escapeToken($token);
}

return $token;
}, $this->tokens);

return implode(' ', $tokens);
}
}
<?php










namespace Symfony\Component\Console\CI;

use Symfony\Component\Console\Output\OutputInterface;






class GithubActionReporter
{
private $output;




private const ESCAPED_DATA = [
'%' => '%25',
"\r" => '%0D',
"\n" => '%0A',
];




private const ESCAPED_PROPERTIES = [
'%' => '%25',
"\r" => '%0D',
"\n" => '%0A',
':' => '%3A',
',' => '%2C',
];

public function __construct(OutputInterface $output)
{
$this->output = $output;
}

public static function isGithubActionEnvironment(): bool
{
return false !== getenv('GITHUB_ACTIONS');
}






public function error(string $message, string $file = null, int $line = null, int $col = null): void
{
$this->log('error', $message, $file, $line, $col);
}






public function warning(string $message, string $file = null, int $line = null, int $col = null): void
{
$this->log('warning', $message, $file, $line, $col);
}






public function debug(string $message, string $file = null, int $line = null, int $col = null): void
{
$this->log('debug', $message, $file, $line, $col);
}

private function log(string $type, string $message, string $file = null, int $line = null, int $col = null): void
{

$message = strtr($message, self::ESCAPED_DATA);

if (!$file) {

$this->output->writeln(sprintf('::%s::%s', $type, $message));

return;
}

$this->output->writeln(sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message));
}
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;






interface OutputInterface
{
public const VERBOSITY_QUIET = 16;
public const VERBOSITY_NORMAL = 32;
public const VERBOSITY_VERBOSE = 64;
public const VERBOSITY_VERY_VERBOSE = 128;
public const VERBOSITY_DEBUG = 256;

public const OUTPUT_NORMAL = 1;
public const OUTPUT_RAW = 2;
public const OUTPUT_PLAIN = 4;







public function write(string|iterable $messages, bool $newline = false, int $options = 0);






public function writeln(string|iterable $messages, int $options = 0);




public function setVerbosity(int $level);




public function getVerbosity(): int;




public function isQuiet(): bool;




public function isVerbose(): bool;




public function isVeryVerbose(): bool;




public function isDebug(): bool;




public function setDecorated(bool $decorated);




public function isDecorated(): bool;

public function setFormatter(OutputFormatterInterface $formatter);




public function getFormatter(): OutputFormatterInterface;
}
<?php










namespace Symfony\Component\Console\Output;







interface ConsoleOutputInterface extends OutputInterface
{



public function getErrorOutput(): OutputInterface;

public function setErrorOutput(OutputInterface $error);

public function section(): ConsoleSectionOutput;
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;














class StreamOutput extends Output
{
private $stream;









public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
{
if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
}

$this->stream = $stream;

if (null === $decorated) {
$decorated = $this->hasColorSupport();
}

parent::__construct($verbosity, $decorated, $formatter);
}






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




protected function doWrite(string $message, bool $newline)
{
if ($newline) {
$message .= \PHP_EOL;
}

@fwrite($this->stream, $message);

fflush($this->stream);
}














protected function hasColorSupport(): bool
{

if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
return false;
}

if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}

if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}

return stream_isatty($this->stream);
}
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;














abstract class Output implements OutputInterface
{
private int $verbosity;
private $formatter;






public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
{
$this->verbosity = $verbosity ?? self::VERBOSITY_NORMAL;
$this->formatter = $formatter ?? new OutputFormatter();
$this->formatter->setDecorated($decorated);
}




public function setFormatter(OutputFormatterInterface $formatter)
{
$this->formatter = $formatter;
}




public function getFormatter(): OutputFormatterInterface
{
return $this->formatter;
}




public function setDecorated(bool $decorated)
{
$this->formatter->setDecorated($decorated);
}




public function isDecorated(): bool
{
return $this->formatter->isDecorated();
}




public function setVerbosity(int $level)
{
$this->verbosity = $level;
}




public function getVerbosity(): int
{
return $this->verbosity;
}




public function isQuiet(): bool
{
return self::VERBOSITY_QUIET === $this->verbosity;
}




public function isVerbose(): bool
{
return self::VERBOSITY_VERBOSE <= $this->verbosity;
}




public function isVeryVerbose(): bool
{
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
}




public function isDebug(): bool
{
return self::VERBOSITY_DEBUG <= $this->verbosity;
}




public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $options);
}




public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}

$types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
$type = $types & $options ?: self::OUTPUT_NORMAL;

$verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
$verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;

if ($verbosity > $this->getVerbosity()) {
return;
}

foreach ($messages as $message) {
switch ($type) {
case OutputInterface::OUTPUT_NORMAL:
$message = $this->formatter->format($message);
break;
case OutputInterface::OUTPUT_RAW:
break;
case OutputInterface::OUTPUT_PLAIN:
$message = strip_tags($this->formatter->format($message));
break;
}

$this->doWrite($message ?? '', $newline);
}
}




abstract protected function doWrite(string $message, bool $newline);
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Terminal;





class ConsoleSectionOutput extends StreamOutput
{
private array $content = [];
private int $lines = 0;
private array $sections;
private $terminal;





public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter)
{
parent::__construct($stream, $verbosity, $decorated, $formatter);
array_unshift($sections, $this);
$this->sections = &$sections;
$this->terminal = new Terminal();
}






public function clear(int $lines = null)
{
if (empty($this->content) || !$this->isDecorated()) {
return;
}

if ($lines) {
array_splice($this->content, -($lines * 2)); 
} else {
$lines = $this->lines;
$this->content = [];
}

$this->lines -= $lines;

parent::doWrite($this->popStreamContentUntilCurrentSection($lines), false);
}




public function overwrite(string|iterable $message)
{
$this->clear();
$this->writeln($message);
}

public function getContent(): string
{
return implode('', $this->content);
}




public function addContent(string $input)
{
foreach (explode(\PHP_EOL, $input) as $lineContent) {
$this->lines += ceil($this->getDisplayLength($lineContent) / $this->terminal->getWidth()) ?: 1;
$this->content[] = $lineContent;
$this->content[] = \PHP_EOL;
}
}




protected function doWrite(string $message, bool $newline)
{
if (!$this->isDecorated()) {
parent::doWrite($message, $newline);

return;
}

$erasedContent = $this->popStreamContentUntilCurrentSection();

$this->addContent($message);

parent::doWrite($message, true);
parent::doWrite($erasedContent, false);
}





private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0): string
{
$numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection;
$erasedContent = [];

foreach ($this->sections as $section) {
if ($section === $this) {
break;
}

$numberOfLinesToClear += $section->lines;
$erasedContent[] = $section->getContent();
}

if ($numberOfLinesToClear > 0) {

parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false);

parent::doWrite("\x1b[0J", false);
}

return implode('', array_reverse($erasedContent));
}

private function getDisplayLength(string $text): int
{
return Helper::width(Helper::removeDecoration($this->getFormatter(), str_replace("\t", '        ', $text)));
}
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;






class TrimmedBufferOutput extends Output
{
private int $maxLength;
private string $buffer = '';

public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
{
if ($maxLength <= 0) {
throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength));
}

parent::__construct($verbosity, $decorated, $formatter);
$this->maxLength = $maxLength;
}




public function fetch(): string
{
$content = $this->buffer;
$this->buffer = '';

return $content;
}




protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;

if ($newline) {
$this->buffer .= \PHP_EOL;
}

$this->buffer = substr($this->buffer, 0 - $this->maxLength);
}
}
<?php










namespace Symfony\Component\Console\Output;




class BufferedOutput extends Output
{
private string $buffer = '';




public function fetch(): string
{
$content = $this->buffer;
$this->buffer = '';

return $content;
}




protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;

if ($newline) {
$this->buffer .= \PHP_EOL;
}
}
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\NullOutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;









class NullOutput implements OutputInterface
{
private $formatter;




public function setFormatter(OutputFormatterInterface $formatter)
{

}




public function getFormatter(): OutputFormatterInterface
{

return $this->formatter ??= new NullOutputFormatter();
}




public function setDecorated(bool $decorated)
{

}




public function isDecorated(): bool
{
return false;
}




public function setVerbosity(int $level)
{

}




public function getVerbosity(): int
{
return self::VERBOSITY_QUIET;
}




public function isQuiet(): bool
{
return true;
}




public function isVerbose(): bool
{
return false;
}




public function isVeryVerbose(): bool
{
return false;
}




public function isDebug(): bool
{
return false;
}




public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL)
{

}




public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{

}
}
<?php










namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;















class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
private $stderr;
private array $consoleSectionOutputs = [];






public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
{
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);

if (null === $formatter) {

$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated);

return;
}

$actualDecorated = $this->isDecorated();
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());

if (null === $decorated) {
$this->setDecorated($actualDecorated && $this->stderr->isDecorated());
}
}




public function section(): ConsoleSectionOutput
{
return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter());
}




public function setDecorated(bool $decorated)
{
parent::setDecorated($decorated);
$this->stderr->setDecorated($decorated);
}




public function setFormatter(OutputFormatterInterface $formatter)
{
parent::setFormatter($formatter);
$this->stderr->setFormatter($formatter);
}




public function setVerbosity(int $level)
{
parent::setVerbosity($level);
$this->stderr->setVerbosity($level);
}




public function getErrorOutput(): OutputInterface
{
return $this->stderr;
}




public function setErrorOutput(OutputInterface $error)
{
$this->stderr = $error;
}





protected function hasStdoutSupport(): bool
{
return false === $this->isRunningOS400();
}





protected function hasStderrSupport(): bool
{
return false === $this->isRunningOS400();
}





private function isRunningOS400(): bool
{
$checks = [
\function_exists('php_uname') ? php_uname('s') : '',
getenv('OSTYPE'),
\PHP_OS,
];

return false !== stripos(implode(';', $checks), 'OS400');
}




private function openOutputStream()
{
if (!$this->hasStdoutSupport()) {
return fopen('php://output', 'w');
}


return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w'));
}




private function openErrorStream()
{
if (!$this->hasStderrSupport()) {
return fopen('php://output', 'w');
}


return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w'));
}
}
<?php










namespace Symfony\Component\String;

if (!\function_exists(u::class)) {
function u(?string $string = ''): UnicodeString
{
return new UnicodeString($string ?? '');
}
}

if (!\function_exists(b::class)) {
function b(?string $string = ''): ByteString
{
return new ByteString($string ?? '');
}
}

if (!\function_exists(s::class)) {



function s(?string $string = ''): AbstractString
{
$string = $string ?? '';

return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string);
}
}
<?php








return [
[
768,
879,
],
[
1155,
1159,
],
[
1160,
1161,
],
[
1425,
1469,
],
[
1471,
1471,
],
[
1473,
1474,
],
[
1476,
1477,
],
[
1479,
1479,
],
[
1552,
1562,
],
[
1611,
1631,
],
[
1648,
1648,
],
[
1750,
1756,
],
[
1759,
1764,
],
[
1767,
1768,
],
[
1770,
1773,
],
[
1809,
1809,
],
[
1840,
1866,
],
[
1958,
1968,
],
[
2027,
2035,
],
[
2045,
2045,
],
[
2070,
2073,
],
[
2075,
2083,
],
[
2085,
2087,
],
[
2089,
2093,
],
[
2137,
2139,
],
[
2200,
2207,
],
[
2250,
2273,
],
[
2275,
2306,
],
[
2362,
2362,
],
[
2364,
2364,
],
[
2369,
2376,
],
[
2381,
2381,
],
[
2385,
2391,
],
[
2402,
2403,
],
[
2433,
2433,
],
[
2492,
2492,
],
[
2497,
2500,
],
[
2509,
2509,
],
[
2530,
2531,
],
[
2558,
2558,
],
[
2561,
2562,
],
[
2620,
2620,
],
[
2625,
2626,
],
[
2631,
2632,
],
[
2635,
2637,
],
[
2641,
2641,
],
[
2672,
2673,
],
[
2677,
2677,
],
[
2689,
2690,
],
[
2748,
2748,
],
[
2753,
2757,
],
[
2759,
2760,
],
[
2765,
2765,
],
[
2786,
2787,
],
[
2810,
2815,
],
[
2817,
2817,
],
[
2876,
2876,
],
[
2879,
2879,
],
[
2881,
2884,
],
[
2893,
2893,
],
[
2901,
2902,
],
[
2914,
2915,
],
[
2946,
2946,
],
[
3008,
3008,
],
[
3021,
3021,
],
[
3072,
3072,
],
[
3076,
3076,
],
[
3132,
3132,
],
[
3134,
3136,
],
[
3142,
3144,
],
[
3146,
3149,
],
[
3157,
3158,
],
[
3170,
3171,
],
[
3201,
3201,
],
[
3260,
3260,
],
[
3263,
3263,
],
[
3270,
3270,
],
[
3276,
3277,
],
[
3298,
3299,
],
[
3328,
3329,
],
[
3387,
3388,
],
[
3393,
3396,
],
[
3405,
3405,
],
[
3426,
3427,
],
[
3457,
3457,
],
[
3530,
3530,
],
[
3538,
3540,
],
[
3542,
3542,
],
[
3633,
3633,
],
[
3636,
3642,
],
[
3655,
3662,
],
[
3761,
3761,
],
[
3764,
3772,
],
[
3784,
3790,
],
[
3864,
3865,
],
[
3893,
3893,
],
[
3895,
3895,
],
[
3897,
3897,
],
[
3953,
3966,
],
[
3968,
3972,
],
[
3974,
3975,
],
[
3981,
3991,
],
[
3993,
4028,
],
[
4038,
4038,
],
[
4141,
4144,
],
[
4146,
4151,
],
[
4153,
4154,
],
[
4157,
4158,
],
[
4184,
4185,
],
[
4190,
4192,
],
[
4209,
4212,
],
[
4226,
4226,
],
[
4229,
4230,
],
[
4237,
4237,
],
[
4253,
4253,
],
[
4957,
4959,
],
[
5906,
5908,
],
[
5938,
5939,
],
[
5970,
5971,
],
[
6002,
6003,
],
[
6068,
6069,
],
[
6071,
6077,
],
[
6086,
6086,
],
[
6089,
6099,
],
[
6109,
6109,
],
[
6155,
6157,
],
[
6159,
6159,
],
[
6277,
6278,
],
[
6313,
6313,
],
[
6432,
6434,
],
[
6439,
6440,
],
[
6450,
6450,
],
[
6457,
6459,
],
[
6679,
6680,
],
[
6683,
6683,
],
[
6742,
6742,
],
[
6744,
6750,
],
[
6752,
6752,
],
[
6754,
6754,
],
[
6757,
6764,
],
[
6771,
6780,
],
[
6783,
6783,
],
[
6832,
6845,
],
[
6846,
6846,
],
[
6847,
6862,
],
[
6912,
6915,
],
[
6964,
6964,
],
[
6966,
6970,
],
[
6972,
6972,
],
[
6978,
6978,
],
[
7019,
7027,
],
[
7040,
7041,
],
[
7074,
7077,
],
[
7080,
7081,
],
[
7083,
7085,
],
[
7142,
7142,
],
[
7144,
7145,
],
[
7149,
7149,
],
[
7151,
7153,
],
[
7212,
7219,
],
[
7222,
7223,
],
[
7376,
7378,
],
[
7380,
7392,
],
[
7394,
7400,
],
[
7405,
7405,
],
[
7412,
7412,
],
[
7416,
7417,
],
[
7616,
7679,
],
[
8400,
8412,
],
[
8413,
8416,
],
[
8417,
8417,
],
[
8418,
8420,
],
[
8421,
8432,
],
[
11503,
11505,
],
[
11647,
11647,
],
[
11744,
11775,
],
[
12330,
12333,
],
[
12441,
12442,
],
[
42607,
42607,
],
[
42608,
42610,
],
[
42612,
42621,
],
[
42654,
42655,
],
[
42736,
42737,
],
[
43010,
43010,
],
[
43014,
43014,
],
[
43019,
43019,
],
[
43045,
43046,
],
[
43052,
43052,
],
[
43204,
43205,
],
[
43232,
43249,
],
[
43263,
43263,
],
[
43302,
43309,
],
[
43335,
43345,
],
[
43392,
43394,
],
[
43443,
43443,
],
[
43446,
43449,
],
[
43452,
43453,
],
[
43493,
43493,
],
[
43561,
43566,
],
[
43569,
43570,
],
[
43573,
43574,
],
[
43587,
43587,
],
[
43596,
43596,
],
[
43644,
43644,
],
[
43696,
43696,
],
[
43698,
43700,
],
[
43703,
43704,
],
[
43710,
43711,
],
[
43713,
43713,
],
[
43756,
43757,
],
[
43766,
43766,
],
[
44005,
44005,
],
[
44008,
44008,
],
[
44013,
44013,
],
[
64286,
64286,
],
[
65024,
65039,
],
[
65056,
65071,
],
[
66045,
66045,
],
[
66272,
66272,
],
[
66422,
66426,
],
[
68097,
68099,
],
[
68101,
68102,
],
[
68108,
68111,
],
[
68152,
68154,
],
[
68159,
68159,
],
[
68325,
68326,
],
[
68900,
68903,
],
[
69291,
69292,
],
[
69373,
69375,
],
[
69446,
69456,
],
[
69506,
69509,
],
[
69633,
69633,
],
[
69688,
69702,
],
[
69744,
69744,
],
[
69747,
69748,
],
[
69759,
69761,
],
[
69811,
69814,
],
[
69817,
69818,
],
[
69826,
69826,
],
[
69888,
69890,
],
[
69927,
69931,
],
[
69933,
69940,
],
[
70003,
70003,
],
[
70016,
70017,
],
[
70070,
70078,
],
[
70089,
70092,
],
[
70095,
70095,
],
[
70191,
70193,
],
[
70196,
70196,
],
[
70198,
70199,
],
[
70206,
70206,
],
[
70209,
70209,
],
[
70367,
70367,
],
[
70371,
70378,
],
[
70400,
70401,
],
[
70459,
70460,
],
[
70464,
70464,
],
[
70502,
70508,
],
[
70512,
70516,
],
[
70712,
70719,
],
[
70722,
70724,
],
[
70726,
70726,
],
[
70750,
70750,
],
[
70835,
70840,
],
[
70842,
70842,
],
[
70847,
70848,
],
[
70850,
70851,
],
[
71090,
71093,
],
[
71100,
71101,
],
[
71103,
71104,
],
[
71132,
71133,
],
[
71219,
71226,
],
[
71229,
71229,
],
[
71231,
71232,
],
[
71339,
71339,
],
[
71341,
71341,
],
[
71344,
71349,
],
[
71351,
71351,
],
[
71453,
71455,
],
[
71458,
71461,
],
[
71463,
71467,
],
[
71727,
71735,
],
[
71737,
71738,
],
[
71995,
71996,
],
[
71998,
71998,
],
[
72003,
72003,
],
[
72148,
72151,
],
[
72154,
72155,
],
[
72160,
72160,
],
[
72193,
72202,
],
[
72243,
72248,
],
[
72251,
72254,
],
[
72263,
72263,
],
[
72273,
72278,
],
[
72281,
72283,
],
[
72330,
72342,
],
[
72344,
72345,
],
[
72752,
72758,
],
[
72760,
72765,
],
[
72767,
72767,
],
[
72850,
72871,
],
[
72874,
72880,
],
[
72882,
72883,
],
[
72885,
72886,
],
[
73009,
73014,
],
[
73018,
73018,
],
[
73020,
73021,
],
[
73023,
73029,
],
[
73031,
73031,
],
[
73104,
73105,
],
[
73109,
73109,
],
[
73111,
73111,
],
[
73459,
73460,
],
[
73472,
73473,
],
[
73526,
73530,
],
[
73536,
73536,
],
[
73538,
73538,
],
[
78912,
78912,
],
[
78919,
78933,
],
[
92912,
92916,
],
[
92976,
92982,
],
[
94031,
94031,
],
[
94095,
94098,
],
[
94180,
94180,
],
[
113821,
113822,
],
[
118528,
118573,
],
[
118576,
118598,
],
[
119143,
119145,
],
[
119163,
119170,
],
[
119173,
119179,
],
[
119210,
119213,
],
[
119362,
119364,
],
[
121344,
121398,
],
[
121403,
121452,
],
[
121461,
121461,
],
[
121476,
121476,
],
[
121499,
121503,
],
[
121505,
121519,
],
[
122880,
122886,
],
[
122888,
122904,
],
[
122907,
122913,
],
[
122915,
122916,
],
[
122918,
122922,
],
[
123023,
123023,
],
[
123184,
123190,
],
[
123566,
123566,
],
[
123628,
123631,
],
[
124140,
124143,
],
[
125136,
125142,
],
[
125252,
125258,
],
[
917760,
917999,
],
];
<?php








return [
[
4352,
4447,
],
[
8986,
8987,
],
[
9001,
9001,
],
[
9002,
9002,
],
[
9193,
9196,
],
[
9200,
9200,
],
[
9203,
9203,
],
[
9725,
9726,
],
[
9748,
9749,
],
[
9800,
9811,
],
[
9855,
9855,
],
[
9875,
9875,
],
[
9889,
9889,
],
[
9898,
9899,
],
[
9917,
9918,
],
[
9924,
9925,
],
[
9934,
9934,
],
[
9940,
9940,
],
[
9962,
9962,
],
[
9970,
9971,
],
[
9973,
9973,
],
[
9978,
9978,
],
[
9981,
9981,
],
[
9989,
9989,
],
[
9994,
9995,
],
[
10024,
10024,
],
[
10060,
10060,
],
[
10062,
10062,
],
[
10067,
10069,
],
[
10071,
10071,
],
[
10133,
10135,
],
[
10160,
10160,
],
[
10175,
10175,
],
[
11035,
11036,
],
[
11088,
11088,
],
[
11093,
11093,
],
[
11904,
11929,
],
[
11931,
12019,
],
[
12032,
12245,
],
[
12272,
12283,
],
[
12288,
12288,
],
[
12289,
12291,
],
[
12292,
12292,
],
[
12293,
12293,
],
[
12294,
12294,
],
[
12295,
12295,
],
[
12296,
12296,
],
[
12297,
12297,
],
[
12298,
12298,
],
[
12299,
12299,
],
[
12300,
12300,
],
[
12301,
12301,
],
[
12302,
12302,
],
[
12303,
12303,
],
[
12304,
12304,
],
[
12305,
12305,
],
[
12306,
12307,
],
[
12308,
12308,
],
[
12309,
12309,
],
[
12310,
12310,
],
[
12311,
12311,
],
[
12312,
12312,
],
[
12313,
12313,
],
[
12314,
12314,
],
[
12315,
12315,
],
[
12316,
12316,
],
[
12317,
12317,
],
[
12318,
12319,
],
[
12320,
12320,
],
[
12321,
12329,
],
[
12330,
12333,
],
[
12334,
12335,
],
[
12336,
12336,
],
[
12337,
12341,
],
[
12342,
12343,
],
[
12344,
12346,
],
[
12347,
12347,
],
[
12348,
12348,
],
[
12349,
12349,
],
[
12350,
12350,
],
[
12353,
12438,
],
[
12441,
12442,
],
[
12443,
12444,
],
[
12445,
12446,
],
[
12447,
12447,
],
[
12448,
12448,
],
[
12449,
12538,
],
[
12539,
12539,
],
[
12540,
12542,
],
[
12543,
12543,
],
[
12549,
12591,
],
[
12593,
12686,
],
[
12688,
12689,
],
[
12690,
12693,
],
[
12694,
12703,
],
[
12704,
12735,
],
[
12736,
12771,
],
[
12784,
12799,
],
[
12800,
12830,
],
[
12832,
12841,
],
[
12842,
12871,
],
[
12880,
12880,
],
[
12881,
12895,
],
[
12896,
12927,
],
[
12928,
12937,
],
[
12938,
12976,
],
[
12977,
12991,
],
[
12992,
13055,
],
[
13056,
13311,
],
[
13312,
19903,
],
[
19968,
40959,
],
[
40960,
40980,
],
[
40981,
40981,
],
[
40982,
42124,
],
[
42128,
42182,
],
[
43360,
43388,
],
[
44032,
55203,
],
[
63744,
64109,
],
[
64110,
64111,
],
[
64112,
64217,
],
[
64218,
64255,
],
[
65040,
65046,
],
[
65047,
65047,
],
[
65048,
65048,
],
[
65049,
65049,
],
[
65072,
65072,
],
[
65073,
65074,
],
[
65075,
65076,
],
[
65077,
65077,
],
[
65078,
65078,
],
[
65079,
65079,
],
[
65080,
65080,
],
[
65081,
65081,
],
[
65082,
65082,
],
[
65083,
65083,
],
[
65084,
65084,
],
[
65085,
65085,
],
[
65086,
65086,
],
[
65087,
65087,
],
[
65088,
65088,
],
[
65089,
65089,
],
[
65090,
65090,
],
[
65091,
65091,
],
[
65092,
65092,
],
[
65093,
65094,
],
[
65095,
65095,
],
[
65096,
65096,
],
[
65097,
65100,
],
[
65101,
65103,
],
[
65104,
65106,
],
[
65108,
65111,
],
[
65112,
65112,
],
[
65113,
65113,
],
[
65114,
65114,
],
[
65115,
65115,
],
[
65116,
65116,
],
[
65117,
65117,
],
[
65118,
65118,
],
[
65119,
65121,
],
[
65122,
65122,
],
[
65123,
65123,
],
[
65124,
65126,
],
[
65128,
65128,
],
[
65129,
65129,
],
[
65130,
65131,
],
[
65281,
65283,
],
[
65284,
65284,
],
[
65285,
65287,
],
[
65288,
65288,
],
[
65289,
65289,
],
[
65290,
65290,
],
[
65291,
65291,
],
[
65292,
65292,
],
[
65293,
65293,
],
[
65294,
65295,
],
[
65296,
65305,
],
[
65306,
65307,
],
[
65308,
65310,
],
[
65311,
65312,
],
[
65313,
65338,
],
[
65339,
65339,
],
[
65340,
65340,
],
[
65341,
65341,
],
[
65342,
65342,
],
[
65343,
65343,
],
[
65344,
65344,
],
[
65345,
65370,
],
[
65371,
65371,
],
[
65372,
65372,
],
[
65373,
65373,
],
[
65374,
65374,
],
[
65375,
65375,
],
[
65376,
65376,
],
[
65504,
65505,
],
[
65506,
65506,
],
[
65507,
65507,
],
[
65508,
65508,
],
[
65509,
65510,
],
[
94176,
94177,
],
[
94178,
94178,
],
[
94179,
94179,
],
[
94180,
94180,
],
[
94192,
94193,
],
[
94208,
100343,
],
[
100352,
101119,
],
[
101120,
101589,
],
[
101632,
101640,
],
[
110576,
110579,
],
[
110581,
110587,
],
[
110589,
110590,
],
[
110592,
110847,
],
[
110848,
110882,
],
[
110898,
110898,
],
[
110928,
110930,
],
[
110933,
110933,
],
[
110948,
110951,
],
[
110960,
111355,
],
[
126980,
126980,
],
[
127183,
127183,
],
[
127374,
127374,
],
[
127377,
127386,
],
[
127488,
127490,
],
[
127504,
127547,
],
[
127552,
127560,
],
[
127568,
127569,
],
[
127584,
127589,
],
[
127744,
127776,
],
[
127789,
127797,
],
[
127799,
127868,
],
[
127870,
127891,
],
[
127904,
127946,
],
[
127951,
127955,
],
[
127968,
127984,
],
[
127988,
127988,
],
[
127992,
127994,
],
[
127995,
127999,
],
[
128000,
128062,
],
[
128064,
128064,
],
[
128066,
128252,
],
[
128255,
128317,
],
[
128331,
128334,
],
[
128336,
128359,
],
[
128378,
128378,
],
[
128405,
128406,
],
[
128420,
128420,
],
[
128507,
128511,
],
[
128512,
128591,
],
[
128640,
128709,
],
[
128716,
128716,
],
[
128720,
128722,
],
[
128725,
128727,
],
[
128732,
128735,
],
[
128747,
128748,
],
[
128756,
128764,
],
[
128992,
129003,
],
[
129008,
129008,
],
[
129292,
129338,
],
[
129340,
129349,
],
[
129351,
129535,
],
[
129648,
129660,
],
[
129664,
129672,
],
[
129680,
129725,
],
[
129727,
129733,
],
[
129742,
129755,
],
[
129760,
129768,
],
[
129776,
129784,
],
[
131072,
173791,
],
[
173792,
173823,
],
[
173824,
177977,
],
[
177978,
177983,
],
[
177984,
178205,
],
[
178206,
178207,
],
[
178208,
183969,
],
[
183970,
183983,
],
[
183984,
191456,
],
[
191457,
194559,
],
[
194560,
195101,
],
[
195102,
195103,
],
[
195104,
196605,
],
[
196608,
201546,
],
[
201547,
201551,
],
[
201552,
205743,
],
[
205744,
262141,
],
];
<?php










namespace Symfony\Component\String\Inflector;

final class EnglishInflector implements InflectorInterface
{





private const PLURAL_MAP = [







['a', 1, true, true, ['on', 'um']],


['ea', 2, true, true, 'a'],


['secivres', 8, true, true, 'service'],


['eci', 3, false, true, 'ouse'],


['esee', 4, false, true, 'oose'],


['i', 1, true, true, 'us'],


['nem', 3, true, true, 'man'],


['nerdlihc', 8, true, true, 'child'],


['nexo', 4, false, false, 'ox'],


['seci', 4, false, true, ['ex', 'ix', 'ice']],


['seifles', 7, true, true, 'selfie'],


['seibmoz', 7, true, true, 'zombie'],


['seivom', 6, true, true, 'movie'],


['sesutcep', 8, true, true, 'pectus'],


['teef', 4, true, true, 'foot'],


['eseeg', 5, true, true, 'goose'],


['hteet', 5, true, true, 'tooth'],


['swen', 4, true, true, 'news'],


['seires', 6, true, true, 'series'],


['sei', 3, false, true, 'y'],


['sess', 4, true, false, 'ss'],





['ses', 3, true, true, ['s', 'se', 'sis']],


['sevit', 5, true, true, 'tive'],


['sevird', 6, false, true, 'drive'],


['sevi', 4, false, true, 'ife'],


['sevom', 5, true, true, 'move'],


['sev', 3, true, true, ['f', 've', 'ff']],


['sexa', 4, false, false, ['ax', 'axe', 'axis']],


['sex', 3, true, false, 'x'],


['sezz', 4, true, false, 'z'],


['suae', 4, false, true, 'eau'],


['see', 3, true, true, 'ee'],


['segd', 4, true, true, 'dge'],




['se', 2, true, true, ['', 'e']],


['s', 1, true, true, ''],


['xuae', 4, false, true, 'eau'],


['elpoep', 6, true, true, 'person'],
];






private const SINGULAR_MAP = [







['airetirc', 8, false, false, 'criterion'],


['aluben', 6, false, false, 'nebulae'],


['dlihc', 5, true, true, 'children'],


['eci', 3, false, true, 'ices'],


['ecivres', 7, true, true, 'services'],


['efi', 3, false, true, 'ives'],


['eifles', 6, true, true, 'selfies'],


['eivom', 5, true, true, 'movies'],


['esuol', 5, false, true, 'lice'],


['esuom', 5, false, true, 'mice'],


['esoo', 4, false, true, 'eese'],


['es', 2, true, true, 'ses'],


['esoog', 5, true, true, 'geese'],


['ev', 2, true, true, 'ves'],


['evird', 5, false, true, 'drives'],


['evit', 4, true, true, 'tives'],


['evom', 4, true, true, 'moves'],


['ffats', 5, true, true, 'staves'],


['ff', 2, true, true, 'ffs'],


['f', 1, true, true, ['fs', 'ves']],


['hc', 2, true, true, 'ches'],


['hs', 2, true, true, 'shes'],


['htoot', 5, true, true, 'teeth'],


['mu', 2, true, true, 'a'],


['nam', 3, true, true, 'men'],


['nosrep', 6, true, true, ['persons', 'people']],


['noi', 3, true, true, 'ions'],


['nop', 3, true, true, 'pons'],


['nos', 3, true, true, 'sons'],


['no', 2, true, true, 'a'],


['ohce', 4, true, true, 'echoes'],


['oreh', 4, true, true, 'heroes'],


['salta', 5, true, true, 'atlases'],


['siri', 4, true, true, 'irises'],




['sis', 3, true, true, 'ses'],


['ss', 2, true, false, 'sses'],


['suballys', 8, true, true, 'syllabi'],


['sub', 3, true, true, 'buses'],


['suc', 3, true, true, 'cuses'],


['sutcep', 6, true, true, 'pectuses'],


['su', 2, true, true, 'i'],


['swen', 4, true, true, 'news'],


['toof', 4, true, true, 'feet'],


['uae', 3, false, true, ['eaus', 'eaux']],


['xo', 2, false, false, 'oxen'],


['xaoh', 4, true, false, 'hoaxes'],


['xedni', 5, false, true, ['indicies', 'indexes']],


['xo', 2, false, true, 'oxes'],


['x', 1, true, false, ['cies', 'xes']],


['xi', 2, false, true, 'ices'],


['y', 1, false, true, 'ies'],


['ziuq', 4, true, false, 'quizzes'],


['z', 1, true, true, 'zes'],
];




private const UNINFLECTED = [
'',


'atad',


'reed',


'kcabdeef',


'hsif',


'ofni',


'esoom',


'seires',


'peehs',


'seiceps',
];




public function singularize(string $plural): array
{
$pluralRev = strrev($plural);
$lowerPluralRev = strtolower($pluralRev);
$pluralLength = \strlen($lowerPluralRev);


if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) {
return [$plural];
}





foreach (self::PLURAL_MAP as $map) {
$suffix = $map[0];
$suffixLength = $map[1];
$j = 0;



while ($suffix[$j] === $lowerPluralRev[$j]) {

++$j;



if ($j === $suffixLength) {

if ($j < $pluralLength) {
$nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);

if (!$map[2] && $nextIsVocal) {

break;
}

if (!$map[3] && !$nextIsVocal) {

break;
}
}

$newBase = substr($plural, 0, $pluralLength - $suffixLength);
$newSuffix = $map[4];




$firstUpper = ctype_upper($pluralRev[$j - 1]);

if (\is_array($newSuffix)) {
$singulars = [];

foreach ($newSuffix as $newSuffixEntry) {
$singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
}

return $singulars;
}

return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
}


if ($j === $pluralLength) {
break;
}
}
}


return [$plural];
}




public function pluralize(string $singular): array
{
$singularRev = strrev($singular);
$lowerSingularRev = strtolower($singularRev);
$singularLength = \strlen($lowerSingularRev);


if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) {
return [$singular];
}





foreach (self::SINGULAR_MAP as $map) {
$suffix = $map[0];
$suffixLength = $map[1];
$j = 0;




while ($suffix[$j] === $lowerSingularRev[$j]) {

++$j;



if ($j === $suffixLength) {

if ($j < $singularLength) {
$nextIsVocal = false !== strpos('aeiou', $lowerSingularRev[$j]);

if (!$map[2] && $nextIsVocal) {

break;
}

if (!$map[3] && !$nextIsVocal) {

break;
}
}

$newBase = substr($singular, 0, $singularLength - $suffixLength);
$newSuffix = $map[4];




$firstUpper = ctype_upper($singularRev[$j - 1]);

if (\is_array($newSuffix)) {
$plurals = [];

foreach ($newSuffix as $newSuffixEntry) {
$plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
}

return $plurals;
}

return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
}


if ($j === $singularLength) {
break;
}
}
}


return [$singular.'s'];
}
}
<?php










namespace Symfony\Component\String\Inflector;






final class FrenchInflector implements InflectorInterface
{





private const PLURALIZE_REGEXP = [





['/(s|x|z)$/i', '\1'],



['/(eau)$/i', '\1x'],



['/^(landau)$/i', '\1s'],
['/(au)$/i', '\1x'],



['/^(pneu|bleu|émeu)$/i', '\1s'],
['/(eu)$/i', '\1x'],



['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'],
['/al$/i', '\1aux'],


['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'],


['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'],


['/^(cinquante|soixante|mille)$/i', '\1'],


['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'],
['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'],
];




private const SINGULARIZE_REGEXP = [




['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'],



['/(eau)x$/i', '\1'],



['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'],



['/(au)x$/i', '\1'],



['/(eu)x$/i', '\1'],



['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'],


['/^mes(dame|demoiselle)s$/', 'ma\1'],
['/^Mes(dame|demoiselle)s$/', 'Ma\1'],
['/^mes(sieur|seigneur)s$/', 'mon\1'],
['/^Mes(sieur|seigneur)s$/', 'Mon\1'],


['/s$/i', ''],
];





private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sans|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i';




public function singularize(string $plural): array
{
if ($this->isInflectedWord($plural)) {
return [$plural];
}

foreach (self::SINGULARIZE_REGEXP as $rule) {
[$regexp, $replace] = $rule;

if (1 === preg_match($regexp, $plural)) {
return [preg_replace($regexp, $replace, $plural)];
}
}

return [$plural];
}




public function pluralize(string $singular): array
{
if ($this->isInflectedWord($singular)) {
return [$singular];
}

foreach (self::PLURALIZE_REGEXP as $rule) {
[$regexp, $replace] = $rule;

if (1 === preg_match($regexp, $singular)) {
return [preg_replace($regexp, $replace, $singular)];
}
}

return [$singular.'s'];
}

private function isInflectedWord(string $word): bool
{
return 1 === preg_match(self::UNINFLECTED, $word);
}
}
<?php










namespace Symfony\Component\String\Inflector;

interface InflectorInterface
{







public function singularize(string $plural): array;








public function pluralize(string $singular): array;
}
<?php










namespace Symfony\Component\String\Slugger;

use Symfony\Component\String\AbstractUnicodeString;






interface SluggerInterface
{



public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString;
}
<?php










namespace Symfony\Component\String\Slugger;

use Symfony\Component\String\AbstractUnicodeString;
use Symfony\Component\String\UnicodeString;
use Symfony\Contracts\Translation\LocaleAwareInterface;

if (!interface_exists(LocaleAwareInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".');
}




class AsciiSlugger implements SluggerInterface, LocaleAwareInterface
{
private const LOCALE_TO_TRANSLITERATOR_ID = [
'am' => 'Amharic-Latin',
'ar' => 'Arabic-Latin',
'az' => 'Azerbaijani-Latin',
'be' => 'Belarusian-Latin',
'bg' => 'Bulgarian-Latin',
'bn' => 'Bengali-Latin',
'de' => 'de-ASCII',
'el' => 'Greek-Latin',
'fa' => 'Persian-Latin',
'he' => 'Hebrew-Latin',
'hy' => 'Armenian-Latin',
'ka' => 'Georgian-Latin',
'kk' => 'Kazakh-Latin',
'ky' => 'Kirghiz-Latin',
'ko' => 'Korean-Latin',
'mk' => 'Macedonian-Latin',
'mn' => 'Mongolian-Latin',
'or' => 'Oriya-Latin',
'ps' => 'Pashto-Latin',
'ru' => 'Russian-Latin',
'sr' => 'Serbian-Latin',
'sr_Cyrl' => 'Serbian-Latin',
'th' => 'Thai-Latin',
'tk' => 'Turkmen-Latin',
'uk' => 'Ukrainian-Latin',
'uz' => 'Uzbek-Latin',
'zh' => 'Han-Latin',
];

private ?string $defaultLocale;
private \Closure|array $symbolsMap = [
'en' => ['@' => 'at', '&' => 'and'],
];






private array $transliterators = [];

public function __construct(string $defaultLocale = null, array|\Closure $symbolsMap = null)
{
$this->defaultLocale = $defaultLocale;
$this->symbolsMap = $symbolsMap ?? $this->symbolsMap;
}




public function setLocale(string $locale)
{
$this->defaultLocale = $locale;
}




public function getLocale(): string
{
return $this->defaultLocale;
}




public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString
{
$locale = $locale ?? $this->defaultLocale;

$transliterator = [];
if ($locale && ('de' === $locale || 0 === strpos($locale, 'de_'))) {

$transliterator = ['de-ASCII'];
} elseif (\function_exists('transliterator_transliterate') && $locale) {
$transliterator = (array) $this->createTransliterator($locale);
}

if ($this->symbolsMap instanceof \Closure) {


$symbolsMap = $this->symbolsMap;
array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) {
return $symbolsMap($s, $locale);
});
}

$unicodeString = (new UnicodeString($string))->ascii($transliterator);

if (\is_array($this->symbolsMap)) {
$map = null;
if (isset($this->symbolsMap[$locale])) {
$map = $this->symbolsMap[$locale];
} else {
$parent = self::getParentLocale($locale);
if ($parent && isset($this->symbolsMap[$parent])) {
$map = $this->symbolsMap[$parent];
}
}
if ($map) {
foreach ($map as $char => $replace) {
$unicodeString = $unicodeString->replace($char, ' '.$replace.' ');
}
}
}

return $unicodeString
->replaceMatches('/[^A-Za-z0-9]++/', $separator)
->trim($separator)
;
}

private function createTransliterator(string $locale): ?\Transliterator
{
if (\array_key_exists($locale, $this->transliterators)) {
return $this->transliterators[$locale];
}


if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) {
return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id);
}


if (!$parent = self::getParentLocale($locale)) {
return $this->transliterators[$locale] = null;
}


if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) {
$transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id);
}

return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null;
}

private static function getParentLocale(?string $locale): ?string
{
if (!$locale) {
return null;
}
if (false === $str = strrchr($locale, '_')) {

return null;
}

return substr($locale, 0, -\strlen($str));
}
}
<?php










namespace Symfony\Component\String;

use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;









class CodePointString extends AbstractUnicodeString
{
public function __construct(string $string = '')
{
if ('' !== $string && !preg_match('//u', $string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

$this->string = $string;
}

public function append(string ...$suffix): static
{
$str = clone $this;
$str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix);

if (!preg_match('//u', $str->string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

return $str;
}

public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new InvalidArgumentException('The chunk length must be greater than zero.');
}

if ('' === $this->string) {
return [];
}

$rx = '/(';
while (65535 < $length) {
$rx .= '.{65535}';
$length -= 65535;
}
$rx .= '.{'.$length.'})/us';

$str = clone $this;
$chunks = [];

foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) {
$str->string = $chunk;
$chunks[] = clone $str;
}

return $chunks;
}

public function codePointsAt(int $offset): array
{
$str = $offset ? $this->slice($offset, 1) : $this;

return '' === $str->string ? [] : [mb_ord($str->string, 'UTF-8')];
}

public function endsWith(string|iterable|AbstractString $suffix): bool
{
if ($suffix instanceof AbstractString) {
$suffix = $suffix->string;
} elseif (!\is_string($suffix)) {
return parent::endsWith($suffix);
}

if ('' === $suffix || !preg_match('//u', $suffix)) {
return false;
}

if ($this->ignoreCase) {
return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string);
}

return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix));
}

public function equalsTo(string|iterable|AbstractString $string): bool
{
if ($string instanceof AbstractString) {
$string = $string->string;
} elseif (!\is_string($string)) {
return parent::equalsTo($string);
}

if ('' !== $string && $this->ignoreCase) {
return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
}

return $string === $this->string;
}

public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (!\is_string($needle)) {
return parent::indexOf($needle, $offset);
}

if ('' === $needle) {
return null;
}

$i = $this->ignoreCase ? mb_stripos($this->string, $needle, $offset, 'UTF-8') : mb_strpos($this->string, $needle, $offset, 'UTF-8');

return false === $i ? null : $i;
}

public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (!\is_string($needle)) {
return parent::indexOfLast($needle, $offset);
}

if ('' === $needle) {
return null;
}

$i = $this->ignoreCase ? mb_strripos($this->string, $needle, $offset, 'UTF-8') : mb_strrpos($this->string, $needle, $offset, 'UTF-8');

return false === $i ? null : $i;
}

public function length(): int
{
return mb_strlen($this->string, 'UTF-8');
}

public function prepend(string ...$prefix): static
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string;

if (!preg_match('//u', $str->string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

return $str;
}

public function replace(string $from, string $to): static
{
$str = clone $this;

if ('' === $from || !preg_match('//u', $from)) {
return $str;
}

if ('' !== $to && !preg_match('//u', $to)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

if ($this->ignoreCase) {
$str->string = implode($to, preg_split('{'.preg_quote($from).'}iuD', $this->string));
} else {
$str->string = str_replace($from, $to, $this->string);
}

return $str;
}

public function slice(int $start = 0, int $length = null): static
{
$str = clone $this;
$str->string = mb_substr($this->string, $start, $length, 'UTF-8');

return $str;
}

public function splice(string $replacement, int $start = 0, int $length = null): static
{
if (!preg_match('//u', $replacement)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

$str = clone $this;
$start = $start ? \strlen(mb_substr($this->string, 0, $start, 'UTF-8')) : 0;
$length = $length ? \strlen(mb_substr($this->string, $start, $length, 'UTF-8')) : $length;
$str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX);

return $str;
}

public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (1 > $limit = $limit ?? \PHP_INT_MAX) {
throw new InvalidArgumentException('Split limit must be a positive integer.');
}

if ('' === $delimiter) {
throw new InvalidArgumentException('Split delimiter is empty.');
}

if (null !== $flags) {
return parent::split($delimiter.'u', $limit, $flags);
}

if (!preg_match('//u', $delimiter)) {
throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.');
}

$str = clone $this;
$chunks = $this->ignoreCase
? preg_split('{'.preg_quote($delimiter).'}iuD', $this->string, $limit)
: explode($delimiter, $this->string, $limit);

foreach ($chunks as &$chunk) {
$str->string = $chunk;
$chunk = clone $str;
}

return $chunks;
}

public function startsWith(string|iterable|AbstractString $prefix): bool
{
if ($prefix instanceof AbstractString) {
$prefix = $prefix->string;
} elseif (!\is_string($prefix)) {
return parent::startsWith($prefix);
}

if ('' === $prefix || !preg_match('//u', $prefix)) {
return false;
}

if ($this->ignoreCase) {
return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8');
}

return 0 === strncmp($this->string, $prefix, \strlen($prefix));
}
}
<?php










namespace Symfony\Component\String;






class LazyString implements \Stringable, \JsonSerializable
{
private \Closure|string $value;




public static function fromCallable(callable|array $callback, mixed ...$arguments): static
{
if (\is_array($callback) && !\is_callable($callback) && !(($callback[0] ?? null) instanceof \Closure || 2 < \count($callback))) {
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a callable or a [Closure, method] lazy-callable, "%s" given.', __METHOD__, '['.implode(', ', array_map('get_debug_type', $callback)).']'));
}

$lazyString = new static();
$lazyString->value = static function () use (&$callback, &$arguments, &$value): string {
if (null !== $arguments) {
if (!\is_callable($callback)) {
$callback[0] = $callback[0]();
$callback[1] = $callback[1] ?? '__invoke';
}
$value = $callback(...$arguments);
$callback = self::getPrettyName($callback);
$arguments = null;
}

return $value ?? '';
};

return $lazyString;
}

public static function fromStringable(string|int|float|bool|\Stringable $value): static
{
if (\is_object($value)) {
return static::fromCallable([$value, '__toString']);
}

$lazyString = new static();
$lazyString->value = (string) $value;

return $lazyString;
}




final public static function isStringable(mixed $value): bool
{
return \is_string($value) || $value instanceof \Stringable || \is_scalar($value);
}






final public static function resolve(\Stringable|string|int|float|bool $value): string
{
return $value;
}

public function __toString(): string
{
if (\is_string($this->value)) {
return $this->value;
}

try {
return $this->value = ($this->value)();
} catch (\Throwable $e) {
if (\TypeError::class === \get_class($e) && __FILE__ === $e->getFile()) {
$type = explode(', ', $e->getMessage());
$type = substr(array_pop($type), 0, -\strlen(' returned'));
$r = new \ReflectionFunction($this->value);
$callback = $r->getStaticVariables()['callback'];

$e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type));
}

throw $e;
}
}

public function __sleep(): array
{
$this->__toString();

return ['value'];
}

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

private function __construct()
{
}

private static function getPrettyName(callable $callback): string
{
if (\is_string($callback)) {
return $callback;
}

if (\is_array($callback)) {
$class = \is_object($callback[0]) ? get_debug_type($callback[0]) : $callback[0];
$method = $callback[1];
} elseif ($callback instanceof \Closure) {
$r = new \ReflectionFunction($callback);

if (false !== strpos($r->name, '{closure}') || !$class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
return $r->name;
}

$class = $class->name;
$method = $r->name;
} else {
$class = get_debug_type($callback);
$method = '__invoke';
}

return $class.'::'.$method;
}
}
<?php










namespace Symfony\Component\String;

use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
use Symfony\Component\String\Exception\RuntimeException;









class ByteString extends AbstractString
{
private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';

public function __construct(string $string = '')
{
$this->string = $string;
}











public static function fromRandom(int $length = 16, string $alphabet = null): self
{
if ($length <= 0) {
throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length));
}

$alphabet = $alphabet ?? self::ALPHABET_ALPHANUMERIC;
$alphabetSize = \strlen($alphabet);
$bits = (int) ceil(log($alphabetSize, 2.0));
if ($bits <= 0 || $bits > 56) {
throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.');
}

$ret = '';
while ($length > 0) {
$urandomLength = (int) ceil(2 * $length * $bits / 8.0);
$data = random_bytes($urandomLength);
$unpackedData = 0;
$unpackedBits = 0;
for ($i = 0; $i < $urandomLength && $length > 0; ++$i) {

$unpackedData = ($unpackedData << 8) | \ord($data[$i]);
$unpackedBits += 8;



for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) {
$index = ($unpackedData & ((1 << $bits) - 1));
$unpackedData >>= $bits;



if ($index < $alphabetSize) {
$ret .= $alphabet[$index];
--$length;
}
}
}
}

return new static($ret);
}

public function bytesAt(int $offset): array
{
$str = $this->string[$offset] ?? '';

return '' === $str ? [] : [\ord($str)];
}

public function append(string ...$suffix): static
{
$str = clone $this;
$str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix);

return $str;
}

public function camel(): static
{
$str = clone $this;

$parts = explode(' ', trim(ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string))));
$parts[0] = 1 !== \strlen($parts[0]) && ctype_upper($parts[0]) ? $parts[0] : lcfirst($parts[0]);
$str->string = implode('', $parts);

return $str;
}

public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new InvalidArgumentException('The chunk length must be greater than zero.');
}

if ('' === $this->string) {
return [];
}

$str = clone $this;
$chunks = [];

foreach (str_split($this->string, $length) as $chunk) {
$str->string = $chunk;
$chunks[] = clone $str;
}

return $chunks;
}

public function endsWith(string|iterable|AbstractString $suffix): bool
{
if ($suffix instanceof AbstractString) {
$suffix = $suffix->string;
} elseif (!\is_string($suffix)) {
return parent::endsWith($suffix);
}

return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase);
}

public function equalsTo(string|iterable|AbstractString $string): bool
{
if ($string instanceof AbstractString) {
$string = $string->string;
} elseif (!\is_string($string)) {
return parent::equalsTo($string);
}

if ('' !== $string && $this->ignoreCase) {
return 0 === strcasecmp($string, $this->string);
}

return $string === $this->string;
}

public function folded(): static
{
$str = clone $this;
$str->string = strtolower($str->string);

return $str;
}

public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (!\is_string($needle)) {
return parent::indexOf($needle, $offset);
}

if ('' === $needle) {
return null;
}

$i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset);

return false === $i ? null : $i;
}

public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (!\is_string($needle)) {
return parent::indexOfLast($needle, $offset);
}

if ('' === $needle) {
return null;
}

$i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset);

return false === $i ? null : $i;
}

public function isUtf8(): bool
{
return '' === $this->string || preg_match('//u', $this->string);
}

public function join(array $strings, string $lastGlue = null): static
{
$str = clone $this;

$tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : '';
$str->string = implode($this->string, $strings).$tail;

return $str;
}

public function length(): int
{
return \strlen($this->string);
}

public function lower(): static
{
$str = clone $this;
$str->string = strtolower($str->string);

return $str;
}

public function match(string $regexp, int $flags = 0, int $offset = 0): array
{
$match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match';

if ($this->ignoreCase) {
$regexp .= 'i';
}

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) {
$lastError = preg_last_error();

foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}

throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}

return $matches;
}

public function padBoth(int $length, string $padStr = ' '): static
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH);

return $str;
}

public function padEnd(int $length, string $padStr = ' '): static
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT);

return $str;
}

public function padStart(int $length, string $padStr = ' '): static
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT);

return $str;
}

public function prepend(string ...$prefix): static
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string;

return $str;
}

public function replace(string $from, string $to): static
{
$str = clone $this;

if ('' !== $from) {
$str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string);
}

return $str;
}

public function replaceMatches(string $fromRegexp, string|callable $to): static
{
if ($this->ignoreCase) {
$fromRegexp .= 'i';
}

$replace = \is_array($to) || $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace';

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
if (null === $string = $replace($fromRegexp, $to, $this->string)) {
$lastError = preg_last_error();

foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}

throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}

$str = clone $this;
$str->string = $string;

return $str;
}

public function reverse(): static
{
$str = clone $this;
$str->string = strrev($str->string);

return $str;
}

public function slice(int $start = 0, int $length = null): static
{
$str = clone $this;
$str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX);

return $str;
}

public function snake(): static
{
$str = $this->camel();
$str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string));

return $str;
}

public function splice(string $replacement, int $start = 0, int $length = null): static
{
$str = clone $this;
$str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX);

return $str;
}

public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (1 > $limit = $limit ?? \PHP_INT_MAX) {
throw new InvalidArgumentException('Split limit must be a positive integer.');
}

if ('' === $delimiter) {
throw new InvalidArgumentException('Split delimiter is empty.');
}

if (null !== $flags) {
return parent::split($delimiter, $limit, $flags);
}

$str = clone $this;
$chunks = $this->ignoreCase
? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit)
: explode($delimiter, $this->string, $limit);

foreach ($chunks as &$chunk) {
$str->string = $chunk;
$chunk = clone $str;
}

return $chunks;
}

public function startsWith(string|iterable|AbstractString $prefix): bool
{
if ($prefix instanceof AbstractString) {
$prefix = $prefix->string;
} elseif (!\is_string($prefix)) {
return parent::startsWith($prefix);
}

return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix)));
}

public function title(bool $allWords = false): static
{
$str = clone $this;
$str->string = $allWords ? ucwords($str->string) : ucfirst($str->string);

return $str;
}

public function toUnicodeString(string $fromEncoding = null): UnicodeString
{
return new UnicodeString($this->toCodePointString($fromEncoding)->string);
}

public function toCodePointString(string $fromEncoding = null): CodePointString
{
$u = new CodePointString();

if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) {
$u->string = $this->string;

return $u;
}

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
try {
$validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true);
} catch (InvalidArgumentException $e) {
if (!\function_exists('iconv')) {
throw $e;
}

$u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string);

return $u;
}
} finally {
restore_error_handler();
}

if (!$validEncoding) {
throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252'));
}

$u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252');

return $u;
}

public function trim(string $chars = " \t\n\r\0\x0B\x0C"): static
{
$str = clone $this;
$str->string = trim($str->string, $chars);

return $str;
}

public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): static
{
$str = clone $this;
$str->string = rtrim($str->string, $chars);

return $str;
}

public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): static
{
$str = clone $this;
$str->string = ltrim($str->string, $chars);

return $str;
}

public function upper(): static
{
$str = clone $this;
$str->string = strtoupper($str->string);

return $str;
}

public function width(bool $ignoreAnsiDecoration = true): int
{
$string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string);

return (new CodePointString($string))->width($ignoreAnsiDecoration);
}
}
<?php










namespace Symfony\Component\String;

use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
use Symfony\Component\String\Exception\RuntimeException;













abstract class AbstractString implements \Stringable, \JsonSerializable
{
public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER;
public const PREG_SET_ORDER = \PREG_SET_ORDER;
public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE;
public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL;

public const PREG_SPLIT = 0;
public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY;
public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE;
public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE;

protected $string = '';
protected $ignoreCase = false;

abstract public function __construct(string $string = '');






public static function unwrap(array $values): array
{
foreach ($values as $k => $v) {
if ($v instanceof self) {
$values[$k] = $v->__toString();
} elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) {
$values[$k] = $v;
}
}

return $values;
}






public static function wrap(array $values): array
{
$i = 0;
$keys = null;

foreach ($values as $k => $v) {
if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) {
$keys = $keys ?? array_keys($values);
$keys[$i] = $j;
}

if (\is_string($v)) {
$values[$k] = new static($v);
} elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) {
$values[$k] = $v;
}

++$i;
}

return null !== $keys ? array_combine($keys, $values) : $values;
}




public function after(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static
{
$str = clone $this;
$i = \PHP_INT_MAX;

if (\is_string($needle)) {
$needle = [$needle];
}

foreach ($needle as $n) {
$n = (string) $n;
$j = $this->indexOf($n, $offset);

if (null !== $j && $j < $i) {
$i = $j;
$str->string = $n;
}
}

if (\PHP_INT_MAX === $i) {
return $str;
}

if (!$includeNeedle) {
$i += $str->length();
}

return $this->slice($i);
}




public function afterLast(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static
{
$str = clone $this;
$i = null;

if (\is_string($needle)) {
$needle = [$needle];
}

foreach ($needle as $n) {
$n = (string) $n;
$j = $this->indexOfLast($n, $offset);

if (null !== $j && $j >= $i) {
$i = $offset = $j;
$str->string = $n;
}
}

if (null === $i) {
return $str;
}

if (!$includeNeedle) {
$i += $str->length();
}

return $this->slice($i);
}

abstract public function append(string ...$suffix): static;




public function before(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static
{
$str = clone $this;
$i = \PHP_INT_MAX;

if (\is_string($needle)) {
$needle = [$needle];
}

foreach ($needle as $n) {
$n = (string) $n;
$j = $this->indexOf($n, $offset);

if (null !== $j && $j < $i) {
$i = $j;
$str->string = $n;
}
}

if (\PHP_INT_MAX === $i) {
return $str;
}

if ($includeNeedle) {
$i += $str->length();
}

return $this->slice(0, $i);
}




public function beforeLast(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static
{
$str = clone $this;
$i = null;

if (\is_string($needle)) {
$needle = [$needle];
}

foreach ($needle as $n) {
$n = (string) $n;
$j = $this->indexOfLast($n, $offset);

if (null !== $j && $j >= $i) {
$i = $offset = $j;
$str->string = $n;
}
}

if (null === $i) {
return $str;
}

if ($includeNeedle) {
$i += $str->length();
}

return $this->slice(0, $i);
}




public function bytesAt(int $offset): array
{
$str = $this->slice($offset, 1);

return '' === $str->string ? [] : array_values(unpack('C*', $str->string));
}

abstract public function camel(): static;




abstract public function chunk(int $length = 1): array;

public function collapseWhitespace(): static
{
$str = clone $this;
$str->string = trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $str->string), " \n\r\t\x0C");

return $str;
}




public function containsAny(string|iterable $needle): bool
{
return null !== $this->indexOf($needle);
}




public function endsWith(string|iterable $suffix): bool
{
if (\is_string($suffix)) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}

foreach ($suffix as $s) {
if ($this->endsWith((string) $s)) {
return true;
}
}

return false;
}

public function ensureEnd(string $suffix): static
{
if (!$this->endsWith($suffix)) {
return $this->append($suffix);
}

$suffix = preg_quote($suffix);
$regex = '{('.$suffix.')(?:'.$suffix.')++$}D';

return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1');
}

public function ensureStart(string $prefix): static
{
$prefix = new static($prefix);

if (!$this->startsWith($prefix)) {
return $this->prepend($prefix);
}

$str = clone $this;
$i = $prefixLen = $prefix->length();

while ($this->indexOf($prefix, $i) === $i) {
$str = $str->slice($prefixLen);
$i += $prefixLen;
}

return $str;
}




public function equalsTo(string|iterable $string): bool
{
if (\is_string($string)) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}

foreach ($string as $s) {
if ($this->equalsTo((string) $s)) {
return true;
}
}

return false;
}

abstract public function folded(): static;

public function ignoreCase(): static
{
$str = clone $this;
$str->ignoreCase = true;

return $str;
}




public function indexOf(string|iterable $needle, int $offset = 0): ?int
{
if (\is_string($needle)) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}

$i = \PHP_INT_MAX;

foreach ($needle as $n) {
$j = $this->indexOf((string) $n, $offset);

if (null !== $j && $j < $i) {
$i = $j;
}
}

return \PHP_INT_MAX === $i ? null : $i;
}




public function indexOfLast(string|iterable $needle, int $offset = 0): ?int
{
if (\is_string($needle)) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}

$i = null;

foreach ($needle as $n) {
$j = $this->indexOfLast((string) $n, $offset);

if (null !== $j && $j >= $i) {
$i = $offset = $j;
}
}

return $i;
}

public function isEmpty(): bool
{
return '' === $this->string;
}

abstract public function join(array $strings, string $lastGlue = null): static;

public function jsonSerialize(): string
{
return $this->string;
}

abstract public function length(): int;

abstract public function lower(): static;








abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array;

abstract public function padBoth(int $length, string $padStr = ' '): static;

abstract public function padEnd(int $length, string $padStr = ' '): static;

abstract public function padStart(int $length, string $padStr = ' '): static;

abstract public function prepend(string ...$prefix): static;

public function repeat(int $multiplier): static
{
if (0 > $multiplier) {
throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier));
}

$str = clone $this;
$str->string = str_repeat($str->string, $multiplier);

return $str;
}

abstract public function replace(string $from, string $to): static;

abstract public function replaceMatches(string $fromRegexp, string|callable $to): static;

abstract public function reverse(): static;

abstract public function slice(int $start = 0, int $length = null): static;

abstract public function snake(): static;

abstract public function splice(string $replacement, int $start = 0, int $length = null): static;




public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (null === $flags) {
throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.');
}

if ($this->ignoreCase) {
$delimiter .= 'i';
}

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) {
$lastError = preg_last_error();

foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Splitting failed with '.$k.'.');
}
}

throw new RuntimeException('Splitting failed with unknown error code.');
}
} finally {
restore_error_handler();
}

$str = clone $this;

if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) {
foreach ($chunks as &$chunk) {
$str->string = $chunk[0];
$chunk[0] = clone $str;
}
} else {
foreach ($chunks as &$chunk) {
$str->string = $chunk;
$chunk = clone $str;
}
}

return $chunks;
}




public function startsWith(string|iterable $prefix): bool
{
if (\is_string($prefix)) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}

foreach ($prefix as $prefix) {
if ($this->startsWith((string) $prefix)) {
return true;
}
}

return false;
}

abstract public function title(bool $allWords = false): static;

public function toByteString(string $toEncoding = null): ByteString
{
$b = new ByteString();

$toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding;

if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') {
$b->string = $this->string;

return $b;
}

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
try {
$b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8');
} catch (InvalidArgumentException $e) {
if (!\function_exists('iconv')) {
throw $e;
}

$b->string = iconv('UTF-8', $toEncoding, $this->string);
}
} finally {
restore_error_handler();
}

return $b;
}

public function toCodePointString(): CodePointString
{
return new CodePointString($this->string);
}

public function toString(): string
{
return $this->string;
}

public function toUnicodeString(): UnicodeString
{
return new UnicodeString($this->string);
}

abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static;

abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static;




public function trimPrefix($prefix): static
{
if (\is_array($prefix) || $prefix instanceof \Traversable) {
foreach ($prefix as $s) {
$t = $this->trimPrefix($s);

if ($t->string !== $this->string) {
return $t;
}
}

return clone $this;
}

$str = clone $this;

if ($prefix instanceof self) {
$prefix = $prefix->string;
} else {
$prefix = (string) $prefix;
}

if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) {
$str->string = substr($this->string, \strlen($prefix));
}

return $str;
}

abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static;




public function trimSuffix($suffix): static
{
if (\is_array($suffix) || $suffix instanceof \Traversable) {
foreach ($suffix as $s) {
$t = $this->trimSuffix($s);

if ($t->string !== $this->string) {
return $t;
}
}

return clone $this;
}

$str = clone $this;

if ($suffix instanceof self) {
$suffix = $suffix->string;
} else {
$suffix = (string) $suffix;
}

if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) {
$str->string = substr($this->string, 0, -\strlen($suffix));
}

return $str;
}

public function truncate(int $length, string $ellipsis = '', bool $cut = true): static
{
$stringLength = $this->length();

if ($stringLength <= $length) {
return clone $this;
}

$ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0;

if ($length < $ellipsisLength) {
$ellipsisLength = 0;
}

if (!$cut) {
if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
return clone $this;
}

$length += $ellipsisLength;
}

$str = $this->slice(0, $length - $ellipsisLength);

return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str;
}

abstract public function upper(): static;




abstract public function width(bool $ignoreAnsiDecoration = true): int;

public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): static
{
$lines = '' !== $break ? $this->split($break) : [clone $this];
$chars = [];
$mask = '';

if (1 === \count($lines) && '' === $lines[0]->string) {
return $lines[0];
}

foreach ($lines as $i => $line) {
if ($i) {
$chars[] = $break;
$mask .= '#';
}

foreach ($line->chunk() as $char) {
$chars[] = $char->string;
$mask .= ' ' === $char->string ? ' ' : '?';
}
}

$string = '';
$j = 0;
$b = $i = -1;
$mask = wordwrap($mask, $width, '#', $cut);

while (false !== $b = strpos($mask, '#', $b + 1)) {
for (++$i; $i < $b; ++$i) {
$string .= $chars[$j];
unset($chars[$j++]);
}

if ($break === $chars[$j] || ' ' === $chars[$j]) {
unset($chars[$j++]);
}

$string .= $break;
}

$str = clone $this;
$str->string = $string.implode('', $chars);

return $str;
}

public function __sleep(): array
{
return ['string'];
}

public function __clone()
{
$this->ignoreCase = false;
}

public function __toString(): string
{
return $this->string;
}
}
<?php










namespace Symfony\Component\String;

use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;

















class UnicodeString extends AbstractUnicodeString
{
public function __construct(string $string = '')
{
$this->string = normalizer_is_normalized($string) ? $string : normalizer_normalize($string);

if (false === $this->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
}

public function append(string ...$suffix): static
{
$str = clone $this;
$str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix));
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);

if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

return $str;
}

public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new InvalidArgumentException('The chunk length must be greater than zero.');
}

if ('' === $this->string) {
return [];
}

$rx = '/(';
while (65535 < $length) {
$rx .= '\X{65535}';
$length -= 65535;
}
$rx .= '\X{'.$length.'})/u';

$str = clone $this;
$chunks = [];

foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) {
$str->string = $chunk;
$chunks[] = clone $str;
}

return $chunks;
}

public function endsWith(string|iterable|AbstractString $suffix): bool
{
if ($suffix instanceof AbstractString) {
$suffix = $suffix->string;
} elseif (!\is_string($suffix)) {
return parent::endsWith($suffix);
}

$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form);

if ('' === $suffix || false === $suffix) {
return false;
}

if ($this->ignoreCase) {
return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8');
}

return $suffix === grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix));
}

public function equalsTo(string|iterable|AbstractString $string): bool
{
if ($string instanceof AbstractString) {
$string = $string->string;
} elseif (!\is_string($string)) {
return parent::equalsTo($string);
}

$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form);

if ('' !== $string && false !== $string && $this->ignoreCase) {
return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
}

return $string === $this->string;
}

public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (!\is_string($needle)) {
return parent::indexOf($needle, $offset);
}

$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form);

if ('' === $needle || false === $needle) {
return null;
}

try {
$i = $this->ignoreCase ? grapheme_stripos($this->string, $needle, $offset) : grapheme_strpos($this->string, $needle, $offset);
} catch (\ValueError $e) {
return null;
}

return false === $i ? null : $i;
}

public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (!\is_string($needle)) {
return parent::indexOfLast($needle, $offset);
}

$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form);

if ('' === $needle || false === $needle) {
return null;
}

$string = $this->string;

if (0 > $offset) {

if (0 > $offset += grapheme_strlen($needle)) {
$string = grapheme_substr($string, 0, $offset);
}
$offset = 0;
}

$i = $this->ignoreCase ? grapheme_strripos($string, $needle, $offset) : grapheme_strrpos($string, $needle, $offset);

return false === $i ? null : $i;
}

public function join(array $strings, string $lastGlue = null): static
{
$str = parent::join($strings, $lastGlue);
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);

return $str;
}

public function length(): int
{
return grapheme_strlen($this->string);
}

public function normalize(int $form = self::NFC): static
{
$str = clone $this;

if (\in_array($form, [self::NFC, self::NFKC], true)) {
normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form);
} elseif (!\in_array($form, [self::NFD, self::NFKD], true)) {
throw new InvalidArgumentException('Unsupported normalization form.');
} elseif (!normalizer_is_normalized($str->string, $form)) {
$str->string = normalizer_normalize($str->string, $form);
$str->ignoreCase = null;
}

return $str;
}

public function prepend(string ...$prefix): static
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string;
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);

if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

return $str;
}

public function replace(string $from, string $to): static
{
$str = clone $this;
normalizer_is_normalized($from) ?: $from = normalizer_normalize($from);

if ('' !== $from && false !== $from) {
$tail = $str->string;
$result = '';
$indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos';

while ('' !== $tail && false !== $i = $indexOf($tail, $from)) {
$slice = grapheme_substr($tail, 0, $i);
$result .= $slice.$to;
$tail = substr($tail, \strlen($slice) + \strlen($from));
}

$str->string = $result.$tail;
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);

if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
}

return $str;
}

public function replaceMatches(string $fromRegexp, string|callable $to): static
{
$str = parent::replaceMatches($fromRegexp, $to);
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);

return $str;
}

public function slice(int $start = 0, int $length = null): static
{
$str = clone $this;

$str->string = (string) grapheme_substr($this->string, $start, $length ?? 2147483647);

return $str;
}

public function splice(string $replacement, int $start = 0, int $length = null): static
{
$str = clone $this;

$start = $start ? \strlen(grapheme_substr($this->string, 0, $start)) : 0;
$length = $length ? \strlen(grapheme_substr($this->string, $start, $length ?? 2147483647)) : $length;
$str->string = substr_replace($this->string, $replacement, $start, $length ?? 2147483647);
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);

if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

return $str;
}

public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (1 > $limit = $limit ?? 2147483647) {
throw new InvalidArgumentException('Split limit must be a positive integer.');
}

if ('' === $delimiter) {
throw new InvalidArgumentException('Split delimiter is empty.');
}

if (null !== $flags) {
return parent::split($delimiter.'u', $limit, $flags);
}

normalizer_is_normalized($delimiter) ?: $delimiter = normalizer_normalize($delimiter);

if (false === $delimiter) {
throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.');
}

$str = clone $this;
$tail = $this->string;
$chunks = [];
$indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos';

while (1 < $limit && false !== $i = $indexOf($tail, $delimiter)) {
$str->string = grapheme_substr($tail, 0, $i);
$chunks[] = clone $str;
$tail = substr($tail, \strlen($str->string) + \strlen($delimiter));
--$limit;
}

$str->string = $tail;
$chunks[] = clone $str;

return $chunks;
}

public function startsWith(string|iterable|AbstractString $prefix): bool
{
if ($prefix instanceof AbstractString) {
$prefix = $prefix->string;
} elseif (!\is_string($prefix)) {
return parent::startsWith($prefix);
}

$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form);

if ('' === $prefix || false === $prefix) {
return false;
}

if ($this->ignoreCase) {
return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8');
}

return $prefix === grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES);
}

public function __wakeup()
{
if (!\is_string($this->string)) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}

normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
}

public function __clone()
{
if (null === $this->ignoreCase) {
normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
}

$this->ignoreCase = false;
}
}
<?php










namespace Symfony\Component\String;

use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
use Symfony\Component\String\Exception\RuntimeException;












abstract class AbstractUnicodeString extends AbstractString
{
public const NFC = \Normalizer::NFC;
public const NFD = \Normalizer::NFD;
public const NFKC = \Normalizer::NFKC;
public const NFKD = \Normalizer::NFKD;


private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";


private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'İ', 'ŉ', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ﬀ', 'ﬁ', 'ﬂ', 'ﬃ', 'ﬄ', 'ﬅ', 'ﬆ', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ'];
private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'i̇', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ'];


private const UPPER_FROM = ['ß', 'ﬀ', 'ﬁ', 'ﬂ', 'ﬃ', 'ﬄ', 'ﬅ', 'ﬆ', 'և', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ', 'ŉ', 'ΐ', 'ΰ', 'ǰ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾶ', 'ῆ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῶ'];
private const UPPER_TO = ['SS', 'FF', 'FI', 'FL', 'FFI', 'FFL', 'ST', 'ST', 'ԵՒ', 'ՄՆ', 'ՄԵ', 'ՄԻ', 'ՎՆ', 'ՄԽ', 'ʼN', 'Ϊ́', 'Ϋ́', 'J̌', 'H̱', 'T̈', 'W̊', 'Y̊', 'Aʾ', 'Υ̓', 'Υ̓̀', 'Υ̓́', 'Υ̓͂', 'Α͂', 'Η͂', 'Ϊ̀', 'Ϊ́', 'Ι͂', 'Ϊ͂', 'Ϋ̀', 'Ϋ́', 'Ρ̓', 'Υ͂', 'Ϋ͂', 'Ω͂'];


private const TRANSLIT_FROM = ['Æ', 'Ð', 'Ø', 'Þ', 'ß', 'æ', 'ð', 'ø', 'þ', 'Đ', 'đ', 'Ħ', 'ħ', 'ı', 'ĸ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'ŉ', 'Ŋ', 'ŋ', 'Œ', 'œ', 'Ŧ', 'ŧ', 'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƈ', 'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'ƕ', 'Ɩ', 'Ɨ', 'Ƙ', 'ƙ', 'ƚ', 'Ɲ', 'ƞ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'ƫ', 'Ƭ', 'ƭ', 'Ʈ', 'Ʋ', 'Ƴ', 'ƴ', 'Ƶ', 'ƶ', 'Ǆ', 'ǅ', 'ǆ', 'Ǥ', 'ǥ', 'ȡ', 'Ȥ', 'ȥ', 'ȴ', 'ȵ', 'ȶ', 'ȷ', 'ȸ', 'ȹ', 'Ⱥ', 'Ȼ', 'ȼ', 'Ƚ', 'Ⱦ', 'ȿ', 'ɀ', 'Ƀ', 'Ʉ', 'Ɇ', 'ɇ', 'Ɉ', 'ɉ', 'Ɍ', 'ɍ', 'Ɏ', 'ɏ', 'ɓ', 'ɕ', 'ɖ', 'ɗ', 'ɛ', 'ɟ', 'ɠ', 'ɡ', 'ɢ', 'ɦ', 'ɧ', 'ɨ', 'ɪ', 'ɫ', 'ɬ', 'ɭ', 'ɱ', 'ɲ', 'ɳ', 'ɴ', 'ɶ', 'ɼ', 'ɽ', 'ɾ', 'ʀ', 'ʂ', 'ʈ', 'ʉ', 'ʋ', 'ʏ', 'ʐ', 'ʑ', 'ʙ', 'ʛ', 'ʜ', 'ʝ', 'ʟ', 'ʠ', 'ʣ', 'ʥ', 'ʦ', 'ʪ', 'ʫ', 'ᴀ', 'ᴁ', 'ᴃ', 'ᴄ', 'ᴅ', 'ᴆ', 'ᴇ', 'ᴊ', 'ᴋ', 'ᴌ', 'ᴍ', 'ᴏ', 'ᴘ', 'ᴛ', 'ᴜ', 'ᴠ', 'ᴡ', 'ᴢ', 'ᵫ', 'ᵬ', 'ᵭ', 'ᵮ', 'ᵯ', 'ᵰ', 'ᵱ', 'ᵲ', 'ᵳ', 'ᵴ', 'ᵵ', 'ᵶ', 'ᵺ', 'ᵻ', 'ᵽ', 'ᵾ', 'ᶀ', 'ᶁ', 'ᶂ', 'ᶃ', 'ᶄ', 'ᶅ', 'ᶆ', 'ᶇ', 'ᶈ', 'ᶉ', 'ᶊ', 'ᶌ', 'ᶍ', 'ᶎ', 'ᶏ', 'ᶑ', 'ᶒ', 'ᶓ', 'ᶖ', 'ᶙ', 'ẚ', 'ẜ', 'ẝ', 'ẞ', 'Ỻ', 'ỻ', 'Ỽ', 'ỽ', 'Ỿ', 'ỿ', '©', '®', '₠', '₢', '₣', '₤', '₧', '₺', '₹', 'ℌ', '℞', '㎧', '㎮', '㏆', '㏗', '㏞', '㏟', '¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '〇', '‘', '’', '‚', '‛', '“', '”', '„', '‟', '′', '″', '〝', '〞', '«', '»', '‹', '›', '‐', '‑', '‒', '–', '—', '―', '︱', '︲', '﹘', '‖', '⁄', '⁅', '⁆', '⁎', '、', '。', '〈', '〉', '《', '》', '〔', '〕', '〘', '〙', '〚', '〛', '︑', '︒', '︹', '︺', '︽', '︾', '︿', '﹀', '﹑', '﹝', '﹞', '｟', '｠', '｡', '､', '×', '÷', '−', '∕', '∖', '∣', '∥', '≪', '≫', '⦅', '⦆'];
private const TRANSLIT_TO = ['AE', 'D', 'O', 'TH', 'ss', 'ae', 'd', 'o', 'th', 'D', 'd', 'H', 'h', 'i', 'q', 'L', 'l', 'L', 'l', '\'n', 'N', 'n', 'OE', 'oe', 'T', 't', 'b', 'B', 'B', 'b', 'C', 'c', 'D', 'D', 'D', 'd', 'E', 'F', 'f', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'N', 'n', 'OI', 'oi', 'P', 'p', 't', 'T', 't', 'T', 'V', 'Y', 'y', 'Z', 'z', 'DZ', 'Dz', 'dz', 'G', 'g', 'd', 'Z', 'z', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', 'z', 'B', 'U', 'E', 'e', 'J', 'j', 'R', 'r', 'Y', 'y', 'b', 'c', 'd', 'd', 'e', 'j', 'g', 'g', 'G', 'h', 'h', 'i', 'I', 'l', 'l', 'l', 'm', 'n', 'n', 'N', 'OE', 'r', 'r', 'r', 'R', 's', 't', 'u', 'v', 'Y', 'z', 'z', 'B', 'G', 'H', 'j', 'L', 'q', 'dz', 'dz', 'ts', 'ls', 'lz', 'A', 'AE', 'B', 'C', 'D', 'D', 'E', 'J', 'K', 'L', 'M', 'O', 'P', 'T', 'U', 'V', 'W', 'Z', 'ue', 'b', 'd', 'f', 'm', 'n', 'p', 'r', 'r', 's', 't', 'z', 'th', 'I', 'p', 'U', 'b', 'd', 'f', 'g', 'k', 'l', 'm', 'n', 'p', 'r', 's', 'v', 'x', 'z', 'a', 'd', 'e', 'e', 'i', 'u', 'a', 's', 's', 'SS', 'LL', 'll', 'V', 'v', 'Y', 'y', '(C)', '(R)', 'CE', 'Cr', 'Fr.', 'L.', 'Pts', 'TL', 'Rs', 'x', 'Rx', 'm/s', 'rad/s', 'C/kg', 'pH', 'V/m', 'A/m', ' 1/4', ' 1/2', ' 3/4', ' 1/3', ' 2/3', ' 1/5', ' 2/5', ' 3/5', ' 4/5', ' 1/6', ' 5/6', ' 1/8', ' 3/8', ' 5/8', ' 7/8', ' 1/', '0', '\'', '\'', ',', '\'', '"', '"', ',,', '"', '\'', '"', '"', '"', '<<', '>>', '<', '>', '-', '-', '-', '-', '-', '-', '-', '-', '-', '||', '/', '[', ']', '*', ',', '.', '<', '>', '<<', '>>', '[', ']', '[', ']', '[', ']', ',', '.', '[', ']', '<<', '>>', '<', '>', ',', '[', ']', '((', '))', '.', ',', '*', '/', '-', '/', '\\', '|', '||', '<<', '>>', '((', '))'];

private static $transliterators = [];
private static $tableZero;
private static $tableWide;

public static function fromCodePoints(int ...$codes): static
{
$string = '';

foreach ($codes as $code) {
if (0x80 > $code %= 0x200000) {
$string .= \chr($code);
} elseif (0x800 > $code) {
$string .= \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
} elseif (0x10000 > $code) {
$string .= \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
} else {
$string .= \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
}
}

return new static($string);
}








public function ascii(array $rules = []): self
{
$str = clone $this;
$s = $str->string;
$str->string = '';

array_unshift($rules, 'nfd');
$rules[] = 'latin-ascii';

if (\function_exists('transliterator_transliterate')) {
$rules[] = 'any-latin/bgn';
}

$rules[] = 'nfkd';
$rules[] = '[:nonspacing mark:] remove';

while (\strlen($s) - 1 > $i = strspn($s, self::ASCII)) {
if (0 < --$i) {
$str->string .= substr($s, 0, $i);
$s = substr($s, $i);
}

if (!$rule = array_shift($rules)) {
$rules = []; 
}

if ($rule instanceof \Transliterator) {
$s = $rule->transliterate($s);
} elseif ($rule instanceof \Closure) {
$s = $rule($s);
} elseif ($rule) {
if ('nfd' === $rule = strtolower($rule)) {
normalizer_is_normalized($s, self::NFD) ?: $s = normalizer_normalize($s, self::NFD);
} elseif ('nfkd' === $rule) {
normalizer_is_normalized($s, self::NFKD) ?: $s = normalizer_normalize($s, self::NFKD);
} elseif ('[:nonspacing mark:] remove' === $rule) {
$s = preg_replace('/\p{Mn}++/u', '', $s);
} elseif ('latin-ascii' === $rule) {
$s = str_replace(self::TRANSLIT_FROM, self::TRANSLIT_TO, $s);
} elseif ('de-ascii' === $rule) {
$s = preg_replace("/([AUO])\u{0308}(?=\p{Ll})/u", '$1e', $s);
$s = str_replace(["a\u{0308}", "o\u{0308}", "u\u{0308}", "A\u{0308}", "O\u{0308}", "U\u{0308}"], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], $s);
} elseif (\function_exists('transliterator_transliterate')) {
if (null === $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule)) {
if ('any-latin/bgn' === $rule) {
$rule = 'any-latin';
$transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule);
}

if (null === $transliterator) {
throw new InvalidArgumentException(sprintf('Unknown transliteration rule "%s".', $rule));
}

self::$transliterators['any-latin/bgn'] = $transliterator;
}

$s = $transliterator->transliterate($s);
}
} elseif (!\function_exists('iconv')) {
$s = preg_replace('/[^\x00-\x7F]/u', '?', $s);
} else {
$s = @preg_replace_callback('/[^\x00-\x7F]/u', static function ($c) {
$c = (string) iconv('UTF-8', 'ASCII//TRANSLIT', $c[0]);

if ('' === $c && '' === iconv('UTF-8', 'ASCII//TRANSLIT', '²')) {
throw new \LogicException(sprintf('"%s" requires a translit-able iconv implementation, try installing "gnu-libiconv" if you\'re using Alpine Linux.', static::class));
}

return 1 < \strlen($c) ? ltrim($c, '\'`"^~') : ('' !== $c ? $c : '?');
}, $s);
}
}

$str->string .= $s;

return $str;
}

public function camel(): static
{
$str = clone $this;
$str->string = str_replace(' ', '', preg_replace_callback('/\b.(?![A-Z]{2,})/u', static function ($m) use (&$i) {
return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : mb_strtolower($m[0], 'UTF-8')) : mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8');
}, preg_replace('/[^\pL0-9]++/u', ' ', $this->string)));

return $str;
}




public function codePointsAt(int $offset): array
{
$str = $this->slice($offset, 1);

if ('' === $str->string) {
return [];
}

$codePoints = [];

foreach (preg_split('//u', $str->string, -1, \PREG_SPLIT_NO_EMPTY) as $c) {
$codePoints[] = mb_ord($c, 'UTF-8');
}

return $codePoints;
}

public function folded(bool $compat = true): static
{
$str = clone $this;

if (!$compat || !\defined('Normalizer::NFKC_CF')) {
$str->string = normalizer_normalize($str->string, $compat ? \Normalizer::NFKC : \Normalizer::NFC);
$str->string = mb_strtolower(str_replace(self::FOLD_FROM, self::FOLD_TO, $this->string), 'UTF-8');
} else {
$str->string = normalizer_normalize($str->string, \Normalizer::NFKC_CF);
}

return $str;
}

public function join(array $strings, string $lastGlue = null): static
{
$str = clone $this;

$tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : '';
$str->string = implode($this->string, $strings).$tail;

if (!preg_match('//u', $str->string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

return $str;
}

public function lower(): static
{
$str = clone $this;
$str->string = mb_strtolower(str_replace('İ', 'i̇', $str->string), 'UTF-8');

return $str;
}

public function match(string $regexp, int $flags = 0, int $offset = 0): array
{
$match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match';

if ($this->ignoreCase) {
$regexp .= 'i';
}

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) {
$lastError = preg_last_error();

foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}

throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}

return $matches;
}

public function normalize(int $form = self::NFC): static
{
if (!\in_array($form, [self::NFC, self::NFD, self::NFKC, self::NFKD])) {
throw new InvalidArgumentException('Unsupported normalization form.');
}

$str = clone $this;
normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form);

return $str;
}

public function padBoth(int $length, string $padStr = ' '): static
{
if ('' === $padStr || !preg_match('//u', $padStr)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

$pad = clone $this;
$pad->string = $padStr;

return $this->pad($length, $pad, \STR_PAD_BOTH);
}

public function padEnd(int $length, string $padStr = ' '): static
{
if ('' === $padStr || !preg_match('//u', $padStr)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

$pad = clone $this;
$pad->string = $padStr;

return $this->pad($length, $pad, \STR_PAD_RIGHT);
}

public function padStart(int $length, string $padStr = ' '): static
{
if ('' === $padStr || !preg_match('//u', $padStr)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}

$pad = clone $this;
$pad->string = $padStr;

return $this->pad($length, $pad, \STR_PAD_LEFT);
}

public function replaceMatches(string $fromRegexp, string|callable $to): static
{
if ($this->ignoreCase) {
$fromRegexp .= 'i';
}

if (\is_array($to) || $to instanceof \Closure) {
$replace = 'preg_replace_callback';
$to = static function (array $m) use ($to): string {
$to = $to($m);

if ('' !== $to && (!\is_string($to) || !preg_match('//u', $to))) {
throw new InvalidArgumentException('Replace callback must return a valid UTF-8 string.');
}

return $to;
};
} elseif ('' !== $to && !preg_match('//u', $to)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
} else {
$replace = 'preg_replace';
}

set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });

try {
if (null === $string = $replace($fromRegexp.'u', $to, $this->string)) {
$lastError = preg_last_error();

foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}

throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}

$str = clone $this;
$str->string = $string;

return $str;
}

public function reverse(): static
{
$str = clone $this;
$str->string = implode('', array_reverse(preg_split('/(\X)/u', $str->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY)));

return $str;
}

public function snake(): static
{
$str = $this->camel();
$str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8');

return $str;
}

public function title(bool $allWords = false): static
{
$str = clone $this;

$limit = $allWords ? -1 : 1;

$str->string = preg_replace_callback('/\b./u', static function (array $m): string {
return mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8');
}, $str->string, $limit);

return $str;
}

public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static
{
if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) {
throw new InvalidArgumentException('Invalid UTF-8 chars.');
}
$chars = preg_quote($chars);

$str = clone $this;
$str->string = preg_replace("{^[$chars]++|[$chars]++$}uD", '', $str->string);

return $str;
}

public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static
{
if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) {
throw new InvalidArgumentException('Invalid UTF-8 chars.');
}
$chars = preg_quote($chars);

$str = clone $this;
$str->string = preg_replace("{[$chars]++$}uD", '', $str->string);

return $str;
}

public function trimPrefix($prefix): static
{
if (!$this->ignoreCase) {
return parent::trimPrefix($prefix);
}

$str = clone $this;

if ($prefix instanceof \Traversable) {
$prefix = iterator_to_array($prefix, false);
} elseif ($prefix instanceof parent) {
$prefix = $prefix->string;
}

$prefix = implode('|', array_map('preg_quote', (array) $prefix));
$str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string);

return $str;
}

public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static
{
if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) {
throw new InvalidArgumentException('Invalid UTF-8 chars.');
}
$chars = preg_quote($chars);

$str = clone $this;
$str->string = preg_replace("{^[$chars]++}uD", '', $str->string);

return $str;
}

public function trimSuffix($suffix): static
{
if (!$this->ignoreCase) {
return parent::trimSuffix($suffix);
}

$str = clone $this;

if ($suffix instanceof \Traversable) {
$suffix = iterator_to_array($suffix, false);
} elseif ($suffix instanceof parent) {
$suffix = $suffix->string;
}

$suffix = implode('|', array_map('preg_quote', (array) $suffix));
$str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string);

return $str;
}

public function upper(): static
{
$str = clone $this;
$str->string = mb_strtoupper($str->string, 'UTF-8');

return $str;
}

public function width(bool $ignoreAnsiDecoration = true): int
{
$width = 0;
$s = str_replace(["\x00", "\x05", "\x07"], '', $this->string);

if (false !== strpos($s, "\r")) {
$s = str_replace(["\r\n", "\r"], "\n", $s);
}

if (!$ignoreAnsiDecoration) {
$s = preg_replace('/[\p{Cc}\x7F]++/u', '', $s);
}

foreach (explode("\n", $s) as $s) {
if ($ignoreAnsiDecoration) {
$s = preg_replace('/(?:\x1B(?:
                    \[ [\x30-\x3F]*+ [\x20-\x2F]*+ [\x40-\x7E]
                    | [P\]X^_] .*? \x1B\\\\
                    | [\x41-\x7E]
                )|[\p{Cc}\x7F]++)/xu', '', $s);
}

$lineWidth = $this->wcswidth($s);

if ($lineWidth > $width) {
$width = $lineWidth;
}
}

return $width;
}

private function pad(int $len, self $pad, int $type): static
{
$sLen = $this->length();

if ($len <= $sLen) {
return clone $this;
}

$padLen = $pad->length();
$freeLen = $len - $sLen;
$len = $freeLen % $padLen;

switch ($type) {
case \STR_PAD_RIGHT:
return $this->append(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : ''));

case \STR_PAD_LEFT:
return $this->prepend(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : ''));

case \STR_PAD_BOTH:
$freeLen /= 2;

$rightLen = ceil($freeLen);
$len = $rightLen % $padLen;
$str = $this->append(str_repeat($pad->string, intdiv($rightLen, $padLen)).($len ? $pad->slice(0, $len) : ''));

$leftLen = floor($freeLen);
$len = $leftLen % $padLen;

return $str->prepend(str_repeat($pad->string, intdiv($leftLen, $padLen)).($len ? $pad->slice(0, $len) : ''));

default:
throw new InvalidArgumentException('Invalid padding type.');
}
}




private function wcswidth(string $string): int
{
$width = 0;

foreach (preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY) as $c) {
$codePoint = mb_ord($c, 'UTF-8');

if (0 === $codePoint 
|| 0x034F === $codePoint 
|| (0x200B <= $codePoint && 0x200F >= $codePoint) 
|| 0x2028 === $codePoint 
|| 0x2029 === $codePoint 
|| (0x202A <= $codePoint && 0x202E >= $codePoint) 
|| (0x2060 <= $codePoint && 0x2063 >= $codePoint) 
) {
continue;
}


if (32 > $codePoint 
|| (0x07F <= $codePoint && 0x0A0 > $codePoint) 
) {
return -1;
}

if (null === self::$tableZero) {
self::$tableZero = require __DIR__.'/Resources/data/wcswidth_table_zero.php';
}

if ($codePoint >= self::$tableZero[0][0] && $codePoint <= self::$tableZero[$ubound = \count(self::$tableZero) - 1][1]) {
$lbound = 0;
while ($ubound >= $lbound) {
$mid = floor(($lbound + $ubound) / 2);

if ($codePoint > self::$tableZero[$mid][1]) {
$lbound = $mid + 1;
} elseif ($codePoint < self::$tableZero[$mid][0]) {
$ubound = $mid - 1;
} else {
continue 2;
}
}
}

if (null === self::$tableWide) {
self::$tableWide = require __DIR__.'/Resources/data/wcswidth_table_wide.php';
}

if ($codePoint >= self::$tableWide[0][0] && $codePoint <= self::$tableWide[$ubound = \count(self::$tableWide) - 1][1]) {
$lbound = 0;
while ($ubound >= $lbound) {
$mid = floor(($lbound + $ubound) / 2);

if ($codePoint > self::$tableWide[$mid][1]) {
$lbound = $mid + 1;
} elseif ($codePoint < self::$tableWide[$mid][0]) {
$ubound = $mid - 1;
} else {
$width += 2;

continue 2;
}
}
}

++$width;
}

return $width;
}
}
<?php










namespace Symfony\Component\String\Exception;

interface ExceptionInterface extends \Throwable
{
}
<?php










namespace Symfony\Component\String\Exception;

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










namespace Symfony\Component\String\Exception;

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










if (\PHP_VERSION_ID >= 70400 && extension_loaded('curl')) {



class CURLStringFile extends CURLFile
{
private $data;

public function __construct(string $data, string $postname, string $mime = 'application/octet-stream')
{
$this->data = $data;
parent::__construct('data://application/octet-stream;base64,'.base64_encode($data), $mime, $postname);
}

public function __set(string $name, $value): void
{
if ('data' !== $name) {
$this->$name = $value;

return;
}

if (is_object($value) ? !method_exists($value, '__toString') : !is_scalar($value)) {
throw new \TypeError('Cannot assign '.gettype($value).' to property CURLStringFile::$data of type string');
}

$this->name = 'data://application/octet-stream;base64,'.base64_encode($value);
}

public function __isset(string $name): bool
{
return isset($this->$name);
}

public function &__get(string $name)
{
return $this->$name;
}
}
}
<?php










if (\PHP_VERSION_ID < 80100) {
#[Attribute(Attribute::TARGET_METHOD)]
final class ReturnTypeWillChange
{
public function __construct()
{
}
}
}
<?php










use Symfony\Polyfill\Php81 as p;

if (\PHP_VERSION_ID >= 80100) {
return;
}

if (defined('MYSQLI_REFRESH_SLAVE') && !defined('MYSQLI_REFRESH_REPLICA')) {
define('MYSQLI_REFRESH_REPLICA', 64);
}

if (!function_exists('array_is_list')) {
function array_is_list(array $array): bool { return p\Php81::array_is_list($array); }
}

if (!function_exists('enum_exists')) {
function enum_exists(string $enum, bool $autoload = true): bool { return $autoload && class_exists($enum) && false; }
}
<?php










namespace Symfony\Polyfill\Php81;






final class Php81
{
public static function array_is_list(array $array): bool
{
if ([] === $array || $array === array_values($array)) {
return true;
}

$nextKey = -1;

foreach ($array as $k => $v) {
if ($k !== ++$nextKey) {
return false;
}
}

return true;
}
}
document.addEventListener('DOMContentLoaded', function() {
  let prev = null;
  Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) {
    const dedupId = article.dataset.dedupId;
    if (dedupId === prev) {
      article.getElementsByTagName('header')[0].classList.add('hidden');
    }
    prev = dedupId;
  });
});
<?php










use Symfony\Component\VarDumper\VarDumper;

if (!function_exists('dump')) {



function dump(mixed $var, mixed ...$moreVars): mixed
{
VarDumper::dump($var);

foreach ($moreVars as $v) {
VarDumper::dump($v);
}

if (1 < func_num_args()) {
return func_get_args();
}

return $var;
}
}

if (!function_exists('dd')) {



function dd(...$vars): void
{
if (!in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}

foreach ($vars as $v) {
VarDumper::dump($v);
}

exit(1);
}
}
body {
    display: flex;
    flex-direction: column-reverse;
    justify-content: flex-end;
    max-width: 1140px;
    margin: auto;
    padding: 15px;
    word-wrap: break-word;
    background-color: #F9F9F9;
    color: #222;
    font-family: Helvetica, Arial, sans-serif;
    font-size: 14px;
    line-height: 1.4;
}
p {
    margin: 0;
}
a {
    color: #218BC3;
    text-decoration: none;
}
a:hover {
    text-decoration: underline;
}
.text-small {
    font-size: 12px !important;
}
article {
    margin: 5px;
    margin-bottom: 10px;
}
article > header > .row {
    display: flex;
    flex-direction: row;
    align-items: baseline;
    margin-bottom: 10px;
}
article > header > .row > .col {
    flex: 1;
    display: flex;
    align-items: baseline;
}
article > header > .row > h2 {
    font-size: 14px;
    color: #222;
    font-weight: normal;
    font-family: "Lucida Console", monospace, sans-serif;
    word-break: break-all;
    margin: 20px 5px 0 0;
    user-select: all;
}
article > header > .row > h2 > code {
    white-space: nowrap;
    user-select: none;
    color: #cc2255;
    background-color: #f7f7f9;
    border: 1px solid #e1e1e8;
    border-radius: 3px;
    margin-right: 5px;
    padding: 0 3px;
}
article > header > .row > time.col {
    flex: 0;
    text-align: right;
    white-space: nowrap;
    color: #999;
    font-style: italic;
}
article > header ul.tags {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 12px;
}
article > header ul.tags > li {
    user-select: all;
    margin-bottom: 2px;
}
article > header ul.tags > li > span.badge {
    display: inline-block;
    padding: .25em .4em;
    margin-right: 5px;
    border-radius: 4px;
    background-color: #6c757d3b;
    color: #524d4d;
    font-size: 12px;
    text-align: center;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
    vertical-align: baseline;
    user-select: none;
}
article > section.body {
    border: 1px solid #d8d8d8;
    background: #FFF;
    padding: 10px;
    border-radius: 3px;
}
pre.sf-dump {
    border-radius: 3px;
    margin-bottom: 0;
}
.hidden {
    display: none !important;
}
.dumped-tag > .sf-dump {
    display: inline-block;
    margin: 0;
    padding: 1px 5px;
    line-height: 1.4;
    vertical-align: top;
    background-color: transparent;
    user-select: auto;
}
.dumped-tag > pre.sf-dump,
.dumped-tag > .sf-dump-default {
    color: #CC7832;
    background: none;
}
.dumped-tag > .sf-dump .sf-dump-str { color: #629755; }
.dumped-tag > .sf-dump .sf-dump-private,
.dumped-tag > .sf-dump .sf-dump-protected,
.dumped-tag > .sf-dump .sf-dump-public { color: #262626; }
.dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; }
.dumped-tag > .sf-dump .sf-dump-key { color: #789339; }
.dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; }
.dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; }
.dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; }
.dumped-tag > .sf-dump .sf-dump-ns { user-select: none; }
<?php










namespace Symfony\Component\VarDumper\Server;

use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;






class Connection
{
private string $host;
private array $contextProviders;




private $socket;





public function __construct(string $host, array $contextProviders = [])
{
if (!str_contains($host, '://')) {
$host = 'tcp://'.$host;
}

$this->host = $host;
$this->contextProviders = $contextProviders;
}

public function getContextProviders(): array
{
return $this->contextProviders;
}

public function write(Data $data): bool
{
$socketIsFresh = !$this->socket;
if (!$this->socket = $this->socket ?: $this->createSocket()) {
return false;
}

$context = ['timestamp' => microtime(true)];
foreach ($this->contextProviders as $name => $provider) {
$context[$name] = $provider->getContext();
}
$context = array_filter($context);
$encodedPayload = base64_encode(serialize([$data, $context]))."\n";

set_error_handler([self::class, 'nullErrorHandler']);
try {
if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
return true;
}
if (!$socketIsFresh) {
stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR);
fclose($this->socket);
$this->socket = $this->createSocket();
}
if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
return true;
}
} finally {
restore_error_handler();
}

return false;
}

private static function nullErrorHandler(int $t, string $m)
{

}

private function createSocket()
{
set_error_handler([self::class, 'nullErrorHandler']);
try {
return stream_socket_client($this->host, $errno, $errstr, 3, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT);
} finally {
restore_error_handler();
}
}
}
<?php










namespace Symfony\Component\VarDumper\Server;

use Psr\Log\LoggerInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;








class DumpServer
{
private string $host;
private $logger;




private $socket;

public function __construct(string $host, LoggerInterface $logger = null)
{
if (!str_contains($host, '://')) {
$host = 'tcp://'.$host;
}

$this->host = $host;
$this->logger = $logger;
}

public function start(): void
{
if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) {
throw new \RuntimeException(sprintf('Server start failed on "%s": ', $this->host).$errstr.' '.$errno);
}
}

public function listen(callable $callback): void
{
if (null === $this->socket) {
$this->start();
}

foreach ($this->getMessages() as $clientId => $message) {
if ($this->logger) {
$this->logger->info('Received a payload from client {clientId}', ['clientId' => $clientId]);
}

$payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]);


if (false === $payload) {
if ($this->logger) {
$this->logger->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]);
}

continue;
}

if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) {
if ($this->logger) {
$this->logger->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]);
}

continue;
}

[$data, $context] = $payload;

$callback($data, $context, $clientId);
}
}

public function getHost(): string
{
return $this->host;
}

private function getMessages(): iterable
{
$sockets = [(int) $this->socket => $this->socket];
$write = [];

while (true) {
$read = $sockets;
stream_select($read, $write, $write, null);

foreach ($read as $stream) {
if ($this->socket === $stream) {
$stream = stream_socket_accept($this->socket);
$sockets[(int) $stream] = $stream;
} elseif (feof($stream)) {
unset($sockets[(int) $stream]);
fclose($stream);
} else {
yield (int) $stream => fgets($stream);
}
}
}
}
}
<?php










namespace Symfony\Component\VarDumper\Test;

use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;




trait VarDumperTestTrait
{



private array $varDumperConfig = [
'casters' => [],
'flags' => null,
];

protected function setUpVarDumper(array $casters, int $flags = null): void
{
$this->varDumperConfig['casters'] = $casters;
$this->varDumperConfig['flags'] = $flags;
}




protected function tearDownVarDumper(): void
{
$this->varDumperConfig['casters'] = [];
$this->varDumperConfig['flags'] = null;
}

public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = '')
{
$this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message);
}

public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = '')
{
$this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message);
}

protected function getDump(mixed $data, string|int $key = null, int $filter = 0): ?string
{
if (null === $flags = $this->varDumperConfig['flags']) {
$flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0;
$flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0;
$flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0;
}

$cloner = new VarCloner();
$cloner->addCasters($this->varDumperConfig['casters']);
$cloner->setMaxItems(-1);
$dumper = new CliDumper(null, null, $flags);
$dumper->setColors(false);
$data = $cloner->cloneVar($data, $filter)->withRefHandles(false);
if (null !== $key && null === $data = $data->seek($key)) {
return null;
}

return rtrim($dumper->dump($data, true));
}

private function prepareExpectation(mixed $expected, int $filter): string
{
if (!\is_string($expected)) {
$expected = $this->getDump($expected, null, $filter);
}

return rtrim($expected);
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class Caster
{
public const EXCLUDE_VERBOSE = 1;
public const EXCLUDE_VIRTUAL = 2;
public const EXCLUDE_DYNAMIC = 4;
public const EXCLUDE_PUBLIC = 8;
public const EXCLUDE_PROTECTED = 16;
public const EXCLUDE_PRIVATE = 32;
public const EXCLUDE_NULL = 64;
public const EXCLUDE_EMPTY = 128;
public const EXCLUDE_NOT_IMPORTANT = 256;
public const EXCLUDE_STRICT = 512;

public const PREFIX_VIRTUAL = "\0~\0";
public const PREFIX_DYNAMIC = "\0+\0";
public const PREFIX_PROTECTED = "\0*\0";






public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, string $debugClass = null): array
{
if ($hasDebugInfo) {
try {
$debugInfo = $obj->__debugInfo();
} catch (\Throwable $e) {

$hasDebugInfo = false;
}
}

$a = $obj instanceof \Closure ? [] : (array) $obj;

if ($obj instanceof \__PHP_Incomplete_Class) {
return $a;
}

if ($a) {
static $publicProperties = [];
$debugClass = $debugClass ?? get_debug_type($obj);

$i = 0;
$prefixedKeys = [];
foreach ($a as $k => $v) {
if ("\0" !== ($k[0] ?? '')) {
if (!isset($publicProperties[$class])) {
foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) {
$publicProperties[$class][$prop->name] = true;
}
}
if (!isset($publicProperties[$class][$k])) {
$prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k;
}
} elseif ($debugClass !== $class && 1 === strpos($k, $class)) {
$prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0");
}
++$i;
}
if ($prefixedKeys) {
$keys = array_keys($a);
foreach ($prefixedKeys as $i => $k) {
$keys[$i] = $k;
}
$a = array_combine($keys, $a);
}
}

if ($hasDebugInfo && \is_array($debugInfo)) {
foreach ($debugInfo as $k => $v) {
if (!isset($k[0]) || "\0" !== $k[0]) {
if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) {
continue;
}
$k = self::PREFIX_VIRTUAL.$k;
}

unset($a[$k]);
$a[$k] = $v;
}
}

return $a;
}












public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array
{
$count = 0;

foreach ($a as $k => $v) {
$type = self::EXCLUDE_STRICT & $filter;

if (null === $v) {
$type |= self::EXCLUDE_NULL & $filter;
$type |= self::EXCLUDE_EMPTY & $filter;
} elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) {
$type |= self::EXCLUDE_EMPTY & $filter;
}
if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) {
$type |= self::EXCLUDE_NOT_IMPORTANT;
}
if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) {
$type |= self::EXCLUDE_VERBOSE;
}

if (!isset($k[1]) || "\0" !== $k[0]) {
$type |= self::EXCLUDE_PUBLIC & $filter;
} elseif ('~' === $k[1]) {
$type |= self::EXCLUDE_VIRTUAL & $filter;
} elseif ('+' === $k[1]) {
$type |= self::EXCLUDE_DYNAMIC & $filter;
} elseif ('*' === $k[1]) {
$type |= self::EXCLUDE_PROTECTED & $filter;
} else {
$type |= self::EXCLUDE_PRIVATE & $filter;
}

if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) {
unset($a[$k]);
++$count;
}
}

return $a;
}

public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array
{
if (isset($a['__PHP_Incomplete_Class_Name'])) {
$stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')';
unset($a['__PHP_Incomplete_Class_Name']);
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class EnumStub extends Stub
{
public $dumpKeys = true;

public function __construct(array $values, bool $dumpKeys = true)
{
$this->value = $values;
$this->dumpKeys = $dumpKeys;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class MemcachedCaster
{
private static array $optionConstants;
private static array $defaultOptions;

public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $isNested)
{
$a += [
Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(),
Caster::PREFIX_VIRTUAL.'options' => new EnumStub(
self::getNonDefaultOptions($c)
),
];

return $a;
}

private static function getNonDefaultOptions(\Memcached $c): array
{
self::$defaultOptions = self::$defaultOptions ?? self::discoverDefaultOptions();
self::$optionConstants = self::$optionConstants ?? self::getOptionConstants();

$nonDefaultOptions = [];
foreach (self::$optionConstants as $constantKey => $value) {
if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) {
$nonDefaultOptions[$constantKey] = $option;
}
}

return $nonDefaultOptions;
}

private static function discoverDefaultOptions(): array
{
$defaultMemcached = new \Memcached();
$defaultMemcached->addServer('127.0.0.1', 11211);

$defaultOptions = [];
self::$optionConstants = self::$optionConstants ?? self::getOptionConstants();

foreach (self::$optionConstants as $constantKey => $value) {
$defaultOptions[$constantKey] = $defaultMemcached->getOption($value);
}

return $defaultOptions;
}

private static function getOptionConstants(): array
{
$reflectedMemcached = new \ReflectionClass(\Memcached::class);

$optionConstants = [];
foreach ($reflectedMemcached->getConstants() as $constantKey => $value) {
if (str_starts_with($constantKey, 'OPT_')) {
$optionConstants[$constantKey] = $value;
}
}

return $optionConstants;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






final class MysqliCaster
{
public static function castMysqliDriver(\mysqli_driver $c, array $a, Stub $stub, bool $isNested): array
{
foreach ($a as $k => $v) {
if (isset($c->$k)) {
$a[$k] = $c->$k;
}
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
use Symfony\Component\VarDumper\Cloner\Stub;
use Symfony\Component\VarDumper\Exception\ThrowingCasterException;








class ExceptionCaster
{
public static int $srcContext = 1;
public static bool $traceArgs = true;
public static array $errorTypes = [
\E_DEPRECATED => 'E_DEPRECATED',
\E_USER_DEPRECATED => 'E_USER_DEPRECATED',
\E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
\E_ERROR => 'E_ERROR',
\E_WARNING => 'E_WARNING',
\E_PARSE => 'E_PARSE',
\E_NOTICE => 'E_NOTICE',
\E_CORE_ERROR => 'E_CORE_ERROR',
\E_CORE_WARNING => 'E_CORE_WARNING',
\E_COMPILE_ERROR => 'E_COMPILE_ERROR',
\E_COMPILE_WARNING => 'E_COMPILE_WARNING',
\E_USER_ERROR => 'E_USER_ERROR',
\E_USER_WARNING => 'E_USER_WARNING',
\E_USER_NOTICE => 'E_USER_NOTICE',
\E_STRICT => 'E_STRICT',
];

private static array $framesCache = [];

public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter);
}

public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter);
}

public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $isNested)
{
if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) {
$a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
}

return $a;
}

public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, bool $isNested)
{
$trace = Caster::PREFIX_VIRTUAL.'trace';
$prefix = Caster::PREFIX_PROTECTED;
$xPrefix = "\0Exception\0";

if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) {
$b = (array) $a[$xPrefix.'previous'];
$class = get_debug_type($a[$xPrefix.'previous']);
self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']);
$a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value));
}

unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']);

return $a;
}

public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, bool $isNested)
{
$sPrefix = "\0".SilencedErrorContext::class."\0";

if (!isset($a[$s = $sPrefix.'severity'])) {
return $a;
}

if (isset(self::$errorTypes[$a[$s]])) {
$a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
}

$trace = [[
'file' => $a[$sPrefix.'file'],
'line' => $a[$sPrefix.'line'],
]];

if (isset($a[$sPrefix.'trace'])) {
$trace = array_merge($trace, $a[$sPrefix.'trace']);
}

unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']);
$a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs);

return $a;
}

public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, bool $isNested)
{
if (!$isNested) {
return $a;
}
$stub->class = '';
$stub->handle = 0;
$frames = $trace->value;
$prefix = Caster::PREFIX_VIRTUAL;

$a = [];
$j = \count($frames);
if (0 > $i = $trace->sliceOffset) {
$i = max(0, $j + $i);
}
if (!isset($trace->value[$i])) {
return [];
}
$lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
$frames[] = ['function' => ''];
$collapse = false;

for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) {
$f = $frames[$i];
$call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???';

$frame = new FrameStub(
[
'object' => $f['object'] ?? null,
'class' => $f['class'] ?? null,
'type' => $f['type'] ?? null,
'function' => $f['function'] ?? null,
] + $frames[$i - 1],
false,
true
);
$f = self::castFrameStub($frame, [], $frame, true);
if (isset($f[$prefix.'src'])) {
foreach ($f[$prefix.'src']->value as $label => $frame) {
if (str_starts_with($label, "\0~collapse=0")) {
if ($collapse) {
$label = substr_replace($label, '1', 11, 1);
} else {
$collapse = true;
}
}
$label = substr_replace($label, "title=Stack level $j.&", 2, 0);
}
$f = $frames[$i - 1];
if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) {
$frame->value['arguments'] = new ArgsStub($f['args'], $f['function'] ?? null, $f['class'] ?? null);
}
} elseif ('???' !== $lastCall) {
$label = new ClassStub($lastCall);
if (isset($label->attr['ellipsis'])) {
$label->attr['ellipsis'] += 2;
$label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()';
} else {
$label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()';
}
} else {
$label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall;
}
$a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame;

$lastCall = $call;
}
if (null !== $trace->sliceLength) {
$a = \array_slice($a, 0, $trace->sliceLength, true);
}

return $a;
}

public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, bool $isNested)
{
if (!$isNested) {
return $a;
}
$f = $frame->value;
$prefix = Caster::PREFIX_VIRTUAL;

if (isset($f['file'], $f['line'])) {
$cacheKey = $f;
unset($cacheKey['object'], $cacheKey['args']);
$cacheKey[] = self::$srcContext;
$cacheKey = implode('-', $cacheKey);

if (isset(self::$framesCache[$cacheKey])) {
$a[$prefix.'src'] = self::$framesCache[$cacheKey];
} else {
if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) {
$f['file'] = substr($f['file'], 0, -\strlen($match[0]));
$f['line'] = (int) $match[1];
}
$src = $f['line'];
$srcKey = $f['file'];
$ellipsis = new LinkStub($srcKey, 0);
$srcAttr = 'collapse='.(int) $ellipsis->inVendor;
$ellipsisTail = $ellipsis->attr['ellipsis-tail'] ?? 0;
$ellipsis = $ellipsis->attr['ellipsis'] ?? 0;

if (is_file($f['file']) && 0 <= self::$srcContext) {
if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) {
$template = null;
if (isset($f['object'])) {
$template = $f['object'];
} elseif ((new \ReflectionClass($f['class']))->isInstantiable()) {
$template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class']));
}
if (null !== $template) {
$ellipsis = 0;
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
$templateInfo = $template->getDebugInfo();
if (isset($templateInfo[$f['line']])) {
if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) {
$templatePath = null;
}
if ($templateSrc) {
$src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f);
$srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
}
}
}
}
if ($srcKey == $f['file']) {
$src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f);
$srcKey .= ':'.$f['line'];
if ($ellipsis) {
$ellipsis += 1 + \strlen($f['line']);
}
}
$srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']);
} else {
$srcAttr .= '&separator=:';
}
$srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : '';
self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]);
}
}

unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']);
if ($frame->inTraceStub) {
unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']);
}
foreach ($a as $k => $v) {
if (!$v) {
unset($a[$k]);
}
}
if ($frame->keepArgs && !empty($f['args'])) {
$a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']);
}

return $a;
}

private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array
{
if (isset($a[$xPrefix.'trace'])) {
$trace = $a[$xPrefix.'trace'];
unset($a[$xPrefix.'trace']); 
} else {
$trace = [];
}

if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) {
if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
}
$a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs);
}
if (empty($a[$xPrefix.'previous'])) {
unset($a[$xPrefix.'previous']);
}
unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']);

if (isset($a[Caster::PREFIX_PROTECTED.'message']) && str_contains($a[Caster::PREFIX_PROTECTED.'message'], "@anonymous\0")) {
$a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $a[Caster::PREFIX_PROTECTED.'message']);
}

if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
$a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
}

return $a;
}

private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void
{
if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) {
return;
}
array_unshift($trace, [
'function' => $class ? 'new '.$class : null,
'file' => $file,
'line' => $line,
]);
}

private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub
{
$srcLines = explode("\n", $srcLines);
$src = [];

for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) {
$src[] = ($srcLines[$i] ?? '')."\n";
}

if ($frame['function'] ?? false) {
$stub = new CutStub(new \stdClass());
$stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function'];
$stub->type = Stub::TYPE_OBJECT;
$stub->attr['cut_hash'] = true;
$stub->attr['file'] = $frame['file'];
$stub->attr['line'] = $frame['line'];

try {
$caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']);
$stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE));

if ($f = $caller->getFileName()) {
$stub->attr['file'] = $f;
$stub->attr['line'] = $caller->getStartLine();
}
} catch (\ReflectionException $e) {

}

$srcLines = ["\0~separator=\0" => $stub];
} else {
$stub = null;
$srcLines = [];
}

$ltrim = 0;
do {
$pad = null;
for ($i = $srcContext << 1; $i >= 0; --$i) {
if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) {
if (null === $pad) {
$pad = $c;
}
if ((' ' !== $c && "\t" !== $c) || $pad !== $c) {
break;
}
}
}
++$ltrim;
} while (0 > $i && null !== $pad);

--$ltrim;

foreach ($src as $i => $c) {
if ($ltrim) {
$c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t");
}
$c = substr($c, 0, -1);
if ($i !== $srcContext) {
$c = new ConstStub('default', $c);
} else {
$c = new ConstStub($c, $stub ? 'in '.$stub->class : '');
if (null !== $file) {
$c->attr['file'] = $file;
$c->attr['line'] = $line;
}
}
$c->attr['lang'] = $lang;
$srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c;
}

return new EnumStub($srcLines);
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;









class GmpCaster
{
public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $filter): array
{
$a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp));

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Ds\Collection;
use Ds\Map;
use Ds\Pair;
use Symfony\Component\VarDumper\Cloner\Stub;








class DsCaster
{
public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array
{
$a[Caster::PREFIX_VIRTUAL.'count'] = $c->count();
$a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity();

if (!$c instanceof Map) {
$a += $c->toArray();
}

return $a;
}

public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array
{
foreach ($c as $k => $v) {
$a[] = new DsPairStub($k, $v);
}

return $a;
}

public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array
{
foreach ($c->toArray() as $k => $v) {
$a[Caster::PREFIX_VIRTUAL.$k] = $v;
}

return $a;
}

public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array
{
if ($isNested) {
$stub->class = Pair::class;
$stub->value = null;
$stub->handle = 0;

$a = $c->value;
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Doctrine\Common\Proxy\Proxy as CommonProxy;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy as OrmProxy;
use Symfony\Component\VarDumper\Cloner\Stub;








class DoctrineCaster
{
public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $isNested)
{
foreach (['__cloner__', '__initializer__'] as $k) {
if (\array_key_exists($k, $a)) {
unset($a[$k]);
++$stub->cut;
}
}

return $a;
}

public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, bool $isNested)
{
foreach (['_entityPersister', '_identifier'] as $k) {
if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) {
unset($a[$k]);
++$stub->cut;
}
}

return $a;
}

public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, bool $isNested)
{
foreach (['snapshot', 'association', 'typeClass'] as $k) {
if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) {
$a[$k] = new CutStub($a[$k]);
}
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class SplCaster
{
private const SPL_FILE_OBJECT_FLAGS = [
\SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE',
\SplFileObject::READ_AHEAD => 'READ_AHEAD',
\SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY',
\SplFileObject::READ_CSV => 'READ_CSV',
];

public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, bool $isNested)
{
return self::castSplArray($c, $a, $stub, $isNested);
}

public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested)
{
return self::castSplArray($c, $a, $stub, $isNested);
}

public static function castHeap(\Iterator $c, array $a, Stub $stub, bool $isNested)
{
$a += [
Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c),
];

return $a;
}

public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;
$mode = $c->getIteratorMode();
$c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE);

$a += [
$prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode),
$prefix.'dllist' => iterator_to_array($c),
];
$c->setIteratorMode($mode);

return $a;
}

public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, bool $isNested)
{
static $map = [
'path' => 'getPath',
'filename' => 'getFilename',
'basename' => 'getBasename',
'pathname' => 'getPathname',
'extension' => 'getExtension',
'realPath' => 'getRealPath',
'aTime' => 'getATime',
'mTime' => 'getMTime',
'cTime' => 'getCTime',
'inode' => 'getInode',
'size' => 'getSize',
'perms' => 'getPerms',
'owner' => 'getOwner',
'group' => 'getGroup',
'type' => 'getType',
'writable' => 'isWritable',
'readable' => 'isReadable',
'executable' => 'isExecutable',
'file' => 'isFile',
'dir' => 'isDir',
'link' => 'isLink',
'linkTarget' => 'getLinkTarget',
];

$prefix = Caster::PREFIX_VIRTUAL;
unset($a["\0SplFileInfo\0fileName"]);
unset($a["\0SplFileInfo\0pathName"]);

try {
$c->isReadable();
} catch (\RuntimeException $e) {
if ('Object not initialized' !== $e->getMessage()) {
throw $e;
}

$a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';

return $a;
} catch (\Error $e) {
if ('Object not initialized' !== $e->getMessage()) {
throw $e;
}

$a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';

return $a;
}

foreach ($map as $key => $accessor) {
try {
$a[$prefix.$key] = $c->$accessor();
} catch (\Exception $e) {
}
}

if ($a[$prefix.'realPath'] ?? false) {
$a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']);
}

if (isset($a[$prefix.'perms'])) {
$a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']);
}

static $mapDate = ['aTime', 'mTime', 'cTime'];
foreach ($mapDate as $key) {
if (isset($a[$prefix.$key])) {
$a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]);
}
}

return $a;
}

public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, bool $isNested)
{
static $map = [
'csvControl' => 'getCsvControl',
'flags' => 'getFlags',
'maxLineLen' => 'getMaxLineLen',
'fstat' => 'fstat',
'eof' => 'eof',
'key' => 'key',
];

$prefix = Caster::PREFIX_VIRTUAL;

foreach ($map as $key => $accessor) {
try {
$a[$prefix.$key] = $c->$accessor();
} catch (\Exception $e) {
}
}

if (isset($a[$prefix.'flags'])) {
$flagsArray = [];
foreach (self::SPL_FILE_OBJECT_FLAGS as $value => $name) {
if ($a[$prefix.'flags'] & $value) {
$flagsArray[] = $name;
}
}
$a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']);
}

if (isset($a[$prefix.'fstat'])) {
$a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']);
}

return $a;
}

public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested)
{
$storage = [];
unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); 
unset($a["\0SplObjectStorage\0storage"]);

$clone = clone $c;
foreach ($clone as $obj) {
$storage[] = [
'object' => $obj,
'info' => $clone->getInfo(),
];
}

$a += [
Caster::PREFIX_VIRTUAL.'storage' => $storage,
];

return $a;
}

public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator();

return $a;
}

public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'object'] = $c->get();

return $a;
}

private static function castSplArray(\ArrayObject|\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array
{
$prefix = Caster::PREFIX_VIRTUAL;
$flags = $c->getFlags();

if (!($flags & \ArrayObject::STD_PROP_LIST)) {
$c->setFlags(\ArrayObject::STD_PROP_LIST);
$a = Caster::castObject($c, \get_class($c), method_exists($c, '__debugInfo'), $stub->class);
$c->setFlags($flags);
}
$a += [
$prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
];
if ($c instanceof \ArrayObject) {
$a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass());
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;






class CutArrayStub extends CutStub
{
public $preservedSubset;

public function __construct(array $value, array $preservedKeys)
{
parent::__construct($value);

$this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys));
$this->cut -= \count($this->preservedSubset);
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






final class FiberCaster
{
public static function castFiber(\Fiber $fiber, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$prefix = Caster::PREFIX_VIRTUAL;

if ($fiber->isTerminated()) {
$status = 'terminated';
} elseif ($fiber->isRunning()) {
$status = 'running';
} elseif ($fiber->isSuspended()) {
$status = 'suspended';
} elseif ($fiber->isStarted()) {
$status = 'started';
} else {
$status = 'not started';
}

$a[$prefix.'status'] = $status;

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class StubCaster
{
public static function castStub(Stub $c, array $a, Stub $stub, bool $isNested)
{
if ($isNested) {
$stub->type = $c->type;
$stub->class = $c->class;
$stub->value = $c->value;
$stub->handle = $c->handle;
$stub->cut = $c->cut;
$stub->attr = $c->attr;

if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) {
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_BINARY;
}

$a = [];
}

return $a;
}

public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, bool $isNested)
{
return $isNested ? $c->preservedSubset : $a;
}

public static function cutInternals($obj, array $a, Stub $stub, bool $isNested)
{
if ($isNested) {
$stub->cut += \count($a);

return [];
}

return $a;
}

public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $isNested)
{
if ($isNested) {
$stub->class = $c->dumpKeys ? '' : null;
$stub->handle = 0;
$stub->value = null;
$stub->cut = $c->cut;
$stub->attr = $c->attr;

$a = [];

if ($c->value) {
foreach (array_keys($c->value) as $k) {
$keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k;
}

$a = array_combine($keys, $c->value);
}
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class TraceStub extends Stub
{
public $keepArgs;
public $sliceOffset;
public $sliceLength;
public $numberingOffset;

public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0)
{
$this->value = $trace;
$this->keepArgs = $keepArgs;
$this->sliceOffset = $sliceOffset;
$this->sliceLength = $sliceLength;
$this->numberingOffset = $numberingOffset;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class RedisCaster
{
private const SERIALIZERS = [
\Redis::SERIALIZER_NONE => 'NONE',
\Redis::SERIALIZER_PHP => 'PHP',
2 => 'IGBINARY', 
];

private const MODES = [
\Redis::ATOMIC => 'ATOMIC',
\Redis::MULTI => 'MULTI',
\Redis::PIPELINE => 'PIPELINE',
];

private const COMPRESSION_MODES = [
0 => 'NONE', 
1 => 'LZF', 
];

private const FAILOVER_OPTIONS = [
\RedisCluster::FAILOVER_NONE => 'NONE',
\RedisCluster::FAILOVER_ERROR => 'ERROR',
\RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE',
\RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES',
];

public static function castRedis(\Redis $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

if (!$connected = $c->isConnected()) {
return $a + [
$prefix.'isConnected' => $connected,
];
}

$mode = $c->getMode();

return $a + [
$prefix.'isConnected' => $connected,
$prefix.'host' => $c->getHost(),
$prefix.'port' => $c->getPort(),
$prefix.'auth' => $c->getAuth(),
$prefix.'mode' => isset(self::MODES[$mode]) ? new ConstStub(self::MODES[$mode], $mode) : $mode,
$prefix.'dbNum' => $c->getDbNum(),
$prefix.'timeout' => $c->getTimeout(),
$prefix.'lastError' => $c->getLastError(),
$prefix.'persistentId' => $c->getPersistentID(),
$prefix.'options' => self::getRedisOptions($c),
];
}

public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

return $a + [
$prefix.'hosts' => $c->_hosts(),
$prefix.'function' => ClassStub::wrapCallable($c->_function()),
$prefix.'lastError' => $c->getLastError(),
$prefix.'options' => self::getRedisOptions($c),
];
}

public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;
$failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER);

$a += [
$prefix.'_masters' => $c->_masters(),
$prefix.'_redir' => $c->_redir(),
$prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()),
$prefix.'lastError' => $c->getLastError(),
$prefix.'options' => self::getRedisOptions($c, [
'SLAVE_FAILOVER' => isset(self::FAILOVER_OPTIONS[$failover]) ? new ConstStub(self::FAILOVER_OPTIONS[$failover], $failover) : $failover,
]),
];

return $a;
}

private static function getRedisOptions(\Redis|\RedisArray|\RedisCluster $redis, array $options = []): EnumStub
{
$serializer = $redis->getOption(\Redis::OPT_SERIALIZER);
if (\is_array($serializer)) {
foreach ($serializer as &$v) {
if (isset(self::SERIALIZERS[$v])) {
$v = new ConstStub(self::SERIALIZERS[$v], $v);
}
}
} elseif (isset(self::SERIALIZERS[$serializer])) {
$serializer = new ConstStub(self::SERIALIZERS[$serializer], $serializer);
}

$compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0;
if (\is_array($compression)) {
foreach ($compression as &$v) {
if (isset(self::COMPRESSION_MODES[$v])) {
$v = new ConstStub(self::COMPRESSION_MODES[$v], $v);
}
}
} elseif (isset(self::COMPRESSION_MODES[$compression])) {
$compression = new ConstStub(self::COMPRESSION_MODES[$compression], $compression);
}

$retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0;
if (\is_array($retry)) {
foreach ($retry as &$v) {
$v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v);
}
} else {
$retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry);
}

$options += [
'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : 0,
'READ_TIMEOUT' => $redis->getOption(\Redis::OPT_READ_TIMEOUT),
'COMPRESSION' => $compression,
'SERIALIZER' => $serializer,
'PREFIX' => $redis->getOption(\Redis::OPT_PREFIX),
'SCAN' => $retry,
];

return new EnumStub($options);
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;






class FrameStub extends EnumStub
{
public $keepArgs;
public $inTraceStub;

public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false)
{
$this->value = $frame;
$this->keepArgs = $keepArgs;
$this->inTraceStub = $inTraceStub;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class ClassStub extends ConstStub
{




public function __construct(string $identifier, callable|array|string $callable = null)
{
$this->value = $identifier;

try {
if (null !== $callable) {
if ($callable instanceof \Closure) {
$r = new \ReflectionFunction($callable);
} elseif (\is_object($callable)) {
$r = [$callable, '__invoke'];
} elseif (\is_array($callable)) {
$r = $callable;
} elseif (false !== $i = strpos($callable, '::')) {
$r = [substr($callable, 0, $i), substr($callable, 2 + $i)];
} else {
$r = new \ReflectionFunction($callable);
}
} elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) {
$r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)];
} else {
$r = new \ReflectionClass($identifier);
}

if (\is_array($r)) {
try {
$r = new \ReflectionMethod($r[0], $r[1]);
} catch (\ReflectionException $e) {
$r = new \ReflectionClass($r[0]);
}
}

if (str_contains($identifier, "@anonymous\0")) {
$this->value = $identifier = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $identifier);
}

if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) {
$s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE);
$s = ReflectionCaster::getSignature($s);

if (str_ends_with($identifier, '()')) {
$this->value = substr_replace($identifier, $s, -2);
} else {
$this->value .= $s;
}
}
} catch (\ReflectionException $e) {
return;
} finally {
if (0 < $i = strrpos($this->value, '\\')) {
$this->attr['ellipsis'] = \strlen($this->value) - $i;
$this->attr['ellipsis-type'] = 'class';
$this->attr['ellipsis-tail'] = 1;
}
}

if ($f = $r->getFileName()) {
$this->attr['file'] = $f;
$this->attr['line'] = $r->getStartLine();
}
}

public static function wrapCallable(mixed $callable)
{
if (\is_object($callable) || !\is_callable($callable)) {
return $callable;
}

if (!\is_array($callable)) {
$callable = new static($callable, $callable);
} elseif (\is_string($callable[0])) {
$callable[0] = new static($callable[0], $callable);
} else {
$callable[1] = new static($callable[1], $callable);
}

return $callable;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Ramsey\Uuid\UuidInterface;
use Symfony\Component\VarDumper\Cloner\Stub;




final class UuidCaster
{
public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array
{
$a += [
Caster::PREFIX_VIRTUAL.'uuid' => (string) $c,
];

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\VarDumper\Cloner\Stub;




class SymfonyCaster
{
private const REQUEST_GETTERS = [
'pathInfo' => 'getPathInfo',
'requestUri' => 'getRequestUri',
'baseUrl' => 'getBaseUrl',
'basePath' => 'getBasePath',
'method' => 'getMethod',
'format' => 'getRequestFormat',
];

public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested)
{
$clone = null;

foreach (self::REQUEST_GETTERS as $prop => $getter) {
$key = Caster::PREFIX_PROTECTED.$prop;
if (\array_key_exists($key, $a) && null === $a[$key]) {
if (null === $clone) {
$clone = clone $request;
}
$a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}();
}
}

return $a;
}

public static function castHttpClient($client, array $a, Stub $stub, bool $isNested)
{
$multiKey = sprintf("\0%s\0multi", \get_class($client));
if (isset($a[$multiKey])) {
$a[$multiKey] = new CutStub($a[$multiKey]);
}

return $a;
}

public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested)
{
$stub->cut += \count($a);
$a = [];

foreach ($response->getInfo() as $k => $v) {
$a[Caster::PREFIX_VIRTUAL.$k] = $v;
}

return $a;
}

public static function castUuid(Uuid $uuid, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'toBase58'] = $uuid->toBase58();
$a[Caster::PREFIX_VIRTUAL.'toBase32'] = $uuid->toBase32();


if (method_exists($uuid, 'getDateTime')) {
$a[Caster::PREFIX_VIRTUAL.'time'] = $uuid->getDateTime()->format('Y-m-d H:i:s.u \U\T\C');
}

return $a;
}

public static function castUlid(Ulid $ulid, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'toBase58'] = $ulid->toBase58();
$a[Caster::PREFIX_VIRTUAL.'toRfc4122'] = $ulid->toRfc4122();


if (method_exists($ulid, 'getDateTime')) {
$a[Caster::PREFIX_VIRTUAL.'time'] = $ulid->getDateTime()->format('Y-m-d H:i:s.v \U\T\C');
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class ResourceCaster
{
public static function castCurl(\CurlHandle $h, array $a, Stub $stub, bool $isNested): array
{
return curl_getinfo($h);
}

public static function castDba($dba, array $a, Stub $stub, bool $isNested)
{
$list = dba_list();
$a['file'] = $list[(int) $dba];

return $a;
}

public static function castProcess($process, array $a, Stub $stub, bool $isNested)
{
return proc_get_status($process);
}

public static function castStream($stream, array $a, Stub $stub, bool $isNested)
{
$a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested);
if ($a['uri'] ?? false) {
$a['uri'] = new LinkStub($a['uri']);
}

return $a;
}

public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested)
{
return @stream_context_get_params($stream) ?: $a;
}

public static function castGd($gd, array $a, Stub $stub, bool $isNested)
{
$a['size'] = imagesx($gd).'x'.imagesy($gd);
$a['trueColor'] = imageistruecolor($gd);

return $a;
}

public static function castMysqlLink($h, array $a, Stub $stub, bool $isNested)
{
$a['host'] = mysql_get_host_info($h);
$a['protocol'] = mysql_get_proto_info($h);
$a['server'] = mysql_get_server_info($h);

return $a;
}

public static function castOpensslX509($h, array $a, Stub $stub, bool $isNested)
{
$stub->cut = -1;
$info = openssl_x509_parse($h, false);

$pin = openssl_pkey_get_public($h);
$pin = openssl_pkey_get_details($pin)['key'];
$pin = \array_slice(explode("\n", $pin), 1, -2);
$pin = base64_decode(implode('', $pin));
$pin = base64_encode(hash('sha256', $pin, true));

$a += [
'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])),
'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])),
'expiry' => new ConstStub(date(\DateTime::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']),
'fingerprint' => new EnumStub([
'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)),
'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)),
'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)),
'pin-sha256' => new ConstStub($pin),
]),
];

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class XmlReaderCaster
{
private const NODE_TYPES = [
\XMLReader::NONE => 'NONE',
\XMLReader::ELEMENT => 'ELEMENT',
\XMLReader::ATTRIBUTE => 'ATTRIBUTE',
\XMLReader::TEXT => 'TEXT',
\XMLReader::CDATA => 'CDATA',
\XMLReader::ENTITY_REF => 'ENTITY_REF',
\XMLReader::ENTITY => 'ENTITY',
\XMLReader::PI => 'PI (Processing Instruction)',
\XMLReader::COMMENT => 'COMMENT',
\XMLReader::DOC => 'DOC',
\XMLReader::DOC_TYPE => 'DOC_TYPE',
\XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT',
\XMLReader::NOTATION => 'NOTATION',
\XMLReader::WHITESPACE => 'WHITESPACE',
\XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE',
\XMLReader::END_ELEMENT => 'END_ELEMENT',
\XMLReader::END_ENTITY => 'END_ENTITY',
\XMLReader::XML_DECLARATION => 'XML_DECLARATION',
];

public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested)
{
try {
$properties = [
'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD),
'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS),
'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE),
'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES),
];
} catch (\Error $e) {
$properties = [
'LOADDTD' => false,
'DEFAULTATTRS' => false,
'VALIDATE' => false,
'SUBST_ENTITIES' => false,
];
}

$props = Caster::PREFIX_VIRTUAL.'parserProperties';
$info = [
'localName' => $reader->localName,
'prefix' => $reader->prefix,
'nodeType' => new ConstStub(self::NODE_TYPES[$reader->nodeType], $reader->nodeType),
'depth' => $reader->depth,
'isDefault' => $reader->isDefault,
'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement,
'xmlLang' => $reader->xmlLang,
'attributeCount' => $reader->attributeCount,
'value' => $reader->value,
'namespaceURI' => $reader->namespaceURI,
'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI,
$props => $properties,
];

if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) {
$info[$props] = new EnumStub($info[$props]);
$info[$props]->cut = $count;
}

$info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count);

$stub->cut += $count + 2;

return $a + $info;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;




class DsPairStub extends Stub
{
public function __construct(string|int $key, mixed $value)
{
$this->value = [
Caster::PREFIX_VIRTUAL.'key' => $key,
Caster::PREFIX_VIRTUAL.'value' => $value,
];
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class CutStub extends Stub
{
public function __construct(mixed $value)
{
$this->value = $value;

switch (\gettype($value)) {
case 'object':
$this->type = self::TYPE_OBJECT;
$this->class = \get_class($value);

if ($value instanceof \Closure) {
ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE);
}

$this->cut = -1;
break;

case 'array':
$this->type = self::TYPE_ARRAY;
$this->class = self::ARRAY_ASSOC;
$this->cut = $this->value = \count($value);
break;

case 'resource':
case 'unknown type':
case 'resource (closed)':
$this->type = self::TYPE_RESOURCE;
$this->handle = (int) $value;
if ('Unknown' === $this->class = @get_resource_type($value)) {
$this->class = 'Closed';
}
$this->cut = -1;
break;

case 'string':
$this->type = self::TYPE_STRING;
$this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY;
$this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8');
$this->value = '';
break;
}
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class ConstStub extends Stub
{
public function __construct(string $name, string|int|float $value = null)
{
$this->class = $name;
$this->value = 1 < \func_num_args() ? $value : $name;
}

public function __toString(): string
{
return (string) $this->value;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;






class LinkStub extends ConstStub
{
public $inVendor = false;

private static array $vendorRoots;
private static array $composerRoots = [];

public function __construct(string $label, int $line = 0, string $href = null)
{
$this->value = $label;

if (null === $href) {
$href = $label;
}
if (!\is_string($href)) {
return;
}
if (str_starts_with($href, 'file://')) {
if ($href === $label) {
$label = substr($label, 7);
}
$href = substr($href, 7);
} elseif (str_contains($href, '://')) {
$this->attr['href'] = $href;

return;
}
if (!is_file($href)) {
return;
}
if ($line) {
$this->attr['line'] = $line;
}
if ($label !== $this->attr['file'] = realpath($href) ?: $href) {
return;
}
if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) {
$this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1;
$this->attr['ellipsis-type'] = 'path';
$this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0);
} elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) {
$this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2)));
$this->attr['ellipsis-type'] = 'path';
$this->attr['ellipsis-tail'] = 1;
}
}

private function getComposerRoot(string $file, bool &$inVendor)
{
if (!isset(self::$vendorRoots)) {
self::$vendorRoots = [];

foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$v = \dirname($r->getFileName(), 2);
if (is_file($v.'/composer/installed.json')) {
self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR;
}
}
}
}
$inVendor = false;

if (isset(self::$composerRoots[$dir = \dirname($file)])) {
return self::$composerRoots[$dir];
}

foreach (self::$vendorRoots as $root) {
if ($inVendor = str_starts_with($file, $root)) {
return $root;
}
}

$parent = $dir;
while (!@is_file($parent.'/composer.json')) {
if (!@file_exists($parent)) {

break;
}
if ($parent === \dirname($parent)) {
return self::$composerRoots[$dir] = false;
}

$parent = \dirname($parent);
}

return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class PgSqlCaster
{
private const PARAM_CODES = [
'server_encoding',
'client_encoding',
'is_superuser',
'session_authorization',
'DateStyle',
'TimeZone',
'IntervalStyle',
'integer_datetimes',
'application_name',
'standard_conforming_strings',
];

private const TRANSACTION_STATUS = [
\PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE',
\PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE',
\PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS',
\PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR',
\PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN',
];

private const RESULT_STATUS = [
\PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY',
\PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK',
\PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK',
\PGSQL_COPY_OUT => 'PGSQL_COPY_OUT',
\PGSQL_COPY_IN => 'PGSQL_COPY_IN',
\PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE',
\PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR',
\PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR',
];

private const DIAG_CODES = [
'severity' => \PGSQL_DIAG_SEVERITY,
'sqlstate' => \PGSQL_DIAG_SQLSTATE,
'message' => \PGSQL_DIAG_MESSAGE_PRIMARY,
'detail' => \PGSQL_DIAG_MESSAGE_DETAIL,
'hint' => \PGSQL_DIAG_MESSAGE_HINT,
'statement position' => \PGSQL_DIAG_STATEMENT_POSITION,
'internal position' => \PGSQL_DIAG_INTERNAL_POSITION,
'internal query' => \PGSQL_DIAG_INTERNAL_QUERY,
'context' => \PGSQL_DIAG_CONTEXT,
'file' => \PGSQL_DIAG_SOURCE_FILE,
'line' => \PGSQL_DIAG_SOURCE_LINE,
'function' => \PGSQL_DIAG_SOURCE_FUNCTION,
];

public static function castLargeObject($lo, array $a, Stub $stub, bool $isNested)
{
$a['seek position'] = pg_lo_tell($lo);

return $a;
}

public static function castLink($link, array $a, Stub $stub, bool $isNested)
{
$a['status'] = pg_connection_status($link);
$a['status'] = new ConstStub(\PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']);
$a['busy'] = pg_connection_busy($link);

$a['transaction'] = pg_transaction_status($link);
if (isset(self::TRANSACTION_STATUS[$a['transaction']])) {
$a['transaction'] = new ConstStub(self::TRANSACTION_STATUS[$a['transaction']], $a['transaction']);
}

$a['pid'] = pg_get_pid($link);
$a['last error'] = pg_last_error($link);
$a['last notice'] = pg_last_notice($link);
$a['host'] = pg_host($link);
$a['port'] = pg_port($link);
$a['dbname'] = pg_dbname($link);
$a['options'] = pg_options($link);
$a['version'] = pg_version($link);

foreach (self::PARAM_CODES as $v) {
if (false !== $s = pg_parameter_status($link, $v)) {
$a['param'][$v] = $s;
}
}

$a['param']['client_encoding'] = pg_client_encoding($link);
$a['param'] = new EnumStub($a['param']);

return $a;
}

public static function castResult($result, array $a, Stub $stub, bool $isNested)
{
$a['num rows'] = pg_num_rows($result);
$a['status'] = pg_result_status($result);
if (isset(self::RESULT_STATUS[$a['status']])) {
$a['status'] = new ConstStub(self::RESULT_STATUS[$a['status']], $a['status']);
}
$a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING);

if (-1 === $a['num rows']) {
foreach (self::DIAG_CODES as $k => $v) {
$a['error'][$k] = pg_result_error_field($result, $v);
}
}

$a['affected rows'] = pg_affected_rows($result);
$a['last OID'] = pg_last_oid($result);

$fields = pg_num_fields($result);

for ($i = 0; $i < $fields; ++$i) {
$field = [
'name' => pg_field_name($result, $i),
'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)),
'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)),
'nullable' => (bool) pg_field_is_null($result, $i),
'storage' => pg_field_size($result, $i).' bytes',
'display' => pg_field_prtlen($result, $i).' chars',
];
if (' (OID: )' === $field['table']) {
$field['table'] = null;
}
if ('-1 bytes' === $field['storage']) {
$field['storage'] = 'variable size';
} elseif ('1 bytes' === $field['storage']) {
$field['storage'] = '1 byte';
}
if ('1 chars' === $field['display']) {
$field['display'] = '1 char';
}
$a['fields'][] = new EnumStub($field);
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class AmqpCaster
{
private const FLAGS = [
\AMQP_DURABLE => 'AMQP_DURABLE',
\AMQP_PASSIVE => 'AMQP_PASSIVE',
\AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE',
\AMQP_AUTODELETE => 'AMQP_AUTODELETE',
\AMQP_INTERNAL => 'AMQP_INTERNAL',
\AMQP_NOLOCAL => 'AMQP_NOLOCAL',
\AMQP_AUTOACK => 'AMQP_AUTOACK',
\AMQP_IFEMPTY => 'AMQP_IFEMPTY',
\AMQP_IFUNUSED => 'AMQP_IFUNUSED',
\AMQP_MANDATORY => 'AMQP_MANDATORY',
\AMQP_IMMEDIATE => 'AMQP_IMMEDIATE',
\AMQP_MULTIPLE => 'AMQP_MULTIPLE',
\AMQP_NOWAIT => 'AMQP_NOWAIT',
\AMQP_REQUEUE => 'AMQP_REQUEUE',
];

private const EXCHANGE_TYPES = [
\AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT',
\AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT',
\AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC',
\AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS',
];

public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'is_connected' => $c->isConnected(),
];


if (isset($a["\x00AMQPConnection\x00login"])) {
return $a;
}


if (method_exists($c, 'getReadTimeout')) {
$timeout = $c->getReadTimeout();
} else {
$timeout = $c->getTimeout();
}

$a += [
$prefix.'is_connected' => $c->isConnected(),
$prefix.'login' => $c->getLogin(),
$prefix.'password' => $c->getPassword(),
$prefix.'host' => $c->getHost(),
$prefix.'vhost' => $c->getVhost(),
$prefix.'port' => $c->getPort(),
$prefix.'read_timeout' => $timeout,
];

return $a;
}

public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'is_connected' => $c->isConnected(),
$prefix.'channel_id' => $c->getChannelId(),
];


if (isset($a["\x00AMQPChannel\x00connection"])) {
return $a;
}

$a += [
$prefix.'connection' => $c->getConnection(),
$prefix.'prefetch_size' => $c->getPrefetchSize(),
$prefix.'prefetch_count' => $c->getPrefetchCount(),
];

return $a;
}

public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'flags' => self::extractFlags($c->getFlags()),
];


if (isset($a["\x00AMQPQueue\x00name"])) {
return $a;
}

$a += [
$prefix.'connection' => $c->getConnection(),
$prefix.'channel' => $c->getChannel(),
$prefix.'name' => $c->getName(),
$prefix.'arguments' => $c->getArguments(),
];

return $a;
}

public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'flags' => self::extractFlags($c->getFlags()),
];

$type = isset(self::EXCHANGE_TYPES[$c->getType()]) ? new ConstStub(self::EXCHANGE_TYPES[$c->getType()], $c->getType()) : $c->getType();


if (isset($a["\x00AMQPExchange\x00name"])) {
$a["\x00AMQPExchange\x00type"] = $type;

return $a;
}

$a += [
$prefix.'connection' => $c->getConnection(),
$prefix.'channel' => $c->getChannel(),
$prefix.'name' => $c->getName(),
$prefix.'type' => $type,
$prefix.'arguments' => $c->getArguments(),
];

return $a;
}

public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$prefix = Caster::PREFIX_VIRTUAL;

$deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode());


if (isset($a["\x00AMQPEnvelope\x00body"])) {
$a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode;

return $a;
}

if (!($filter & Caster::EXCLUDE_VERBOSE)) {
$a += [$prefix.'body' => $c->getBody()];
}

$a += [
$prefix.'delivery_tag' => $c->getDeliveryTag(),
$prefix.'is_redelivery' => $c->isRedelivery(),
$prefix.'exchange_name' => $c->getExchangeName(),
$prefix.'routing_key' => $c->getRoutingKey(),
$prefix.'content_type' => $c->getContentType(),
$prefix.'content_encoding' => $c->getContentEncoding(),
$prefix.'headers' => $c->getHeaders(),
$prefix.'delivery_mode' => $deliveryMode,
$prefix.'priority' => $c->getPriority(),
$prefix.'correlation_id' => $c->getCorrelationId(),
$prefix.'reply_to' => $c->getReplyTo(),
$prefix.'expiration' => $c->getExpiration(),
$prefix.'message_id' => $c->getMessageId(),
$prefix.'timestamp' => $c->getTimeStamp(),
$prefix.'type' => $c->getType(),
$prefix.'user_id' => $c->getUserId(),
$prefix.'app_id' => $c->getAppId(),
];

return $a;
}

private static function extractFlags(int $flags): ConstStub
{
$flagsArray = [];

foreach (self::FLAGS as $value => $name) {
if ($flags & $value) {
$flagsArray[] = $name;
}
}

if (!$flagsArray) {
$flagsArray = ['AMQP_NOPARAM'];
}

return new ConstStub(implode('|', $flagsArray), $flags);
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use ProxyManager\Proxy\ProxyInterface;
use Symfony\Component\VarDumper\Cloner\Stub;






class ProxyManagerCaster
{
public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested)
{
if ($parent = get_parent_class($c)) {
$stub->class .= ' - '.$parent;
}
$stub->class .= '@proxy';

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class PdoCaster
{
private const PDO_ATTRIBUTES = [
'CASE' => [
\PDO::CASE_LOWER => 'LOWER',
\PDO::CASE_NATURAL => 'NATURAL',
\PDO::CASE_UPPER => 'UPPER',
],
'ERRMODE' => [
\PDO::ERRMODE_SILENT => 'SILENT',
\PDO::ERRMODE_WARNING => 'WARNING',
\PDO::ERRMODE_EXCEPTION => 'EXCEPTION',
],
'TIMEOUT',
'PREFETCH',
'AUTOCOMMIT',
'PERSISTENT',
'DRIVER_NAME',
'SERVER_INFO',
'ORACLE_NULLS' => [
\PDO::NULL_NATURAL => 'NATURAL',
\PDO::NULL_EMPTY_STRING => 'EMPTY_STRING',
\PDO::NULL_TO_STRING => 'TO_STRING',
],
'CLIENT_VERSION',
'SERVER_VERSION',
'STATEMENT_CLASS',
'EMULATE_PREPARES',
'CONNECTION_STATUS',
'STRINGIFY_FETCHES',
'DEFAULT_FETCH_MODE' => [
\PDO::FETCH_ASSOC => 'ASSOC',
\PDO::FETCH_BOTH => 'BOTH',
\PDO::FETCH_LAZY => 'LAZY',
\PDO::FETCH_NUM => 'NUM',
\PDO::FETCH_OBJ => 'OBJ',
],
];

public static function castPdo(\PDO $c, array $a, Stub $stub, bool $isNested)
{
$attr = [];
$errmode = $c->getAttribute(\PDO::ATTR_ERRMODE);
$c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

foreach (self::PDO_ATTRIBUTES as $k => $v) {
if (!isset($k[0])) {
$k = $v;
$v = [];
}

try {
$attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k));
if ($v && isset($v[$attr[$k]])) {
$attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]);
}
} catch (\Exception $e) {
}
}
if (isset($attr[$k = 'STATEMENT_CLASS'][1])) {
if ($attr[$k][1]) {
$attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]);
}
$attr[$k][0] = new ClassStub($attr[$k][0]);
}

$prefix = Caster::PREFIX_VIRTUAL;
$a += [
$prefix.'inTransaction' => method_exists($c, 'inTransaction'),
$prefix.'errorInfo' => $c->errorInfo(),
$prefix.'attributes' => new EnumStub($attr),
];

if ($a[$prefix.'inTransaction']) {
$a[$prefix.'inTransaction'] = $c->inTransaction();
} else {
unset($a[$prefix.'inTransaction']);
}

if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) {
unset($a[$prefix.'errorInfo']);
}

$c->setAttribute(\PDO::ATTR_ERRMODE, $errmode);

return $a;
}

public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;
$a[$prefix.'errorInfo'] = $c->errorInfo();

if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) {
unset($a[$prefix.'errorInfo']);
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class DateCaster
{
private const PERIOD_LIMIT = 3;

public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter)
{
$prefix = Caster::PREFIX_VIRTUAL;
$location = $d->getTimezone()->getLocation();
$fromNow = (new \DateTime())->diff($d);

$title = $d->format('l, F j, Y')
."\n".self::formatInterval($fromNow).' from now'
.($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '')
;

unset(
$a[Caster::PREFIX_DYNAMIC.'date'],
$a[Caster::PREFIX_DYNAMIC.'timezone'],
$a[Caster::PREFIX_DYNAMIC.'timezone_type']
);
$a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title);

$stub->class .= $d->format(' @U');

return $a;
}

public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter)
{
$now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC'));
$numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp();
$title = number_format($numberOfSeconds, 0, '.', ' ').'s';

$i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)];

return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a;
}

private static function formatInterval(\DateInterval $i): string
{
$format = '%R ';

if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) {
$d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC'));
$i = $d->diff($d->add($i)); 
$format .= 0 < $i->days ? '%ad ' : '';
} else {
$format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : '');
}

$format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : '';
$format = '%R ' === $format ? '0s' : $format;

return $i->format(rtrim($format));
}

public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter)
{
$location = $timeZone->getLocation();
$formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P');
$title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : '';

$z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)];

return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a;
}

public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter)
{
$dates = [];
foreach (clone $p as $i => $d) {
if (self::PERIOD_LIMIT === $i) {
$now = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
$dates[] = sprintf('%s more', ($end = $p->getEndDate())
? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u')))
: $p->recurrences - $i
);
break;
}
$dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d));
}

$period = sprintf(
'every %s, from %s%s %s',
self::formatInterval($p->getDateInterval()),
$p->include_start_date ? '[' : ']',
self::formatDateTime($p->getStartDate()),
($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end).(\PHP_VERSION_ID >= 80200 && $p->include_end_date ? ']' : '[') : 'recurring '.$p->recurrences.' time/s'
);

$p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))];

return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a;
}

private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string
{
return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra);
}

private static function formatSeconds(string $s, string $us): string
{
return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us));
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;




class ImgStub extends ConstStub
{
public function __construct(string $data, string $contentType, string $size = '')
{
$this->value = '';
$this->attr['img-data'] = $data;
$this->attr['img-size'] = $size;
$this->attr['content-type'] = $contentType;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Imagine\Image\ImageInterface;
use Symfony\Component\VarDumper\Cloner\Stub;




final class ImagineCaster
{
public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array
{
$imgData = $c->get('png');
if (\strlen($imgData) > 1 * 1000 * 1000) {
$a += [
Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()),
];
} else {
$a += [
Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()),
];
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;







class IntlCaster
{
public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $isNested)
{
$a += [
Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(),
Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(),
];

return self::castError($c, $a);
}

public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$a += [
Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(),
Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(),
];

if ($filter & Caster::EXCLUDE_VERBOSE) {
$stub->cut += 3;

return self::castError($c, $a);
}

$a += [
Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub(
[
'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY),
'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED),
'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN),
'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS),
'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS),
'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS),
'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS),
'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS),
'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS),
'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER),
'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE),
'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE),
'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT),
'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH),
'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION),
'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE),
'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED),
'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS),
'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS),
'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE),
]
),
Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub(
[
'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX),
'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX),
'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX),
'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX),
'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER),
'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE),
'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET),
'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS),
]
),
Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub(
[
'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL),
'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL),
'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL),
'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL),
'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL),
'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL),
'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL),
'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL),
'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL),
'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL),
'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL),
'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL),
'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL),
'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL),
'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL),
'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL),
'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL),
'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL),
]
),
];

return self::castError($c, $a);
}

public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, bool $isNested)
{
$a += [
Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(),
Caster::PREFIX_VIRTUAL.'id' => $c->getID(),
Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(),
];

if ($c->useDaylightTime()) {
$a += [
Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(),
];
}

return self::castError($c, $a);
}

public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$a += [
Caster::PREFIX_VIRTUAL.'type' => $c->getType(),
Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(),
Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(),
Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(),
Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(),
Caster::PREFIX_VIRTUAL.'time' => $c->getTime(),
Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(),
Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(),
Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(),
];

return self::castError($c, $a);
}

public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$a += [
Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(),
Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(),
Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(),
Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(),
Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(),
Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(),
Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(),
Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(),
];

return self::castError($c, $a);
}

private static function castError(object $c, array $a): array
{
if ($errorCode = $c->getErrorCode()) {
$a += [
Caster::PREFIX_VIRTUAL.'error_code' => $errorCode,
Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(),
];
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class DOMCaster
{
private const ERROR_CODES = [
\DOM_PHP_ERR => 'DOM_PHP_ERR',
\DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR',
\DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR',
\DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR',
\DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR',
\DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR',
\DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR',
\DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR',
\DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR',
\DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR',
\DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR',
\DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR',
\DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR',
\DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR',
\DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR',
\DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR',
\DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR',
];

private const NODE_TYPES = [
\XML_ELEMENT_NODE => 'XML_ELEMENT_NODE',
\XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE',
\XML_TEXT_NODE => 'XML_TEXT_NODE',
\XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE',
\XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE',
\XML_ENTITY_NODE => 'XML_ENTITY_NODE',
\XML_PI_NODE => 'XML_PI_NODE',
\XML_COMMENT_NODE => 'XML_COMMENT_NODE',
\XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE',
\XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE',
\XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE',
\XML_NOTATION_NODE => 'XML_NOTATION_NODE',
\XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE',
\XML_DTD_NODE => 'XML_DTD_NODE',
\XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE',
\XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE',
\XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE',
\XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE',
];

public static function castException(\DOMException $e, array $a, Stub $stub, bool $isNested)
{
$k = Caster::PREFIX_PROTECTED.'code';
if (isset($a[$k], self::ERROR_CODES[$a[$k]])) {
$a[$k] = new ConstStub(self::ERROR_CODES[$a[$k]], $a[$k]);
}

return $a;
}

public static function castLength($dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'length' => $dom->length,
];

return $a;
}

public static function castImplementation(\DOMImplementation $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
Caster::PREFIX_VIRTUAL.'Core' => '1.0',
Caster::PREFIX_VIRTUAL.'XML' => '2.0',
];

return $a;
}

public static function castNode(\DOMNode $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'nodeName' => $dom->nodeName,
'nodeValue' => new CutStub($dom->nodeValue),
'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType),
'parentNode' => new CutStub($dom->parentNode),
'childNodes' => $dom->childNodes,
'firstChild' => new CutStub($dom->firstChild),
'lastChild' => new CutStub($dom->lastChild),
'previousSibling' => new CutStub($dom->previousSibling),
'nextSibling' => new CutStub($dom->nextSibling),
'attributes' => $dom->attributes,
'ownerDocument' => new CutStub($dom->ownerDocument),
'namespaceURI' => $dom->namespaceURI,
'prefix' => $dom->prefix,
'localName' => $dom->localName,
'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI,
'textContent' => new CutStub($dom->textContent),
];

return $a;
}

public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'nodeName' => $dom->nodeName,
'nodeValue' => new CutStub($dom->nodeValue),
'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType),
'prefix' => $dom->prefix,
'localName' => $dom->localName,
'namespaceURI' => $dom->namespaceURI,
'ownerDocument' => new CutStub($dom->ownerDocument),
'parentNode' => new CutStub($dom->parentNode),
];

return $a;
}

public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$a += [
'doctype' => $dom->doctype,
'implementation' => $dom->implementation,
'documentElement' => new CutStub($dom->documentElement),
'actualEncoding' => $dom->actualEncoding,
'encoding' => $dom->encoding,
'xmlEncoding' => $dom->xmlEncoding,
'standalone' => $dom->standalone,
'xmlStandalone' => $dom->xmlStandalone,
'version' => $dom->version,
'xmlVersion' => $dom->xmlVersion,
'strictErrorChecking' => $dom->strictErrorChecking,
'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI,
'config' => $dom->config,
'formatOutput' => $dom->formatOutput,
'validateOnParse' => $dom->validateOnParse,
'resolveExternals' => $dom->resolveExternals,
'preserveWhiteSpace' => $dom->preserveWhiteSpace,
'recover' => $dom->recover,
'substituteEntities' => $dom->substituteEntities,
];

if (!($filter & Caster::EXCLUDE_VERBOSE)) {
$formatOutput = $dom->formatOutput;
$dom->formatOutput = true;
$a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()];
$dom->formatOutput = $formatOutput;
}

return $a;
}

public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'data' => $dom->data,
'length' => $dom->length,
];

return $a;
}

public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'name' => $dom->name,
'specified' => $dom->specified,
'value' => $dom->value,
'ownerElement' => $dom->ownerElement,
'schemaTypeInfo' => $dom->schemaTypeInfo,
];

return $a;
}

public static function castElement(\DOMElement $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'tagName' => $dom->tagName,
'schemaTypeInfo' => $dom->schemaTypeInfo,
];

return $a;
}

public static function castText(\DOMText $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'wholeText' => $dom->wholeText,
];

return $a;
}

public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'typeName' => $dom->typeName,
'typeNamespace' => $dom->typeNamespace,
];

return $a;
}

public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'severity' => $dom->severity,
'message' => $dom->message,
'type' => $dom->type,
'relatedException' => $dom->relatedException,
'related_data' => $dom->related_data,
'location' => $dom->location,
];

return $a;
}

public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'lineNumber' => $dom->lineNumber,
'columnNumber' => $dom->columnNumber,
'offset' => $dom->offset,
'relatedNode' => $dom->relatedNode,
'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri,
];

return $a;
}

public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'name' => $dom->name,
'entities' => $dom->entities,
'notations' => $dom->notations,
'publicId' => $dom->publicId,
'systemId' => $dom->systemId,
'internalSubset' => $dom->internalSubset,
];

return $a;
}

public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'publicId' => $dom->publicId,
'systemId' => $dom->systemId,
];

return $a;
}

public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'publicId' => $dom->publicId,
'systemId' => $dom->systemId,
'notationName' => $dom->notationName,
'actualEncoding' => $dom->actualEncoding,
'encoding' => $dom->encoding,
'version' => $dom->version,
];

return $a;
}

public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'target' => $dom->target,
'data' => $dom->data,
];

return $a;
}

public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, bool $isNested)
{
$a += [
'document' => $dom->document,
];

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use RdKafka\Conf;
use RdKafka\Exception as RdKafkaException;
use RdKafka\KafkaConsumer;
use RdKafka\Message;
use RdKafka\Metadata\Broker as BrokerMetadata;
use RdKafka\Metadata\Collection as CollectionMetadata;
use RdKafka\Metadata\Partition as PartitionMetadata;
use RdKafka\Metadata\Topic as TopicMetadata;
use RdKafka\Topic;
use RdKafka\TopicConf;
use RdKafka\TopicPartition;
use Symfony\Component\VarDumper\Cloner\Stub;






class RdKafkaCaster
{
public static function castKafkaConsumer(KafkaConsumer $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

try {
$assignment = $c->getAssignment();
} catch (RdKafkaException $e) {
$assignment = [];
}

$a += [
$prefix.'subscription' => $c->getSubscription(),
$prefix.'assignment' => $assignment,
];

$a += self::extractMetadata($c);

return $a;
}

public static function castTopic(Topic $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'name' => $c->getName(),
];

return $a;
}

public static function castTopicPartition(TopicPartition $c, array $a)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'offset' => $c->getOffset(),
$prefix.'partition' => $c->getPartition(),
$prefix.'topic' => $c->getTopic(),
];

return $a;
}

public static function castMessage(Message $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'errstr' => $c->errstr(),
];

return $a;
}

public static function castConf(Conf $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

foreach ($c->dump() as $key => $value) {
$a[$prefix.$key] = $value;
}

return $a;
}

public static function castTopicConf(TopicConf $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

foreach ($c->dump() as $key => $value) {
$a[$prefix.$key] = $value;
}

return $a;
}

public static function castRdKafka(\RdKafka $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'out_q_len' => $c->getOutQLen(),
];

$a += self::extractMetadata($c);

return $a;
}

public static function castCollectionMetadata(CollectionMetadata $c, array $a, Stub $stub, bool $isNested)
{
$a += iterator_to_array($c);

return $a;
}

public static function castTopicMetadata(TopicMetadata $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'name' => $c->getTopic(),
$prefix.'partitions' => $c->getPartitions(),
];

return $a;
}

public static function castPartitionMetadata(PartitionMetadata $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'id' => $c->getId(),
$prefix.'err' => $c->getErr(),
$prefix.'leader' => $c->getLeader(),
];

return $a;
}

public static function castBrokerMetadata(BrokerMetadata $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

$a += [
$prefix.'id' => $c->getId(),
$prefix.'host' => $c->getHost(),
$prefix.'port' => $c->getPort(),
];

return $a;
}

private static function extractMetadata(KafkaConsumer|\RdKafka $c)
{
$prefix = Caster::PREFIX_VIRTUAL;

try {
$m = $c->getMetadata(true, null, 500);
} catch (RdKafkaException $e) {
return [];
}

return [
$prefix.'orig_broker_id' => $m->getOrigBrokerId(),
$prefix.'orig_broker_name' => $m->getOrigBrokerName(),
$prefix.'brokers' => $m->getBrokers(),
$prefix.'topics' => $m->getTopics(),
];
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class XmlResourceCaster
{
private const XML_ERRORS = [
\XML_ERROR_NONE => 'XML_ERROR_NONE',
\XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY',
\XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX',
\XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS',
\XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN',
\XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN',
\XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR',
\XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH',
\XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE',
\XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT',
\XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF',
\XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY',
\XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF',
\XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY',
\XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF',
\XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF',
\XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF',
\XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI',
\XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING',
\XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING',
\XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION',
\XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING',
];

public static function castXml($h, array $a, Stub $stub, bool $isNested)
{
$a['current_byte_index'] = xml_get_current_byte_index($h);
$a['current_column_number'] = xml_get_current_column_number($h);
$a['current_line_number'] = xml_get_current_line_number($h);
$a['error_code'] = xml_get_error_code($h);

if (isset(self::XML_ERRORS[$a['error_code']])) {
$a['error_code'] = new ConstStub(self::XML_ERRORS[$a['error_code']], $a['error_code']);
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;






class ArgsStub extends EnumStub
{
private static array $parameters = [];

public function __construct(array $args, string $function, ?string $class)
{
[$variadic, $params] = self::getParameters($function, $class);

$values = [];
foreach ($args as $k => $v) {
$values[$k] = !\is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v;
}
if (null === $params) {
parent::__construct($values, false);

return;
}
if (\count($values) < \count($params)) {
$params = \array_slice($params, 0, \count($values));
} elseif (\count($values) > \count($params)) {
$values[] = new EnumStub(array_splice($values, \count($params)), false);
$params[] = $variadic;
}
if (['...'] === $params) {
$this->dumpKeys = false;
$this->value = $values[0]->value;
} else {
$this->value = array_combine($params, $values);
}
}

private static function getParameters(string $function, ?string $class): array
{
if (isset(self::$parameters[$k = $class.'::'.$function])) {
return self::$parameters[$k];
}

try {
$r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function);
} catch (\ReflectionException $e) {
return [null, null];
}

$variadic = '...';
$params = [];
foreach ($r->getParameters() as $v) {
$k = '$'.$v->name;
if ($v->isPassedByReference()) {
$k = '&'.$k;
}
if ($v->isVariadic()) {
$variadic .= $k;
} else {
$params[] = $k;
}
}

return self::$parameters[$k] = [$variadic, $params];
}
}
<?php










namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;








class ReflectionCaster
{
public const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo'];

private const EXTRA_MAP = [
'docComment' => 'getDocComment',
'extension' => 'getExtensionName',
'isDisabled' => 'isDisabled',
'isDeprecated' => 'isDeprecated',
'isInternal' => 'isInternal',
'isUserDefined' => 'isUserDefined',
'isGenerator' => 'isGenerator',
'isVariadic' => 'isVariadic',
];

public static function castClosure(\Closure $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$prefix = Caster::PREFIX_VIRTUAL;
$c = new \ReflectionFunction($c);

$a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter);

if (!str_contains($c->name, '{closure}')) {
$stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name;
unset($a[$prefix.'class']);
}
unset($a[$prefix.'extra']);

$stub->class .= self::getSignature($a);

if ($f = $c->getFileName()) {
$stub->attr['file'] = $f;
$stub->attr['line'] = $c->getStartLine();
}

unset($a[$prefix.'parameters']);

if ($filter & Caster::EXCLUDE_VERBOSE) {
$stub->cut += ($c->getFileName() ? 2 : 0) + \count($a);

return [];
}

if ($f) {
$a[$prefix.'file'] = new LinkStub($f, $c->getStartLine());
$a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine();
}

return $a;
}

public static function unsetClosureFileInfo(\Closure $c, array $a)
{
unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']);

return $a;
}

public static function castGenerator(\Generator $c, array $a, Stub $stub, bool $isNested)
{

try {
$reflectionGenerator = new \ReflectionGenerator($c);
} catch (\Exception $e) {
$a[Caster::PREFIX_VIRTUAL.'closed'] = true;

return $a;
}

return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested);
}

public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

if ($c instanceof \ReflectionNamedType) {
$a += [
$prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c,
$prefix.'allowsNull' => $c->allowsNull(),
$prefix.'isBuiltin' => $c->isBuiltin(),
];
} elseif ($c instanceof \ReflectionUnionType || $c instanceof \ReflectionIntersectionType) {
$a[$prefix.'allowsNull'] = $c->allowsNull();
self::addMap($a, $c, [
'types' => 'getTypes',
]);
} else {
$a[$prefix.'allowsNull'] = $c->allowsNull();
}

return $a;
}

public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested)
{
self::addMap($a, $c, [
'name' => 'getName',
'arguments' => 'getArguments',
]);

return $a;
}

public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

if ($c->getThis()) {
$a[$prefix.'this'] = new CutStub($c->getThis());
}
$function = $c->getFunction();
$frame = [
'class' => $function->class ?? null,
'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null,
'function' => $function->name,
'file' => $c->getExecutingFile(),
'line' => $c->getExecutingLine(),
];
if ($trace = $c->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS)) {
$function = new \ReflectionGenerator($c->getExecutingGenerator());
array_unshift($trace, [
'function' => 'yield',
'file' => $function->getExecutingFile(),
'line' => $function->getExecutingLine() - (int) (\PHP_VERSION_ID < 80100),
]);
$trace[] = $frame;
$a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1);
} else {
$function = new FrameStub($frame, false, true);
$function = ExceptionCaster::castFrameStub($function, [], $function, true);
$a[$prefix.'executing'] = $function[$prefix.'src'];
}

$a[Caster::PREFIX_VIRTUAL.'closed'] = false;

return $a;
}

public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$prefix = Caster::PREFIX_VIRTUAL;

if ($n = \Reflection::getModifierNames($c->getModifiers())) {
$a[$prefix.'modifiers'] = implode(' ', $n);
}

self::addMap($a, $c, [
'extends' => 'getParentClass',
'implements' => 'getInterfaceNames',
'constants' => 'getReflectionConstants',
]);

foreach ($c->getProperties() as $n) {
$a[$prefix.'properties'][$n->name] = $n;
}

foreach ($c->getMethods() as $n) {
$a[$prefix.'methods'][$n->name] = $n;
}

self::addAttributes($a, $c, $prefix);

if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
self::addExtra($a, $c);
}

return $a;
}

public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
$prefix = Caster::PREFIX_VIRTUAL;

self::addMap($a, $c, [
'returnsReference' => 'returnsReference',
'returnType' => 'getReturnType',
'class' => \PHP_VERSION_ID >= 80111 ? 'getClosureCalledClass' : 'getClosureScopeClass',
'this' => 'getClosureThis',
]);

if (isset($a[$prefix.'returnType'])) {
$v = $a[$prefix.'returnType'];
$v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v;
$a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType'] instanceof \ReflectionNamedType && $a[$prefix.'returnType']->allowsNull() && 'mixed' !== $v ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']);
}
if (isset($a[$prefix.'class'])) {
$a[$prefix.'class'] = new ClassStub($a[$prefix.'class']);
}
if (isset($a[$prefix.'this'])) {
$a[$prefix.'this'] = new CutStub($a[$prefix.'this']);
}

foreach ($c->getParameters() as $v) {
$k = '$'.$v->name;
if ($v->isVariadic()) {
$k = '...'.$k;
}
if ($v->isPassedByReference()) {
$k = '&'.$k;
}
$a[$prefix.'parameters'][$k] = $v;
}
if (isset($a[$prefix.'parameters'])) {
$a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']);
}

self::addAttributes($a, $c, $prefix);

if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) {
foreach ($v as $k => &$v) {
if (\is_object($v)) {
$a[$prefix.'use']['$'.$k] = new CutStub($v);
} else {
$a[$prefix.'use']['$'.$k] = &$v;
}
}
unset($v);
$a[$prefix.'use'] = new EnumStub($a[$prefix.'use']);
}

if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
self::addExtra($a, $c);
}

return $a;
}

public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
$a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue();

self::addAttributes($a, $c);

return $a;
}

public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));

return $a;
}

public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;

self::addMap($a, $c, [
'position' => 'getPosition',
'isVariadic' => 'isVariadic',
'byReference' => 'isPassedByReference',
'allowsNull' => 'allowsNull',
]);

self::addAttributes($a, $c, $prefix);

if ($v = $c->getType()) {
$a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v;
}

if (isset($a[$prefix.'typeHint'])) {
$v = $a[$prefix.'typeHint'];
$a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']);
} else {
unset($a[$prefix.'allowsNull']);
}

if ($c->isOptional()) {
try {
$a[$prefix.'default'] = $v = $c->getDefaultValue();
if ($c->isDefaultValueConstant()) {
$a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
}
if (null === $v) {
unset($a[$prefix.'allowsNull']);
}
} catch (\ReflectionException $e) {
}
}

return $a;
}

public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));

self::addAttributes($a, $c);
self::addExtra($a, $c);

return $a;
}

public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested)
{
$a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId();

return $a;
}

public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $isNested)
{
self::addMap($a, $c, [
'version' => 'getVersion',
'dependencies' => 'getDependencies',
'iniEntries' => 'getIniEntries',
'isPersistent' => 'isPersistent',
'isTemporary' => 'isTemporary',
'constants' => 'getConstants',
'functions' => 'getFunctions',
'classes' => 'getClasses',
]);

return $a;
}

public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, bool $isNested)
{
self::addMap($a, $c, [
'version' => 'getVersion',
'author' => 'getAuthor',
'copyright' => 'getCopyright',
'url' => 'getURL',
]);

return $a;
}

public static function getSignature(array $a)
{
$prefix = Caster::PREFIX_VIRTUAL;
$signature = '';

if (isset($a[$prefix.'parameters'])) {
foreach ($a[$prefix.'parameters']->value as $k => $param) {
$signature .= ', ';
if ($type = $param->getType()) {
if (!$type instanceof \ReflectionNamedType) {
$signature .= $type.' ';
} else {
if (!$param->isOptional() && $param->allowsNull() && 'mixed' !== $type->getName()) {
$signature .= '?';
}
$signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' ';
}
}
$signature .= $k;

if (!$param->isDefaultValueAvailable()) {
continue;
}
$v = $param->getDefaultValue();
$signature .= ' = ';

if ($param->isDefaultValueConstant()) {
$signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1);
} elseif (null === $v) {
$signature .= 'null';
} elseif (\is_array($v)) {
$signature .= $v ? '[…'.\count($v).']' : '[]';
} elseif (\is_string($v)) {
$signature .= 10 > \strlen($v) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'";
} elseif (\is_bool($v)) {
$signature .= $v ? 'true' : 'false';
} elseif (\is_object($v)) {
$signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1);
} else {
$signature .= $v;
}
}
}
$signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')';

if (isset($a[$prefix.'returnType'])) {
$signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1);
}

return $signature;
}

private static function addExtra(array &$a, \Reflector $c)
{
$x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : [];

if (method_exists($c, 'getFileName') && $m = $c->getFileName()) {
$x['file'] = new LinkStub($m, $c->getStartLine());
$x['line'] = $c->getStartLine().' to '.$c->getEndLine();
}

self::addMap($x, $c, self::EXTRA_MAP, '');

if ($x) {
$a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x);
}
}

private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL)
{
foreach ($map as $k => $m) {
if ('isDisabled' === $k) {
continue;
}

if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) {
$a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m;
}
}
}

private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void
{
foreach ($c->getAttributes() as $n) {
$a[$prefix.'attributes'][] = $n;
}
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper;

use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;




class ContextualizedDumper implements DataDumperInterface
{
private $wrappedDumper;
private array $contextProviders;




public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders)
{
$this->wrappedDumper = $wrappedDumper;
$this->contextProviders = $contextProviders;
}

public function dump(Data $data)
{
$context = [];
foreach ($this->contextProviders as $contextProvider) {
$context[\get_class($contextProvider)] = $contextProvider->getContext();
}

$this->wrappedDumper->dump($data->withContext($context));
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper;

use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\DumperInterface;






abstract class AbstractDumper implements DataDumperInterface, DumperInterface
{
public const DUMP_LIGHT_ARRAY = 1;
public const DUMP_STRING_LENGTH = 2;
public const DUMP_COMMA_SEPARATOR = 4;
public const DUMP_TRAILING_COMMA = 8;

public static $defaultOutput = 'php://output';

protected $line = '';
protected $lineDumper;
protected $outputStream;
protected $decimalPoint = '.';
protected $indentPad = '  ';
protected $flags;

private string $charset = '';






public function __construct($output = null, string $charset = null, int $flags = 0)
{
$this->flags = $flags;
$this->setCharset($charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8');
$this->setOutput($output ?: static::$defaultOutput);
if (!$output && \is_string(static::$defaultOutput)) {
static::$defaultOutput = $this->outputStream;
}
}








public function setOutput($output)
{
$prev = $this->outputStream ?? $this->lineDumper;

if (\is_callable($output)) {
$this->outputStream = null;
$this->lineDumper = $output;
} else {
if (\is_string($output)) {
$output = fopen($output, 'w');
}
$this->outputStream = $output;
$this->lineDumper = [$this, 'echoLine'];
}

return $prev;
}






public function setCharset(string $charset): string
{
$prev = $this->charset;

$charset = strtoupper($charset);
$charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset;

$this->charset = $charset;

return $prev;
}








public function setIndentPad(string $pad): string
{
$prev = $this->indentPad;
$this->indentPad = $pad;

return $prev;
}








public function dump(Data $data, $output = null): ?string
{
if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) {
setlocale(\LC_NUMERIC, 'C');
}

if ($returnDump = true === $output) {
$output = fopen('php://memory', 'r+');
}
if ($output) {
$prevOutput = $this->setOutput($output);
}
try {
$data->dump($this);
$this->dumpLine(-1);

if ($returnDump) {
$result = stream_get_contents($output, -1, 0);
fclose($output);

return $result;
}
} finally {
if ($output) {
$this->setOutput($prevOutput);
}
if ($locale) {
setlocale(\LC_NUMERIC, $locale);
}
}

return null;
}







protected function dumpLine(int $depth)
{
($this->lineDumper)($this->line, $depth, $this->indentPad);
$this->line = '';
}




protected function echoLine(string $line, int $depth, string $indentPad)
{
if (-1 !== $depth) {
fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n");
}
}




protected function utf8Encode(?string $s): ?string
{
if (null === $s || preg_match('//u', $s)) {
return $s;
}

if (!\function_exists('iconv')) {
throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
}

if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
return $c;
}
if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) {
return $c;
}

return iconv('CP850', 'UTF-8', $s);
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper;

use Symfony\Component\VarDumper\Cloner\Cursor;
use Symfony\Component\VarDumper\Cloner\Data;






class HtmlDumper extends CliDumper
{
public static $defaultOutput = 'php://output';

protected static $themes = [
'dark' => [
'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
'num' => 'font-weight:bold; color:#1299DA',
'const' => 'font-weight:bold',
'str' => 'font-weight:bold; color:#56DB3A',
'note' => 'color:#1299DA',
'ref' => 'color:#A0A0A0',
'public' => 'color:#FFFFFF',
'protected' => 'color:#FFFFFF',
'private' => 'color:#FFFFFF',
'meta' => 'color:#B729D9',
'key' => 'color:#56DB3A',
'index' => 'color:#1299DA',
'ellipsis' => 'color:#FF8400',
'ns' => 'user-select:none;',
],
'light' => [
'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
'num' => 'font-weight:bold; color:#1299DA',
'const' => 'font-weight:bold',
'str' => 'font-weight:bold; color:#629755;',
'note' => 'color:#6897BB',
'ref' => 'color:#6E6E6E',
'public' => 'color:#262626',
'protected' => 'color:#262626',
'private' => 'color:#262626',
'meta' => 'color:#B729D9',
'key' => 'color:#789339',
'index' => 'color:#1299DA',
'ellipsis' => 'color:#CC7832',
'ns' => 'user-select:none;',
],
];

protected $dumpHeader;
protected $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">';
protected $dumpSuffix = '</pre><script>Sfdump(%s)</script>';
protected $dumpId = 'sf-dump';
protected $colors = true;
protected $headerIsDumped = false;
protected $lastDepth = -1;
protected $styles;

private array $displayOptions = [
'maxDepth' => 1,
'maxStringLength' => 160,
'fileLinkFormat' => null,
];
private array $extraDisplayOptions = [];




public function __construct($output = null, string $charset = null, int $flags = 0)
{
AbstractDumper::__construct($output, $charset, $flags);
$this->dumpId = 'sf-dump-'.mt_rand();
$this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
$this->styles = static::$themes['dark'] ?? self::$themes['dark'];
}




public function setStyles(array $styles)
{
$this->headerIsDumped = false;
$this->styles = $styles + $this->styles;
}

public function setTheme(string $themeName)
{
if (!isset(static::$themes[$themeName])) {
throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class));
}

$this->setStyles(static::$themes[$themeName]);
}






public function setDisplayOptions(array $displayOptions)
{
$this->headerIsDumped = false;
$this->displayOptions = $displayOptions + $this->displayOptions;
}




public function setDumpHeader(?string $header)
{
$this->dumpHeader = $header;
}




public function setDumpBoundaries(string $prefix, string $suffix)
{
$this->dumpPrefix = $prefix;
$this->dumpSuffix = $suffix;
}




public function dump(Data $data, $output = null, array $extraDisplayOptions = []): ?string
{
$this->extraDisplayOptions = $extraDisplayOptions;
$result = parent::dump($data, $output);
$this->dumpId = 'sf-dump-'.mt_rand();

return $result;
}




protected function getDumpHeader()
{
$this->headerIsDumped = $this->outputStream ?? $this->lineDumper;

if (null !== $this->dumpHeader) {
return $this->dumpHeader;
}

$line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML'
<script>
Sfdump = window.Sfdump || (function (doc) {

var refStyle = doc.createElement('style'),
    rxEsc = /([.*+?^${}()|\[\]\/\\])/g,
    idRx = /\bsf-dump-\d+-ref[012]\w+\b/,
    keyHint = 0 <= navigator.platform.toUpperCase().indexOf('MAC') ? 'Cmd' : 'Ctrl',
    addEventListener = function (e, n, cb) {
        e.addEventListener(n, cb, false);
    };

refStyle.innerHTML = 'pre.sf-dump .sf-dump-compact, .sf-dump-str-collapse .sf-dump-str-collapse, .sf-dump-str-expand .sf-dump-str-expand { display: none; }';
doc.head.appendChild(refStyle);
refStyle = doc.createElement('style');
doc.head.appendChild(refStyle);

if (!doc.addEventListener) {
    addEventListener = function (element, eventName, callback) {
        element.attachEvent('on' + eventName, function (e) {
            e.preventDefault = function () {e.returnValue = false;};
            e.target = e.srcElement;
            callback(e);
        });
    };
}

function toggle(a, recursive) {
    var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass;

    if (/\bsf-dump-compact\b/.test(oldClass)) {
        arrow = '▼';
        newClass = 'sf-dump-expanded';
    } else if (/\bsf-dump-expanded\b/.test(oldClass)) {
        arrow = '▶';
        newClass = 'sf-dump-compact';
    } else {
        return false;
    }

    if (doc.createEvent && s.dispatchEvent) {
        var event = doc.createEvent('Event');
        event.initEvent('sf-dump-expanded' === newClass ? 'sfbeforedumpexpand' : 'sfbeforedumpcollapse', true, false);

        s.dispatchEvent(event);
    }

    a.lastChild.innerHTML = arrow;
    s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass);

    if (recursive) {
        try {
            a = s.querySelectorAll('.'+oldClass);
            for (s = 0; s < a.length; ++s) {
                if (-1 == a[s].className.indexOf(newClass)) {
                    a[s].className = newClass;
                    a[s].previousSibling.lastChild.innerHTML = arrow;
                }
            }
        } catch (e) {
        }
    }

    return true;
};

function collapse(a, recursive) {
    var s = a.nextSibling || {}, oldClass = s.className;

    if (/\bsf-dump-expanded\b/.test(oldClass)) {
        toggle(a, recursive);

        return true;
    }

    return false;
};

function expand(a, recursive) {
    var s = a.nextSibling || {}, oldClass = s.className;

    if (/\bsf-dump-compact\b/.test(oldClass)) {
        toggle(a, recursive);

        return true;
    }

    return false;
};

function collapseAll(root) {
    var a = root.querySelector('a.sf-dump-toggle');
    if (a) {
        collapse(a, true);
        expand(a);

        return true;
    }

    return false;
}

function reveal(node) {
    var previous, parents = [];

    while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) {
        parents.push(previous);
    }

    if (0 !== parents.length) {
        parents.forEach(function (parent) {
            expand(parent);
        });

        return true;
    }

    return false;
}

function highlight(root, activeNode, nodes) {
    resetHighlightedNodes(root);

    Array.from(nodes||[]).forEach(function (node) {
        if (!/\bsf-dump-highlight\b/.test(node.className)) {
            node.className = node.className + ' sf-dump-highlight';
        }
    });

    if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) {
        activeNode.className = activeNode.className + ' sf-dump-highlight-active';
    }
}

function resetHighlightedNodes(root) {
    Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) {
        strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, '');
        strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, '');
    });
}

return function (root, x) {
    root = doc.getElementById(root);

    var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || '  ').replace(rxEsc, '\\$1')+')+', 'm'),
        options = {$options},
        elt = root.getElementsByTagName('A'),
        len = elt.length,
        i = 0, s, h,
        t = [];

    while (i < len) t.push(elt[i++]);

    for (i in x) {
        options[i] = x[i];
    }

    function a(e, f) {
        addEventListener(root, e, function (e, n) {
            if ('A' == e.target.tagName) {
                f(e.target, e);
            } else if ('A' == e.target.parentNode.tagName) {
                f(e.target.parentNode, e);
            } else {
                n = /\bsf-dump-ellipsis\b/.test(e.target.className) ? e.target.parentNode : e.target;

                if ((n = n.nextElementSibling) && 'A' == n.tagName) {
                    if (!/\bsf-dump-toggle\b/.test(n.className)) {
                        n = n.nextElementSibling || n;
                    }

                    f(n, e, true);
                }
            }
        });
    };
    function isCtrlKey(e) {
        return e.ctrlKey || e.metaKey;
    }
    function xpathString(str) {
        var parts = str.match(/[^'"]+|['"]/g).map(function (part) {
            if ("'" == part)  {
                return '"\'"';
            }
            if ('"' == part) {
                return "'\"'";
            }

            return "'" + part + "'";
        });

        return "concat(" + parts.join(",") + ", '')";
    }
    function xpathHasClass(className) {
        return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')";
    }
    addEventListener(root, 'mouseover', function (e) {
        if ('' != refStyle.innerHTML) {
            refStyle.innerHTML = '';
        }
    });
    a('mouseover', function (a, e, c) {
        if (c) {
            e.target.style.cursor = "pointer";
        } else if (a = idRx.exec(a.className)) {
            try {
                refStyle.innerHTML = 'pre.sf-dump .'+a[0]+'{background-color: #B729D9; color: #FFF !important; border-radius: 2px}';
            } catch (e) {
            }
        }
    });
    a('click', function (a, e, c) {
        if (/\bsf-dump-toggle\b/.test(a.className)) {
            e.preventDefault();
            if (!toggle(a, isCtrlKey(e))) {
                var r = doc.getElementById(a.getAttribute('href').slice(1)),
                    s = r.previousSibling,
                    f = r.parentNode,
                    t = a.parentNode;
                t.replaceChild(r, a);
                f.replaceChild(a, s);
                t.insertBefore(s, r);
                f = f.firstChild.nodeValue.match(indentRx);
                t = t.firstChild.nodeValue.match(indentRx);
                if (f && t && f[0] !== t[0]) {
                    r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]);
                }
                if (/\bsf-dump-compact\b/.test(r.className)) {
                    toggle(s, isCtrlKey(e));
                }
            }

            if (c) {
            } else if (doc.getSelection) {
                try {
                    doc.getSelection().removeAllRanges();
                } catch (e) {
                    doc.getSelection().empty();
                }
            } else {
                doc.selection.empty();
            }
        } else if (/\bsf-dump-str-toggle\b/.test(a.className)) {
            e.preventDefault();
            e = a.parentNode.parentNode;
            e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className);
        }
    });

    elt = root.getElementsByTagName('SAMP');
    len = elt.length;
    i = 0;

    while (i < len) t.push(elt[i++]);
    len = t.length;

    for (i = 0; i < len; ++i) {
        elt = t[i];
        if ('SAMP' == elt.tagName) {
            a = elt.previousSibling || {};
            if ('A' != a.tagName) {
                a = doc.createElement('A');
                a.className = 'sf-dump-ref';
                elt.parentNode.insertBefore(a, elt);
            } else {
                a.innerHTML += ' ';
            }
            a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children';
            a.innerHTML += elt.className == 'sf-dump-compact' ? '<span>▶</span>' : '<span>▼</span>';
            a.className += ' sf-dump-toggle';

            x = 1;
            if ('sf-dump' != elt.parentNode.className) {
                x += elt.parentNode.getAttribute('data-depth')/1;
            }
        } else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
            a = a.slice(1);
            elt.className += ' '+a;

            if (/[\[{]$/.test(elt.previousSibling.nodeValue)) {
                a = a != elt.nextSibling.id && doc.getElementById(a);
                try {
                    s = a.nextSibling;
                    elt.appendChild(a);
                    s.parentNode.insertBefore(a, s);
                    if (/^[@#]/.test(elt.innerHTML)) {
                        elt.innerHTML += ' <span>▶</span>';
                    } else {
                        elt.innerHTML = '<span>▶</span>';
                        elt.className = 'sf-dump-ref';
                    }
                    elt.className += ' sf-dump-toggle';
                } catch (e) {
                    if ('&' == elt.innerHTML.charAt(0)) {
                        elt.innerHTML = '…';
                        elt.className = 'sf-dump-ref';
                    }
                }
            }
        }
    }

    if (doc.evaluate && Array.from && root.children.length > 1) {
        root.setAttribute('tabindex', 0);

        SearchState = function () {
            this.nodes = [];
            this.idx = 0;
        };
        SearchState.prototype = {
            next: function () {
                if (this.isEmpty()) {
                    return this.current();
                }
                this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0;

                return this.current();
            },
            previous: function () {
                if (this.isEmpty()) {
                    return this.current();
                }
                this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1);

                return this.current();
            },
            isEmpty: function () {
                return 0 === this.count();
            },
            current: function () {
                if (this.isEmpty()) {
                    return null;
                }
                return this.nodes[this.idx];
            },
            reset: function () {
                this.nodes = [];
                this.idx = 0;
            },
            count: function () {
                return this.nodes.length;
            },
        };

        function showCurrent(state)
        {
            var currentNode = state.current(), currentRect, searchRect;
            if (currentNode) {
                reveal(currentNode);
                highlight(root, currentNode, state.nodes);
                if ('scrollIntoView' in currentNode) {
                    currentNode.scrollIntoView(true);
                    currentRect = currentNode.getBoundingClientRect();
                    searchRect = search.getBoundingClientRect();
                    if (currentRect.top < (searchRect.top + searchRect.height)) {
                        window.scrollBy(0, -(searchRect.top + searchRect.height + 5));
                    }
                }
            }
            counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count();
        }

        var search = doc.createElement('div');
        search.className = 'sf-dump-search-wrapper sf-dump-search-hidden';
        search.innerHTML = '
            <input type="text" class="sf-dump-search-input">
            <span class="sf-dump-search-count">0 of 0<\/span>
            <button type="button" class="sf-dump-search-input-previous" tabindex="-1">
                <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 1331l-166 165q-19 19-45 19t-45-19L896 965l-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/><\/svg>
            <\/button>
            <button type="button" class="sf-dump-search-input-next" tabindex="-1">
                <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19L109 808q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/><\/svg>
            <\/button>
        ';
        root.insertBefore(search, root.firstChild);

        var state = new SearchState();
        var searchInput = search.querySelector('.sf-dump-search-input');
        var counter = search.querySelector('.sf-dump-search-count');
        var searchInputTimer = 0;
        var previousSearchQuery = '';

        addEventListener(searchInput, 'keyup', function (e) {
            var searchQuery = e.target.value;
            /* Don't perform anything if the pressed key didn't change the query */
            if (searchQuery === previousSearchQuery) {
                return;
            }
            previousSearchQuery = searchQuery;
            clearTimeout(searchInputTimer);
            searchInputTimer = setTimeout(function () {
                state.reset();
                collapseAll(root);
                resetHighlightedNodes(root);
                if ('' === searchQuery) {
                    counter.textContent = '0 of 0';

                    return;
                }

                var classMatches = [
                    "sf-dump-str",
                    "sf-dump-key",
                    "sf-dump-public",
                    "sf-dump-protected",
                    "sf-dump-private",
                ].map(xpathHasClass).join(' or ');

                var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);

                while (node = xpathResult.iterateNext()) state.nodes.push(node);

                showCurrent(state);
            }, 400);
        });

        Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) {
            addEventListener(btn, 'click', function (e) {
                e.preventDefault();
                -1 !== e.target.className.indexOf('next') ? state.next() : state.previous();
                searchInput.focus();
                collapseAll(root);
                showCurrent(state);
            })
        });

        addEventListener(root, 'keydown', function (e) {
            var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className);
            if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) {
                /* F3 or CMD/CTRL + F */
                if (70 === e.keyCode && document.activeElement === searchInput) {
                   /*
                    * If CMD/CTRL + F is hit while having focus on search input,
                    * the user probably meant to trigger browser search instead.
                    * Let the browser execute its behavior:
                    */
                    return;
                }

                e.preventDefault();
                search.className = search.className.replace(/\bsf-dump-search-hidden\b/, '');
                searchInput.focus();
            } else if (isSearchActive) {
                if (27 === e.keyCode) {
                    /* ESC key */
                    search.className += ' sf-dump-search-hidden';
                    e.preventDefault();
                    resetHighlightedNodes(root);
                    searchInput.value = '';
                } else if (
                    (isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */
                    || 13 === e.keyCode /* Enter */
                    || 114 === e.keyCode /* F3 */
                ) {
                    e.preventDefault();
                    e.shiftKey ? state.previous() : state.next();
                    collapseAll(root);
                    showCurrent(state);
                }
            }
        });
    }

    if (0 >= options.maxStringLength) {
        return;
    }
    try {
        elt = root.querySelectorAll('.sf-dump-str');
        len = elt.length;
        i = 0;
        t = [];

        while (i < len) t.push(elt[i++]);
        len = t.length;

        for (i = 0; i < len; ++i) {
            elt = t[i];
            s = elt.innerText || elt.textContent;
            x = s.length - options.maxStringLength;
            if (0 < x) {
                h = elt.innerHTML;
                elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength);
                elt.className += ' sf-dump-str-collapse';
                elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+
                    '<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>';
            }
        }
    } catch (e) {
    }
};

})(document);
</script><style>
pre.sf-dump {
    display: block;
    white-space: pre;
    padding: 5px;
    overflow: initial !important;
}
pre.sf-dump:after {
   content: "";
   visibility: hidden;
   display: block;
   height: 0;
   clear: both;
}
pre.sf-dump span {
    display: inline;
}
pre.sf-dump a {
    text-decoration: none;
    cursor: pointer;
    border: 0;
    outline: none;
    color: inherit;
}
pre.sf-dump img {
    max-width: 50em;
    max-height: 50em;
    margin: .5em 0 0 0;
    padding: 0;
    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAHUlEQVQY02O8zAABilCaiQEN0EeA8QuUcX9g3QEAAjcC5piyhyEAAAAASUVORK5CYII=) #D3D3D3;
}
pre.sf-dump .sf-dump-ellipsis {
    display: inline-block;
    overflow: visible;
    text-overflow: ellipsis;
    max-width: 5em;
    white-space: nowrap;
    overflow: hidden;
    vertical-align: top;
}
pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis {
    max-width: none;
}
pre.sf-dump code {
    display:inline;
    padding:0;
    background:none;
}
.sf-dump-public.sf-dump-highlight,
.sf-dump-protected.sf-dump-highlight,
.sf-dump-private.sf-dump-highlight,
.sf-dump-str.sf-dump-highlight,
.sf-dump-key.sf-dump-highlight {
    background: rgba(111, 172, 204, 0.3);
    border: 1px solid #7DA0B1;
    border-radius: 3px;
}
.sf-dump-public.sf-dump-highlight-active,
.sf-dump-protected.sf-dump-highlight-active,
.sf-dump-private.sf-dump-highlight-active,
.sf-dump-str.sf-dump-highlight-active,
.sf-dump-key.sf-dump-highlight-active {
    background: rgba(253, 175, 0, 0.4);
    border: 1px solid #ffa500;
    border-radius: 3px;
}
pre.sf-dump .sf-dump-search-hidden {
    display: none !important;
}
pre.sf-dump .sf-dump-search-wrapper {
    font-size: 0;
    white-space: nowrap;
    margin-bottom: 5px;
    display: flex;
    position: -webkit-sticky;
    position: sticky;
    top: 5px;
}
pre.sf-dump .sf-dump-search-wrapper > * {
    vertical-align: top;
    box-sizing: border-box;
    height: 21px;
    font-weight: normal;
    border-radius: 0;
    background: #FFF;
    color: #757575;
    border: 1px solid #BBB;
}
pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input {
    padding: 3px;
    height: 21px;
    font-size: 12px;
    border-right: none;
    border-top-left-radius: 3px;
    border-bottom-left-radius: 3px;
    color: #000;
    min-width: 15px;
    width: 100%;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next,
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous {
    background: #F2F2F2;
    outline: none;
    border-left: none;
    font-size: 0;
    line-height: 0;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next {
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg,
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg {
    pointer-events: none;
    width: 12px;
    height: 12px;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count {
    display: inline-block;
    padding: 0 5px;
    margin: 0;
    border-left: none;
    line-height: 21px;
    font-size: 12px;
}
EOHTML
);

foreach ($this->styles as $class => $style) {
$line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}';
}
$line .= 'pre.sf-dump .sf-dump-ellipsis-note{'.$this->styles['note'].'}';

return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader;
}




public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut)
{
if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) {
$this->dumpKey($cursor);
$this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []);
$this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' <samp class=sf-dump-compact>' : ' <samp class=sf-dump-expanded>';
$this->endValue($cursor);
$this->line .= $this->indentPad;
$this->line .= sprintf('<img src="data:%s;base64,%s" /></samp>', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data']));
$this->endValue($cursor);
} else {
parent::dumpString($cursor, $str, $bin, $cut);
}
}




public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild)
{
if (Cursor::HASH_OBJECT === $type) {
$cursor->attr['depth'] = $cursor->depth;
}
parent::enterHash($cursor, $type, $class, false);

if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) {
$cursor->skipChildren = false;
$eol = ' class=sf-dump-compact>';
} else {
$this->expandNextHash = false;
$eol = ' class=sf-dump-expanded>';
}

if ($hasChild) {
$this->line .= '<samp data-depth='.($cursor->depth + 1);
if ($cursor->refIndex) {
$r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2;
$r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex;

$this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r);
}
$this->line .= $eol;
$this->dumpLine($cursor->depth);
}
}




public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut)
{
$this->dumpEllipsis($cursor, $hasChild, $cut);
if ($hasChild) {
$this->line .= '</samp>';
}
parent::leaveHash($cursor, $type, $class, $hasChild, 0);
}




protected function style(string $style, string $value, array $attr = []): string
{
if ('' === $value) {
return '';
}

$v = esc($value);

if ('ref' === $style) {
if (empty($attr['count'])) {
return sprintf('<a class=sf-dump-ref>%s</a>', $v);
}
$r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1);

return sprintf('<a class=sf-dump-ref href=#%s-ref%s title="%d occurrences">%s</a>', $this->dumpId, $r, 1 + $attr['count'], $v);
}

if ('const' === $style && isset($attr['value'])) {
$style .= sprintf(' title="%s"', esc(\is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value'])));
} elseif ('public' === $style) {
$style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property');
} elseif ('str' === $style && 1 < $attr['length']) {
$style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : '');
} elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) {
$style .= ' title=""';
$attr += [
'ellipsis' => \strlen($value) - $c,
'ellipsis-type' => 'note',
'ellipsis-tail' => 1,
];
} elseif ('protected' === $style) {
$style .= ' title="Protected property"';
} elseif ('meta' === $style && isset($attr['title'])) {
$style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title'])));
} elseif ('private' === $style) {
$style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($this->utf8Encode($attr['class'])));
}
$map = static::$controlCharsMap;

if (isset($attr['ellipsis'])) {
$class = 'sf-dump-ellipsis';
if (isset($attr['ellipsis-type'])) {
$class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']);
}
$label = esc(substr($value, -$attr['ellipsis']));
$style = str_replace(' title="', " title=\"$v\n", $style);
$v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label)));

if (!empty($attr['ellipsis-tail'])) {
$tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail'])));
$v .= sprintf('<span class=%s>%s</span>%s', $class, substr($label, 0, $tail), substr($label, $tail));
} else {
$v .= $label;
}
}

$v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) {
$s = $b = '<span class="sf-dump-default';
$c = $c[$i = 0];
if ($ns = "\r" === $c[$i] || "\n" === $c[$i]) {
$s .= ' sf-dump-ns';
}
$s .= '">';
do {
if (("\r" === $c[$i] || "\n" === $c[$i]) !== $ns) {
$s .= '</span>'.$b;
if ($ns = !$ns) {
$s .= ' sf-dump-ns';
}
$s .= '">';
}

$s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i]));
} while (isset($c[++$i]));

return $s.'</span>';
}, $v).'</span>';

if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) {
$attr['href'] = $href;
}
if (isset($attr['href'])) {
$target = isset($attr['file']) ? '' : ' target="_blank"';
$v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v);
}
if (isset($attr['lang'])) {
$v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v);
}

return $v;
}




protected function dumpLine(int $depth, bool $endOfValue = false)
{
if (-1 === $this->lastDepth) {
$this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line;
}
if ($this->headerIsDumped !== ($this->outputStream ?? $this->lineDumper)) {
$this->line = $this->getDumpHeader().$this->line;
}

if (-1 === $depth) {
$args = ['"'.$this->dumpId.'"'];
if ($this->extraDisplayOptions) {
$args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT);
}

$this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args));
}
$this->lastDepth = $depth;

$this->line = mb_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8');

if (-1 === $depth) {
AbstractDumper::dumpLine(0);
}
AbstractDumper::dumpLine($depth);
}

private function getSourceLink(string $file, int $line)
{
$options = $this->extraDisplayOptions + $this->displayOptions;

if ($fmt = $options['fileLinkFormat']) {
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line);
}

return false;
}
}

function esc(string $str)
{
return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8');
}
<?php










namespace Symfony\Component\VarDumper\Dumper\ContextProvider;






interface ContextProviderInterface
{
public function getContext(): ?array;
}
<?php










namespace Symfony\Component\VarDumper\Dumper\ContextProvider;






final class CliContextProvider implements ContextProviderInterface
{
public function getContext(): ?array
{
if ('cli' !== \PHP_SAPI) {
return null;
}

return [
'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []),
'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']),
];
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper\ContextProvider;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Cloner\VarCloner;






final class RequestContextProvider implements ContextProviderInterface
{
private $requestStack;
private $cloner;

public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
$this->cloner = new VarCloner();
$this->cloner->setMaxItems(0);
$this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
}

public function getContext(): ?array
{
if (null === $request = $this->requestStack->getCurrentRequest()) {
return null;
}

$controller = $request->attributes->get('_controller');

return [
'uri' => $request->getUri(),
'method' => $request->getMethod(),
'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller,
'identifier' => spl_object_hash($request),
];
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper\ContextProvider;

use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\VarDumper;
use Twig\Template;







final class SourceContextProvider implements ContextProviderInterface
{
private int $limit;
private ?string $charset;
private ?string $projectDir;
private $fileLinkFormatter;

public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9)
{
$this->charset = $charset;
$this->projectDir = $projectDir;
$this->fileLinkFormatter = $fileLinkFormatter;
$this->limit = $limit;
}

public function getContext(): ?array
{
$trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit);

$file = $trace[1]['file'];
$line = $trace[1]['line'];
$name = false;
$fileExcerpt = false;

for ($i = 2; $i < $this->limit; ++$i) {
if (isset($trace[$i]['class'], $trace[$i]['function'])
&& 'dump' === $trace[$i]['function']
&& VarDumper::class === $trace[$i]['class']
) {
$file = $trace[$i]['file'] ?? $file;
$line = $trace[$i]['line'] ?? $line;

while (++$i < $this->limit) {
if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && !str_starts_with($trace[$i]['function'], 'call_user_func')) {
$file = $trace[$i]['file'];
$line = $trace[$i]['line'];

break;
} elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) {
$template = $trace[$i]['object'];
$name = $template->getTemplateName();
$src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false);
$info = $template->getDebugInfo();
if (isset($info[$trace[$i - 1]['line']])) {
$line = $info[$trace[$i - 1]['line']];
$file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null;

if ($src) {
$src = explode("\n", $src);
$fileExcerpt = [];

for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) {
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
}

$fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
}
}
break;
}
}
break;
}
}

if (false === $name) {
$name = str_replace('\\', '/', $file);
$name = substr($name, strrpos($name, '/') + 1);
}

$context = ['name' => $name, 'file' => $file, 'line' => $line];
$context['file_excerpt'] = $fileExcerpt;

if (null !== $this->projectDir) {
$context['project_dir'] = $this->projectDir;
if (str_starts_with($file, $this->projectDir)) {
$context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR);
}
}

if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) {
$context['file_link'] = $fileLink;
}

return $context;
}

private function htmlEncode(string $s): string
{
$html = '';

$dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
$dumper->setDumpHeader('');
$dumper->setDumpBoundaries('', '');

$cloner = new VarCloner();
$dumper->dump($cloner->cloneVar($s));

return substr(strip_tags($html), 1, -1);
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper;

use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
use Symfony\Component\VarDumper\Server\Connection;






class ServerDumper implements DataDumperInterface
{
private $connection;
private $wrappedDumper;






public function __construct(string $host, DataDumperInterface $wrappedDumper = null, array $contextProviders = [])
{
$this->connection = new Connection($host, $contextProviders);
$this->wrappedDumper = $wrappedDumper;
}

public function getContextProviders(): array
{
return $this->connection->getContextProviders();
}




public function dump(Data $data)
{
if (!$this->connection->write($data) && $this->wrappedDumper) {
$this->wrappedDumper->dump($data);
}
}
}
<?php










namespace Symfony\Component\VarDumper\Dumper;

use Symfony\Component\VarDumper\Cloner\Data;






interface DataDumperInterface
{
public function dump(Data $data);
}
<?php










namespace Symfony\Component\VarDumper\Dumper;

use Symfony\Component\VarDumper\Cloner\Cursor;
use Symfony\Component\VarDumper\Cloner\Stub;






class CliDumper extends AbstractDumper
{
public static $defaultColors;
public static $defaultOutput = 'php://stdout';

protected $colors;
protected $maxStringWidth = 0;
protected $styles = [

'default' => '0;38;5;208',
'num' => '1;38;5;38',
'const' => '1;38;5;208',
'str' => '1;38;5;113',
'note' => '38;5;38',
'ref' => '38;5;247',
'public' => '',
'protected' => '',
'private' => '',
'meta' => '38;5;170',
'key' => '38;5;113',
'index' => '38;5;38',
];

protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/';
protected static $controlCharsMap = [
"\t" => '\t',
"\n" => '\n',
"\v" => '\v',
"\f" => '\f',
"\r" => '\r',
"\033" => '\e',
];

protected $collapseNextHash = false;
protected $expandNextHash = false;

private array $displayOptions = [
'fileLinkFormat' => null,
];

private bool $handlesHrefGracefully;




public function __construct($output = null, string $charset = null, int $flags = 0)
{
parent::__construct($output, $charset, $flags);

if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) {

$this->setStyles([
'default' => '31',
'num' => '1;34',
'const' => '1;31',
'str' => '1;32',
'note' => '34',
'ref' => '1;30',
'meta' => '35',
'key' => '32',
'index' => '34',
]);
}

$this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l';
}




public function setColors(bool $colors)
{
$this->colors = $colors;
}




public function setMaxStringWidth(int $maxStringWidth)
{
$this->maxStringWidth = $maxStringWidth;
}






public function setStyles(array $styles)
{
$this->styles = $styles + $this->styles;
}






public function setDisplayOptions(array $displayOptions)
{
$this->displayOptions = $displayOptions + $this->displayOptions;
}




public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value)
{
$this->dumpKey($cursor);

$style = 'const';
$attr = $cursor->attr;

switch ($type) {
case 'default':
$style = 'default';
break;

case 'integer':
$style = 'num';

if (isset($this->styles['integer'])) {
$style = 'integer';
}

break;

case 'double':
$style = 'num';

if (isset($this->styles['float'])) {
$style = 'float';
}

switch (true) {
case \INF === $value: $value = 'INF'; break;
case -\INF === $value: $value = '-INF'; break;
case is_nan($value): $value = 'NAN'; break;
default:
$value = (string) $value;
if (!str_contains($value, $this->decimalPoint)) {
$value .= $this->decimalPoint.'0';
}
break;
}
break;

case 'NULL':
$value = 'null';
break;

case 'boolean':
$value = $value ? 'true' : 'false';
break;

default:
$attr += ['value' => $this->utf8Encode($value)];
$value = $this->utf8Encode($type);
break;
}

$this->line .= $this->style($style, $value, $attr);

$this->endValue($cursor);
}




public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut)
{
$this->dumpKey($cursor);
$attr = $cursor->attr;

if ($bin) {
$str = $this->utf8Encode($str);
}
if ('' === $str) {
$this->line .= '""';
$this->endValue($cursor);
} else {
$attr += [
'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
'binary' => $bin,
];
$str = $bin && false !== strpos($str, "\0") ? [$str] : explode("\n", $str);
if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) {
unset($str[1]);
$str[0] .= "\n";
}
$m = \count($str) - 1;
$i = $lineCut = 0;

if (self::DUMP_STRING_LENGTH & $this->flags) {
$this->line .= '('.$attr['length'].') ';
}
if ($bin) {
$this->line .= 'b';
}

if ($m) {
$this->line .= '"""';
$this->dumpLine($cursor->depth);
} else {
$this->line .= '"';
}

foreach ($str as $str) {
if ($i < $m) {
$str .= "\n";
}
if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) {
$str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8');
$lineCut = $len - $this->maxStringWidth;
}
if ($m && 0 < $cursor->depth) {
$this->line .= $this->indentPad;
}
if ('' !== $str) {
$this->line .= $this->style('str', $str, $attr);
}
if ($i++ == $m) {
if ($m) {
if ('' !== $str) {
$this->dumpLine($cursor->depth);
if (0 < $cursor->depth) {
$this->line .= $this->indentPad;
}
}
$this->line .= '"""';
} else {
$this->line .= '"';
}
if ($cut < 0) {
$this->line .= '…';
$lineCut = 0;
} elseif ($cut) {
$lineCut += $cut;
}
}
if ($lineCut) {
$this->line .= '…'.$lineCut;
$lineCut = 0;
}

if ($i > $m) {
$this->endValue($cursor);
} else {
$this->dumpLine($cursor->depth);
}
}
}
}




public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild)
{
if (null === $this->colors) {
$this->colors = $this->supportsColors();
}

$this->dumpKey($cursor);
$attr = $cursor->attr;

if ($this->collapseNextHash) {
$cursor->skipChildren = true;
$this->collapseNextHash = $hasChild = false;
}

$class = $this->utf8Encode($class);
if (Cursor::HASH_OBJECT === $type) {
$prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{';
} elseif (Cursor::HASH_RESOURCE === $type) {
$prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' ');
} else {
$prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '[';
}

if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) {
$prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]);
} elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) {
$prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]);
} elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) {
$prefix = substr($prefix, 0, -1);
}

$this->line .= $prefix;

if ($hasChild) {
$this->dumpLine($cursor->depth);
}
}




public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut)
{
if (empty($cursor->attr['cut_hash'])) {
$this->dumpEllipsis($cursor, $hasChild, $cut);
$this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : ''));
}

$this->endValue($cursor);
}







protected function dumpEllipsis(Cursor $cursor, bool $hasChild, int $cut)
{
if ($cut) {
$this->line .= ' …';
if (0 < $cut) {
$this->line .= $cut;
}
if ($hasChild) {
$this->dumpLine($cursor->depth + 1);
}
}
}




protected function dumpKey(Cursor $cursor)
{
if (null !== $key = $cursor->hashKey) {
if ($cursor->hashKeyIsBinary) {
$key = $this->utf8Encode($key);
}
$attr = ['binary' => $cursor->hashKeyIsBinary];
$bin = $cursor->hashKeyIsBinary ? 'b' : '';
$style = 'key';
switch ($cursor->hashType) {
default:
case Cursor::HASH_INDEXED:
if (self::DUMP_LIGHT_ARRAY & $this->flags) {
break;
}
$style = 'index';

case Cursor::HASH_ASSOC:
if (\is_int($key)) {
$this->line .= $this->style($style, $key).' => ';
} else {
$this->line .= $bin.'"'.$this->style($style, $key).'" => ';
}
break;

case Cursor::HASH_RESOURCE:
$key = "\0~\0".$key;

case Cursor::HASH_OBJECT:
if (!isset($key[0]) || "\0" !== $key[0]) {
$this->line .= '+'.$bin.$this->style('public', $key).': ';
} elseif (0 < strpos($key, "\0", 1)) {
$key = explode("\0", substr($key, 1), 2);

switch ($key[0][0]) {
case '+': 
$attr['dynamic'] = true;
$this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": ';
break 2;
case '~':
$style = 'meta';
if (isset($key[0][1])) {
parse_str(substr($key[0], 1), $attr);
$attr += ['binary' => $cursor->hashKeyIsBinary];
}
break;
case '*':
$style = 'protected';
$bin = '#'.$bin;
break;
default:
$attr['class'] = $key[0];
$style = 'private';
$bin = '-'.$bin;
break;
}

if (isset($attr['collapse'])) {
if ($attr['collapse']) {
$this->collapseNextHash = true;
} else {
$this->expandNextHash = true;
}
}

$this->line .= $bin.$this->style($style, $key[1], $attr).($attr['separator'] ?? ': ');
} else {

$this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": ';
}
break;
}

if ($cursor->hardRefTo) {
$this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' ';
}
}
}








protected function style(string $style, string $value, array $attr = []): string
{
if (null === $this->colors) {
$this->colors = $this->supportsColors();
}

$this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);

if (isset($attr['ellipsis'], $attr['ellipsis-type'])) {
$prefix = substr($value, 0, -$attr['ellipsis']);
if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && str_starts_with($prefix, $_SERVER[$pwd])) {
$prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd]));
}
if (!empty($attr['ellipsis-tail'])) {
$prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']);
$value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']);
} else {
$value = substr($value, -$attr['ellipsis']);
}

$value = $this->style('default', $prefix).$this->style($style, $value);

goto href;
}

$map = static::$controlCharsMap;
$startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : '';
$endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : '';
$value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) {
$s = $startCchr;
$c = $c[$i = 0];
do {
$s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i]));
} while (isset($c[++$i]));

return $s.$endCchr;
}, $value, -1, $cchrCount);

if ($this->colors) {
if ($cchrCount && "\033" === $value[0]) {
$value = substr($value, \strlen($startCchr));
} else {
$value = "\033[{$this->styles[$style]}m".$value;
}
if ($cchrCount && str_ends_with($value, $endCchr)) {
$value = substr($value, 0, -\strlen($endCchr));
} else {
$value .= "\033[{$this->styles['default']}m";
}
}

href:
if ($this->colors && $this->handlesHrefGracefully) {
if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) {
if ('note' === $style) {
$value .= "\033]8;;{$href}\033\\^\033]8;;\033\\";
} else {
$attr['href'] = $href;
}
}
if (isset($attr['href'])) {
$value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
}
} elseif ($attr['if_links'] ?? false) {
return '';
}

return $value;
}

protected function supportsColors(): bool
{
if ($this->outputStream !== static::$defaultOutput) {
return $this->hasColorSupport($this->outputStream);
}
if (null !== static::$defaultColors) {
return static::$defaultColors;
}
if (isset($_SERVER['argv'][1])) {
$colors = $_SERVER['argv'];
$i = \count($colors);
while (--$i > 0) {
if (isset($colors[$i][5])) {
switch ($colors[$i]) {
case '--ansi':
case '--color':
case '--color=yes':
case '--color=force':
case '--color=always':
case '--colors=always':
return static::$defaultColors = true;

case '--no-ansi':
case '--color=no':
case '--color=none':
case '--color=never':
case '--colors=never':
return static::$defaultColors = false;
}
}
}
}

$h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null];
$h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'w') : $this->outputStream;

return static::$defaultColors = $this->hasColorSupport($h);
}




protected function dumpLine(int $depth, bool $endOfValue = false)
{
if ($this->colors) {
$this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line);
}
parent::dumpLine($depth);
}

protected function endValue(Cursor $cursor)
{
if (-1 === $cursor->hashType) {
return;
}

if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
$this->line .= ',';
} elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) {
$this->line .= ',';
}
}

$this->dumpLine($cursor->depth, true);
}







private function hasColorSupport(mixed $stream): bool
{
if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
return false;
}


if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
return false;
}

if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}

if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}

return stream_isatty($stream);
}








private function isWindowsTrueColor(): bool
{
$result = 183 <= getenv('ANSICON_VER')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM')
|| 'Hyper' === getenv('TERM_PROGRAM');

if (!$result) {
$version = sprintf(
'%s.%s.%s',
PHP_WINDOWS_VERSION_MAJOR,
PHP_WINDOWS_VERSION_MINOR,
PHP_WINDOWS_VERSION_BUILD
);
$result = $version >= '10.0.15063';
}

return $result;
}

private function getSourceLink(string $file, int $line)
{
if ($fmt = $this->displayOptions['fileLinkFormat']) {
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line);
}

return false;
}
}
<?php










namespace Symfony\Component\VarDumper;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider;
use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider;
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Dumper\ServerDumper;


require_once __DIR__.'/Resources/functions/dump.php';




class VarDumper
{



private static $handler;

public static function dump(mixed $var)
{
if (null === self::$handler) {
self::register();
}

return (self::$handler)($var);
}

public static function setHandler(callable $callable = null): ?callable
{
$prevHandler = self::$handler;


if (isset($_SERVER['VAR_DUMPER_FORMAT'])) {
return $prevHandler;
}

self::$handler = $callable;

return $prevHandler;
}

private static function register(): void
{
$cloner = new VarCloner();
$cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);

$format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null;
switch (true) {
case 'html' === $format:
$dumper = new HtmlDumper();
break;
case 'cli' === $format:
$dumper = new CliDumper();
break;
case 'server' === $format:
case $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME):
$host = 'server' === $format ? $_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912' : $format;
$dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper();
$dumper = new ServerDumper($host, $dumper, self::getDefaultContextProviders());
break;
default:
$dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper();
}

if (!$dumper instanceof ServerDumper) {
$dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]);
}

self::$handler = function ($var) use ($cloner, $dumper) {
$dumper->dump($cloner->cloneVar($var));
};
}

private static function getDefaultContextProviders(): array
{
$contextProviders = [];

if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && class_exists(Request::class)) {
$requestStack = new RequestStack();
$requestStack->push(Request::createFromGlobals());
$contextProviders['request'] = new RequestContextProvider($requestStack);
}

$fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null;

return $contextProviders + [
'cli' => new CliContextProvider(),
'source' => new SourceContextProvider(null, null, $fileLinkFormatter),
];
}
}
<?php










namespace Symfony\Component\VarDumper\Cloner;






class Stub
{
public const TYPE_REF = 1;
public const TYPE_STRING = 2;
public const TYPE_ARRAY = 3;
public const TYPE_OBJECT = 4;
public const TYPE_RESOURCE = 5;

public const STRING_BINARY = 1;
public const STRING_UTF8 = 2;

public const ARRAY_ASSOC = 1;
public const ARRAY_INDEXED = 2;

public $type = self::TYPE_REF;
public $class = '';
public $value;
public $cut = 0;
public $handle = 0;
public $refCount = 0;
public $position = 0;
public $attr = [];

private static array $defaultProperties = [];




public function __sleep(): array
{
$properties = [];

if (!isset(self::$defaultProperties[$c = static::class])) {
self::$defaultProperties[$c] = get_class_vars($c);

foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) {
unset(self::$defaultProperties[$c][$k]);
}
}

foreach (self::$defaultProperties[$c] as $k => $v) {
if ($this->$k !== $v) {
$properties[] = $k;
}
}

return $properties;
}
}
<?php










namespace Symfony\Component\VarDumper\Cloner;




class VarCloner extends AbstractCloner
{
private static string $gid;
private static array $arrayCache = [];




protected function doClone(mixed $var): array
{
$len = 1; 
$pos = 0; 
$refsCounter = 0; 
$queue = [[$var]]; 
$hardRefs = []; 
$objRefs = []; 
$objects = []; 
$resRefs = []; 
$values = []; 
$maxItems = $this->maxItems;
$maxString = $this->maxString;
$minDepth = $this->minDepth;
$currentDepth = 0; 
$currentDepthFinalIndex = 0; 
$minimumDepthReached = 0 === $minDepth; 
$cookie = (object) []; 
$a = null; 
$stub = null; 


$gid = self::$gid ??= md5(random_bytes(6)); 
$arrayStub = new Stub();
$arrayStub->type = Stub::TYPE_ARRAY;
$fromObjCast = false;

for ($i = 0; $i < $len; ++$i) {

if ($i > $currentDepthFinalIndex) {
++$currentDepth;
$currentDepthFinalIndex = $len - 1;
if ($currentDepth >= $minDepth) {
$minimumDepthReached = true;
}
}

$refs = $vals = $queue[$i];
foreach ($vals as $k => $v) {


$zvalRef = ($r = \ReflectionReference::fromArrayElement($vals, $k)) ? $r->getId() : null;

if ($zvalRef) {
$vals[$k] = &$stub; 
unset($stub); 
if (null !== $vals[$k] = $hardRefs[$zvalRef] ?? null) {
$v = $vals[$k];
if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
++$v->value->refCount;
}
++$v->refCount;
continue;
}
$vals[$k] = new Stub();
$vals[$k]->value = $v;
$vals[$k]->handle = ++$refsCounter;
$hardRefs[$zvalRef] = $vals[$k];
}


switch (true) {
case null === $v:
case \is_bool($v):
case \is_int($v):
case \is_float($v):
continue 2;
case \is_string($v):
if ('' === $v) {
continue 2;
}
if (!preg_match('//u', $v)) {
$stub = new Stub();
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_BINARY;
if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) {
$stub->cut = $cut;
$stub->value = substr($v, 0, -$cut);
} else {
$stub->value = $v;
}
} elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) {
$stub = new Stub();
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_UTF8;
$stub->cut = $cut;
$stub->value = mb_substr($v, 0, $maxString, 'UTF-8');
} else {
continue 2;
}
$a = null;
break;

case \is_array($v):
if (!$v) {
continue 2;
}
$stub = $arrayStub;

if (\PHP_VERSION_ID >= 80100) {
$stub->class = array_is_list($v) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC;
$a = $v;
break;
}

$stub->class = Stub::ARRAY_INDEXED;

$j = -1;
foreach ($v as $gk => $gv) {
if ($gk !== ++$j) {
$stub->class = Stub::ARRAY_ASSOC;
$a = $v;
$a[$gid] = true;
break;
}
}



if (isset($v[$gid])) {
unset($v[$gid]);
$a = [];
foreach ($v as $gk => &$gv) {
if ($v === $gv && !isset($hardRefs[\ReflectionReference::fromArrayElement($v, $gk)->getId()])) {
unset($v);
$v = new Stub();
$v->value = [$v->cut = \count($gv), Stub::TYPE_ARRAY => 0];
$v->handle = -1;
$gv = &$a[$gk];
$hardRefs[\ReflectionReference::fromArrayElement($a, $gk)->getId()] = &$gv;
$gv = $v;
}

$a[$gk] = &$gv;
}
unset($gv);
} else {
$a = $v;
}
break;

case \is_object($v):
if (empty($objRefs[$h = spl_object_id($v)])) {
$stub = new Stub();
$stub->type = Stub::TYPE_OBJECT;
$stub->class = \get_class($v);
$stub->value = $v;
$stub->handle = $h;
$a = $this->castObject($stub, 0 < $i);
if ($v !== $stub->value) {
if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) {
break;
}
$stub->handle = $h = spl_object_id($stub->value);
}
$stub->value = null;
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
$stub->cut = \count($a);
$a = null;
}
}
if (empty($objRefs[$h])) {
$objRefs[$h] = $stub;
$objects[] = $v;
} else {
$stub = $objRefs[$h];
++$stub->refCount;
$a = null;
}
break;

default: 
if (empty($resRefs[$h = (int) $v])) {
$stub = new Stub();
$stub->type = Stub::TYPE_RESOURCE;
if ('Unknown' === $stub->class = @get_resource_type($v)) {
$stub->class = 'Closed';
}
$stub->value = $v;
$stub->handle = $h;
$a = $this->castResource($stub, 0 < $i);
$stub->value = null;
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
$stub->cut = \count($a);
$a = null;
}
}
if (empty($resRefs[$h])) {
$resRefs[$h] = $stub;
} else {
$stub = $resRefs[$h];
++$stub->refCount;
$a = null;
}
break;
}

if ($a) {
if (!$minimumDepthReached || 0 > $maxItems) {
$queue[$len] = $a;
$stub->position = $len++;
} elseif ($pos < $maxItems) {
if ($maxItems < $pos += \count($a)) {
$a = \array_slice($a, 0, $maxItems - $pos, true);
if ($stub->cut >= 0) {
$stub->cut += $pos - $maxItems;
}
}
$queue[$len] = $a;
$stub->position = $len++;
} elseif ($stub->cut >= 0) {
$stub->cut += \count($a);
$stub->position = 0;
}
}

if ($arrayStub === $stub) {
if ($arrayStub->cut) {
$stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position];
$arrayStub->cut = 0;
} elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) {
$stub = self::$arrayCache[$arrayStub->class][$arrayStub->position];
} else {
self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position];
}
}

if (!$zvalRef) {
$vals[$k] = $stub;
} else {
$hardRefs[$zvalRef]->value = $stub;
}
}

if ($fromObjCast) {
$fromObjCast = false;
$refs = $vals;
$vals = [];
$j = -1;
foreach ($queue[$i] as $k => $v) {
foreach ([$k => true] as $gk => $gv) {
}
if ($gk !== $k) {
$vals = (object) $vals;
$vals->{$k} = $refs[++$j];
$vals = (array) $vals;
} else {
$vals[$k] = $refs[++$j];
}
}
}

$queue[$i] = $vals;
}

foreach ($values as $h => $v) {
$hardRefs[$h] = $v;
}

return $queue;
}
}
<?php










namespace Symfony\Component\VarDumper\Cloner;






class Cursor
{
public const HASH_INDEXED = Stub::ARRAY_INDEXED;
public const HASH_ASSOC = Stub::ARRAY_ASSOC;
public const HASH_OBJECT = Stub::TYPE_OBJECT;
public const HASH_RESOURCE = Stub::TYPE_RESOURCE;

public $depth = 0;
public $refIndex = 0;
public $softRefTo = 0;
public $softRefCount = 0;
public $softRefHandle = 0;
public $hardRefTo = 0;
public $hardRefCount = 0;
public $hardRefHandle = 0;
public $hashType;
public $hashKey;
public $hashKeyIsBinary;
public $hashIndex = 0;
public $hashLength = 0;
public $hashCut = 0;
public $stop = false;
public $attr = [];
public $skipChildren = false;
}
<?php










namespace Symfony\Component\VarDumper\Cloner;






interface DumperInterface
{



public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value);








public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut);








public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild);









public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut);
}
<?php










namespace Symfony\Component\VarDumper\Cloner;




interface ClonerInterface
{



public function cloneVar(mixed $var): Data;
}
<?php










namespace Symfony\Component\VarDumper\Cloner;

use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;




class Data implements \ArrayAccess, \Countable, \IteratorAggregate
{
private array $data;
private int $position = 0;
private int|string $key = 0;
private int $maxDepth = 20;
private int $maxItemsPerDepth = -1;
private int $useRefHandles = -1;
private array $context = [];




public function __construct(array $data)
{
$this->data = $data;
}

public function getType(): ?string
{
$item = $this->data[$this->position][$this->key];

if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
$item = $item->value;
}
if (!$item instanceof Stub) {
return \gettype($item);
}
if (Stub::TYPE_STRING === $item->type) {
return 'string';
}
if (Stub::TYPE_ARRAY === $item->type) {
return 'array';
}
if (Stub::TYPE_OBJECT === $item->type) {
return $item->class;
}
if (Stub::TYPE_RESOURCE === $item->type) {
return $item->class.' resource';
}

return null;
}








public function getValue(array|bool $recursive = false): string|int|float|bool|array|null
{
$item = $this->data[$this->position][$this->key];

if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
$item = $item->value;
}
if (!($item = $this->getStub($item)) instanceof Stub) {
return $item;
}
if (Stub::TYPE_STRING === $item->type) {
return $item->value;
}

$children = $item->position ? $this->data[$item->position] : [];

foreach ($children as $k => $v) {
if ($recursive && !($v = $this->getStub($v)) instanceof Stub) {
continue;
}
$children[$k] = clone $this;
$children[$k]->key = $k;
$children[$k]->position = $item->position;

if ($recursive) {
if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) {
$recursive = (array) $recursive;
if (isset($recursive[$v->position])) {
continue;
}
$recursive[$v->position] = true;
}
$children[$k] = $children[$k]->getValue($recursive);
}
}

return $children;
}

public function count(): int
{
return \count($this->getValue());
}

public function getIterator(): \Traversable
{
if (!\is_array($value = $this->getValue())) {
throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, get_debug_type($value)));
}

yield from $value;
}

public function __get(string $key)
{
if (null !== $data = $this->seek($key)) {
$item = $this->getStub($data->data[$data->position][$data->key]);

return $item instanceof Stub || [] === $item ? $data : $item;
}

return null;
}

public function __isset(string $key): bool
{
return null !== $this->seek($key);
}

public function offsetExists(mixed $key): bool
{
return $this->__isset($key);
}

public function offsetGet(mixed $key): mixed
{
return $this->__get($key);
}

public function offsetSet(mixed $key, mixed $value): void
{
throw new \BadMethodCallException(self::class.' objects are immutable.');
}

public function offsetUnset(mixed $key): void
{
throw new \BadMethodCallException(self::class.' objects are immutable.');
}

public function __toString(): string
{
$value = $this->getValue();

if (!\is_array($value)) {
return (string) $value;
}

return sprintf('%s (count=%d)', $this->getType(), \count($value));
}




public function withMaxDepth(int $maxDepth): static
{
$data = clone $this;
$data->maxDepth = $maxDepth;

return $data;
}




public function withMaxItemsPerDepth(int $maxItemsPerDepth): static
{
$data = clone $this;
$data->maxItemsPerDepth = $maxItemsPerDepth;

return $data;
}






public function withRefHandles(bool $useRefHandles): static
{
$data = clone $this;
$data->useRefHandles = $useRefHandles ? -1 : 0;

return $data;
}

public function withContext(array $context): static
{
$data = clone $this;
$data->context = $context;

return $data;
}




public function seek(string|int $key): ?static
{
$item = $this->data[$this->position][$this->key];

if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
$item = $item->value;
}
if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) {
return null;
}
$keys = [$key];

switch ($item->type) {
case Stub::TYPE_OBJECT:
$keys[] = Caster::PREFIX_DYNAMIC.$key;
$keys[] = Caster::PREFIX_PROTECTED.$key;
$keys[] = Caster::PREFIX_VIRTUAL.$key;
$keys[] = "\0$item->class\0$key";

case Stub::TYPE_ARRAY:
case Stub::TYPE_RESOURCE:
break;
default:
return null;
}

$data = null;
$children = $this->data[$item->position];

foreach ($keys as $key) {
if (isset($children[$key]) || \array_key_exists($key, $children)) {
$data = clone $this;
$data->key = $key;
$data->position = $item->position;
break;
}
}

return $data;
}




public function dump(DumperInterface $dumper)
{
$refs = [0];
$cursor = new Cursor();

if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) {
$cursor->attr['if_links'] = true;
$cursor->hashType = -1;
$dumper->dumpScalar($cursor, 'default', '^');
$cursor->attr = ['if_links' => true];
$dumper->dumpScalar($cursor, 'default', ' ');
$cursor->hashType = 0;
}

$this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]);
}






private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, mixed $item)
{
$cursor->refIndex = 0;
$cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0;
$cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0;
$firstSeen = true;

if (!$item instanceof Stub) {
$cursor->attr = [];
$type = \gettype($item);
if ($item && 'array' === $type) {
$item = $this->getStub($item);
}
} elseif (Stub::TYPE_REF === $item->type) {
if ($item->handle) {
if (!isset($refs[$r = $item->handle - (\PHP_INT_MAX >> 1)])) {
$cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0];
} else {
$firstSeen = false;
}
$cursor->hardRefTo = $refs[$r];
$cursor->hardRefHandle = $this->useRefHandles & $item->handle;
$cursor->hardRefCount = 0 < $item->handle ? $item->refCount : 0;
}
$cursor->attr = $item->attr;
$type = $item->class ?: \gettype($item->value);
$item = $this->getStub($item->value);
}
if ($item instanceof Stub) {
if ($item->refCount) {
if (!isset($refs[$r = $item->handle])) {
$cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0];
} else {
$firstSeen = false;
}
$cursor->softRefTo = $refs[$r];
}
$cursor->softRefHandle = $this->useRefHandles & $item->handle;
$cursor->softRefCount = $item->refCount;
$cursor->attr = $item->attr;
$cut = $item->cut;

if ($item->position && $firstSeen) {
$children = $this->data[$item->position];

if ($cursor->stop) {
if ($cut >= 0) {
$cut += \count($children);
}
$children = [];
}
} else {
$children = [];
}
switch ($item->type) {
case Stub::TYPE_STRING:
$dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut);
break;

case Stub::TYPE_ARRAY:
$item = clone $item;
$item->type = $item->class;
$item->class = $item->value;

case Stub::TYPE_OBJECT:
case Stub::TYPE_RESOURCE:
$withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
$dumper->enterHash($cursor, $item->type, $item->class, $withChildren);
if ($withChildren) {
if ($cursor->skipChildren) {
$withChildren = false;
$cut = -1;
} else {
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class);
}
} elseif ($children && 0 <= $cut) {
$cut += \count($children);
}
$cursor->skipChildren = false;
$dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut);
break;

default:
throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".', $item->type));
}
} elseif ('array' === $type) {
$dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false);
$dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0);
} elseif ('string' === $type) {
$dumper->dumpString($cursor, $item, false, 0);
} else {
$dumper->dumpScalar($cursor, $type, $item);
}
}






private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int
{
$cursor = clone $parentCursor;
++$cursor->depth;
$cursor->hashType = $hashType;
$cursor->hashIndex = 0;
$cursor->hashLength = \count($children);
$cursor->hashCut = $hashCut;
foreach ($children as $key => $child) {
$cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key);
$cursor->hashKey = $dumpKeys ? $key : null;
$this->dumpItem($dumper, $cursor, $refs, $child);
if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
$parentCursor->stop = true;

return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut;
}
}

return $hashCut;
}

private function getStub(mixed $item)
{
if (!$item || !\is_array($item)) {
return $item;
}

$stub = new Stub();
$stub->type = Stub::TYPE_ARRAY;
foreach ($item as $stub->class => $stub->position) {
}
if (isset($item[0])) {
$stub->cut = $item[0];
}
$stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0);

return $stub;
}
}
<?php










namespace Symfony\Component\VarDumper\Cloner;

use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Exception\ThrowingCasterException;






abstract class AbstractCloner implements ClonerInterface
{
public static $defaultCasters = [
'__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'],

'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'],
'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'],
'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'],
'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'],

'Fiber' => ['Symfony\Component\VarDumper\Caster\FiberCaster', 'castFiber'],

'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'],
'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'],
'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'],
'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'],
'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'],
'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'],
'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'],
'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'],
'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'],
'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'],
'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'],
'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'],
'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'],
'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'],

'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'],
'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'],
'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'],
'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],

'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'],
'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'],
'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'],
'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'],
'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'],
'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'],
'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'],
'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'],
'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'],
'DOMTypeinfo' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'],
'DOMDomError' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'],
'DOMLocator' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'],
'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'],
'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'],
'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'],
'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'],
'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'],

'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'],

'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'],
'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'],
'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'],
'Symfony\Bridge\Monolog\Logger' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Symfony\Component\HttpClient\AmpHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
'Symfony\Component\HttpClient\Response\AmpResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'],
'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'],
'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'],
'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'],
'Symfony\Component\Uid\Ulid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUlid'],
'Symfony\Component\Uid\Uuid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUuid'],
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'],
'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'],
'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'],
'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'],

'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'],

'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'],

'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'],
'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],

'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'],
'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'],

'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'],
'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'],
'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'],
'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'],
'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'],

'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'],
'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'],
'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'],
'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'],
'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'],
'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'],
'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'],
'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'],
'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'],
'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'],

'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'],
'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'],
'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'],

'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'],
'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'],
'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'],
'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'],

'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'],

'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'],
'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'],
'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'],
'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'],
'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'],

'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'],

'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'],
'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'],
'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'],
'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'],

'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'],

'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'],

':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],
':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],

'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'],
':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'],

':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'],
':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'],
':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'],
':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'],
':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'],

'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'],
':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'],

':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'],
':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'],

'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'],
':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'],

'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'],
'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'],
'RdKafka\KafkaConsumer' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castKafkaConsumer'],
'RdKafka\Metadata\Broker' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castBrokerMetadata'],
'RdKafka\Metadata\Collection' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castCollectionMetadata'],
'RdKafka\Metadata\Partition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castPartitionMetadata'],
'RdKafka\Metadata\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicMetadata'],
'RdKafka\Message' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castMessage'],
'RdKafka\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopic'],
'RdKafka\TopicPartition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicPartition'],
'RdKafka\TopicConf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicConf'],
];

protected $maxItems = 2500;
protected $maxString = -1;
protected $minDepth = 1;




private array $casters = [];




private $prevErrorHandler;

private array $classInfo = [];
private int $filter = 0;






public function __construct(array $casters = null)
{
if (null === $casters) {
$casters = static::$defaultCasters;
}
$this->addCasters($casters);
}











public function addCasters(array $casters)
{
foreach ($casters as $type => $callback) {
$this->casters[$type][] = $callback;
}
}




public function setMaxItems(int $maxItems)
{
$this->maxItems = $maxItems;
}




public function setMaxString(int $maxString)
{
$this->maxString = $maxString;
}





public function setMinDepth(int $minDepth)
{
$this->minDepth = $minDepth;
}






public function cloneVar(mixed $var, int $filter = 0): Data
{
$this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) {
if (\E_RECOVERABLE_ERROR === $type || \E_USER_ERROR === $type) {

throw new \ErrorException($msg, 0, $type, $file, $line);
}

if ($this->prevErrorHandler) {
return ($this->prevErrorHandler)($type, $msg, $file, $line, $context);
}

return false;
});
$this->filter = $filter;

if ($gc = gc_enabled()) {
gc_disable();
}
try {
return new Data($this->doClone($var));
} finally {
if ($gc) {
gc_enable();
}
restore_error_handler();
$this->prevErrorHandler = null;
}
}




abstract protected function doClone(mixed $var): array;






protected function castObject(Stub $stub, bool $isNested): array
{
$obj = $stub->value;
$class = $stub->class;

if (str_contains($class, "@anonymous\0")) {
$stub->class = get_debug_type($obj);
}
if (isset($this->classInfo[$class])) {
[$i, $parents, $hasDebugInfo, $fileInfo] = $this->classInfo[$class];
} else {
$i = 2;
$parents = [$class];
$hasDebugInfo = method_exists($class, '__debugInfo');

foreach (class_parents($class) as $p) {
$parents[] = $p;
++$i;
}
foreach (class_implements($class) as $p) {
$parents[] = $p;
++$i;
}
$parents[] = '*';

$r = new \ReflectionClass($class);
$fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [
'file' => $r->getFileName(),
'line' => $r->getStartLine(),
];

$this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo];
}

$stub->attr += $fileInfo;
$a = Caster::castObject($obj, $class, $hasDebugInfo, $stub->class);

try {
while ($i--) {
if (!empty($this->casters[$p = $parents[$i]])) {
foreach ($this->casters[$p] as $callback) {
$a = $callback($obj, $a, $stub, $isNested, $this->filter);
}
}
}
} catch (\Exception $e) {
$a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a;
}

return $a;
}






protected function castResource(Stub $stub, bool $isNested): array
{
$a = [];
$res = $stub->value;
$type = $stub->class;

try {
if (!empty($this->casters[':'.$type])) {
foreach ($this->casters[':'.$type] as $callback) {
$a = $callback($res, $a, $stub, $isNested, $this->filter);
}
}
} catch (\Exception $e) {
$a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a;
}

return $a;
}
}
<?php










namespace Symfony\Component\VarDumper\Command\Descriptor;

use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\CliDumper;








class CliDescriptor implements DumpDescriptorInterface
{
private $dumper;
private mixed $lastIdentifier = null;

public function __construct(CliDumper $dumper)
{
$this->dumper = $dumper;
}

public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
{
$io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output);
$this->dumper->setColors($output->isDecorated());

$rows = [['date', date('r', (int) $context['timestamp'])]];
$lastIdentifier = $this->lastIdentifier;
$this->lastIdentifier = $clientId;

$section = "Received from client #$clientId";
if (isset($context['request'])) {
$request = $context['request'];
$this->lastIdentifier = $request['identifier'];
$section = sprintf('%s %s', $request['method'], $request['uri']);
if ($controller = $request['controller']) {
$rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")];
}
} elseif (isset($context['cli'])) {
$this->lastIdentifier = $context['cli']['identifier'];
$section = '$ '.$context['cli']['command_line'];
}

if ($this->lastIdentifier !== $lastIdentifier) {
$io->section($section);
}

if (isset($context['source'])) {
$source = $context['source'];
$sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']);
if ($fileLink = $source['file_link'] ?? null) {
$sourceInfo = sprintf('<href=%s>%s</>', $fileLink, $sourceInfo);
}
$rows[] = ['source', $sourceInfo];
$file = $source['file_relative'] ?? $source['file'];
$rows[] = ['file', $file];
}

$io->table([], $rows);

$this->dumper->dump($data);
$io->newLine();
}
}
<?php










namespace Symfony\Component\VarDumper\Command\Descriptor;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;








class HtmlDescriptor implements DumpDescriptorInterface
{
private $dumper;
private bool $initialized = false;

public function __construct(HtmlDumper $dumper)
{
$this->dumper = $dumper;
}

public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
{
if (!$this->initialized) {
$styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css');
$scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js');
$output->writeln("<style>$styles</style><script>$scripts</script>");
$this->initialized = true;
}

$title = '-';
if (isset($context['request'])) {
$request = $context['request'];
$controller = "<span class='dumped-tag'>{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}</span>";
$title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri);
$dedupIdentifier = $request['identifier'];
} elseif (isset($context['cli'])) {
$title = '<code>$ </code>'.$context['cli']['command_line'];
$dedupIdentifier = $context['cli']['identifier'];
} else {
$dedupIdentifier = uniqid('', true);
}

$sourceDescription = '';
if (isset($context['source'])) {
$source = $context['source'];
$projectDir = $source['project_dir'] ?? null;
$sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']);
if (isset($source['file_link'])) {
$sourceDescription = sprintf('<a href="%s">%s</a>', $source['file_link'], $sourceDescription);
}
}

$isoDate = $this->extractDate($context, 'c');
$tags = array_filter([
'controller' => $controller ?? null,
'project dir' => $projectDir ?? null,
]);

$output->writeln(<<<HTML
<article data-dedup-id="$dedupIdentifier">
    <header>
        <div class="row">
            <h2 class="col">$title</h2>
            <time class="col text-small" title="$isoDate" datetime="$isoDate">
                {$this->extractDate($context)}
            </time>
        </div>
        {$this->renderTags($tags)}
    </header>
    <section class="body">
        <p class="text-small">
            $sourceDescription
        </p>
        {$this->dumper->dump($data, true)}
    </section>
</article>
HTML
);
}

private function extractDate(array $context, string $format = 'r'): string
{
return date($format, (int) $context['timestamp']);
}

private function renderTags(array $tags): string
{
if (!$tags) {
return '';
}

$renderedTags = '';
foreach ($tags as $key => $value) {
$renderedTags .= sprintf('<li><span class="badge">%s</span>%s</li>', $key, $value);
}

return <<<HTML
<div class="row">
    <ul class="tags">
        $renderedTags
    </ul>
</div>
HTML;
}
}
<?php










namespace Symfony\Component\VarDumper\Command\Descriptor;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\Data;




interface DumpDescriptorInterface
{
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void;
}
<?php










namespace Symfony\Component\VarDumper\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor;
use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface;
use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Server\DumpServer;








#[AsCommand(name: 'server:dump', description: 'Start a dump server that collects and displays dumps in a single place')]
class ServerDumpCommand extends Command
{
private $server;


private array $descriptors;

public function __construct(DumpServer $server, array $descriptors = [])
{
$this->server = $server;
$this->descriptors = $descriptors + [
'cli' => new CliDescriptor(new CliDumper()),
'html' => new HtmlDescriptor(new HtmlDumper()),
];

parent::__construct();
}

protected function configure()
{
$this
->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', implode(', ', $this->getAvailableFormats())), 'cli')
->setHelp(<<<'EOF'
<info>%command.name%</info> starts a dump server that collects and displays
dumps in a single place for debugging you application:

  <info>php %command.full_name%</info>

You can consult dumped data in HTML format in your browser by providing the <comment>--format=html</comment> option
and redirecting the output to a file:

  <info>php %command.full_name% --format="html" > dump.html</info>

EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$format = $input->getOption('format');

if (!$descriptor = $this->descriptors[$format] ?? null) {
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format));
}

$errorIo = $io->getErrorStyle();
$errorIo->title('Symfony Var Dumper Server');

$this->server->start();

$errorIo->success(sprintf('Server listening on %s', $this->server->getHost()));
$errorIo->comment('Quit the server with CONTROL-C.');

$this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) {
$descriptor->describe($io, $data, $context, $clientId);
});

return 0;
}

public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues($this->getAvailableFormats());
}
}

private function getAvailableFormats(): array
{
return array_keys($this->descriptors);
}
}
<?php










namespace Symfony\Component\VarDumper\Exception;




class ThrowingCasterException extends \Exception
{



public function __construct(\Throwable $prev)
{
parent::__construct('Unexpected '.\get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev);
}
}
<?php










use Symfony\Polyfill\Ctype as p;

if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}

if (!function_exists('ctype_alnum')) {
function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
}
if (!function_exists('ctype_cntrl')) {
function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
}
if (!function_exists('ctype_digit')) {
function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
}
if (!function_exists('ctype_graph')) {
function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
}
if (!function_exists('ctype_lower')) {
function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
}
if (!function_exists('ctype_print')) {
function ctype_print($text) { return p\Ctype::ctype_print($text); }
}
if (!function_exists('ctype_punct')) {
function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
}
if (!function_exists('ctype_space')) {
function ctype_space($text) { return p\Ctype::ctype_space($text); }
}
if (!function_exists('ctype_upper')) {
function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
}
if (!function_exists('ctype_xdigit')) {
function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
}
<?php










use Symfony\Polyfill\Ctype as p;

if (!function_exists('ctype_alnum')) {
function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); }
}
if (!function_exists('ctype_cntrl')) {
function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); }
}
if (!function_exists('ctype_digit')) {
function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); }
}
if (!function_exists('ctype_graph')) {
function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); }
}
if (!function_exists('ctype_lower')) {
function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); }
}
if (!function_exists('ctype_print')) {
function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); }
}
if (!function_exists('ctype_punct')) {
function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); }
}
if (!function_exists('ctype_space')) {
function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); }
}
if (!function_exists('ctype_upper')) {
function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); }
}
if (!function_exists('ctype_xdigit')) {
function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); }
}
<?php










namespace Symfony\Polyfill\Ctype;








final class Ctype
{









public static function ctype_alnum($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
}










public static function ctype_alpha($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
}










public static function ctype_cntrl($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
}










public static function ctype_digit($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
}










public static function ctype_graph($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
}










public static function ctype_lower($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
}










public static function ctype_print($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
}










public static function ctype_punct($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
}










public static function ctype_space($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
}










public static function ctype_upper($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
}










public static function ctype_xdigit($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);

return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
}














private static function convert_int_to_char_for_ctype($int, $function)
{
if (!\is_int($int)) {
return $int;
}

if ($int < -128 || $int > 255) {
return (string) $int;
}

if (\PHP_VERSION_ID >= 80100) {
@trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED);
}

if ($int < 0) {
$int += 256;
}

return \chr($int);
}
}
<?php










namespace Symfony\Contracts\Service\Test;

use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceLocatorTrait;

abstract class ServiceLocatorTest extends TestCase
{
protected function getServiceLocator(array $factories): ContainerInterface
{
return new class($factories) implements ContainerInterface {
use ServiceLocatorTrait;
};
}

public function testHas()
{
$locator = $this->getServiceLocator([
'foo' => function () { return 'bar'; },
'bar' => function () { return 'baz'; },
function () { return 'dummy'; },
]);

$this->assertTrue($locator->has('foo'));
$this->assertTrue($locator->has('bar'));
$this->assertFalse($locator->has('dummy'));
}

public function testGet()
{
$locator = $this->getServiceLocator([
'foo' => function () { return 'bar'; },
'bar' => function () { return 'baz'; },
]);

$this->assertSame('bar', $locator->get('foo'));
$this->assertSame('baz', $locator->get('bar'));
}

public function testGetDoesNotMemoize()
{
$i = 0;
$locator = $this->getServiceLocator([
'foo' => function () use (&$i) {
++$i;

return 'bar';
},
]);

$this->assertSame('bar', $locator->get('foo'));
$this->assertSame('bar', $locator->get('foo'));
$this->assertSame(2, $i);
}

public function testThrowsOnUndefinedInternalService()
{
if (!$this->getExpectedException()) {
$this->expectException(\Psr\Container\NotFoundExceptionInterface::class);
$this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.');
}
$locator = $this->getServiceLocator([
'foo' => function () use (&$locator) { return $locator->get('bar'); },
]);

$locator->get('foo');
}

public function testThrowsOnCircularReference()
{
$this->expectException(\Psr\Container\ContainerExceptionInterface::class);
$this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".');
$locator = $this->getServiceLocator([
'foo' => function () use (&$locator) { return $locator->get('bar'); },
'bar' => function () use (&$locator) { return $locator->get('baz'); },
'baz' => function () use (&$locator) { return $locator->get('bar'); },
]);

$locator->get('foo');
}
}
<?php










namespace Symfony\Contracts\Service;

use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;


class_exists(ContainerExceptionInterface::class);
class_exists(NotFoundExceptionInterface::class);







trait ServiceLocatorTrait
{
private array $factories;
private array $loading = [];
private array $providedTypes;




public function __construct(array $factories)
{
$this->factories = $factories;
}




public function has(string $id): bool
{
return isset($this->factories[$id]);
}




public function get(string $id): mixed
{
if (!isset($this->factories[$id])) {
throw $this->createNotFoundException($id);
}

if (isset($this->loading[$id])) {
$ids = array_values($this->loading);
$ids = \array_slice($this->loading, array_search($id, $ids));
$ids[] = $id;

throw $this->createCircularReferenceException($id, $ids);
}

$this->loading[$id] = $id;
try {
return $this->factories[$id]($this);
} finally {
unset($this->loading[$id]);
}
}




public function getProvidedServices(): array
{
if (!isset($this->providedTypes)) {
$this->providedTypes = [];

foreach ($this->factories as $name => $factory) {
if (!\is_callable($factory)) {
$this->providedTypes[$name] = '?';
} else {
$type = (new \ReflectionFunction($factory))->getReturnType();

$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?';
}
}
}

return $this->providedTypes;
}

private function createNotFoundException(string $id): NotFoundExceptionInterface
{
if (!$alternatives = array_keys($this->factories)) {
$message = 'is empty...';
} else {
$last = array_pop($alternatives);
if ($alternatives) {
$message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last);
} else {
$message = sprintf('only knows about the "%s" service.', $last);
}
}

if ($this->loading) {
$message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message);
} else {
$message = sprintf('Service "%s" not found: the current service locator %s', $id, $message);
}

return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface {
};
}

private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface
{
return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface {
};
}
}
<?php










namespace Symfony\Contracts\Service;














interface ResetInterface
{
public function reset();
}
<?php










namespace Symfony\Contracts\Service;

use Psr\Container\ContainerInterface;







interface ServiceProviderInterface extends ContainerInterface
{











public function getProvidedServices(): array;
}
<?php










namespace Symfony\Contracts\Service;
















interface ServiceSubscriberInterface
{





















public static function getSubscribedServices(): array;
}
<?php










namespace Symfony\Contracts\Service;

use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\Attribute\SubscribedService;







trait ServiceSubscriberTrait
{

protected $container;




public static function getSubscribedServices(): array
{
$services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : [];

foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if (self::class !== $method->getDeclaringClass()->name) {
continue;
}

if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) {
continue;
}

if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name));
}

if (!$returnType = $method->getReturnType()) {
throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class));
}

$serviceId = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType;

if ($returnType->allowsNull()) {
$serviceId = '?'.$serviceId;
}

$services[$attribute->newInstance()->key ?? self::class.'::'.$method->name] = $serviceId;
}

return $services;
}




public function setContainer(ContainerInterface $container): ?ContainerInterface
{
$this->container = $container;

if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) {
return parent::setContainer($container);
}

return null;
}
}
<?php










namespace Symfony\Contracts\Service\Attribute;

use Symfony\Contracts\Service\ServiceSubscriberTrait;







#[\Attribute(\Attribute::TARGET_METHOD)]
final class SubscribedService
{




public function __construct(
public ?string $key = null
) {
}
}
<?php










namespace Symfony\Contracts\Service\Attribute;









#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)]
final class Required
{
}
<?php










namespace Symfony\Component\DomCrawler;




class Image extends AbstractUriElement
{
public function __construct(\DOMElement $node, string $currentUri = null)
{
parent::__construct($node, $currentUri, 'GET');
}

protected function getRawUri(): string
{
return $this->node->getAttribute('src');
}

protected function setNode(\DOMElement $node)
{
if ('img' !== $node->nodeName) {
throw new \LogicException(sprintf('Unable to visualize a "%s" tag.', $node->nodeName));
}

$this->node = $node;
}
}
<?php










namespace Symfony\Component\DomCrawler;

use Symfony\Component\DomCrawler\Field\FormField;






class FormFieldRegistry
{
private array $fields = [];
private string $base = '';




public function add(FormField $field)
{
$segments = $this->getSegments($field->getName());

$target = &$this->fields;
while ($segments) {
if (!\is_array($target)) {
$target = [];
}
$path = array_shift($segments);
if ('' === $path) {
$target = &$target[];
} else {
$target = &$target[$path];
}
}
$target = $field;
}




public function remove(string $name)
{
$segments = $this->getSegments($name);
$target = &$this->fields;
while (\count($segments) > 1) {
$path = array_shift($segments);
if (!\is_array($target) || !\array_key_exists($path, $target)) {
return;
}
$target = &$target[$path];
}
unset($target[array_shift($segments)]);
}








public function &get(string $name): FormField|array
{
$segments = $this->getSegments($name);
$target = &$this->fields;
while ($segments) {
$path = array_shift($segments);
if (!\is_array($target) || !\array_key_exists($path, $target)) {
throw new \InvalidArgumentException(sprintf('Unreachable field "%s".', $path));
}
$target = &$target[$path];
}

return $target;
}




public function has(string $name): bool
{
try {
$this->get($name);

return true;
} catch (\InvalidArgumentException $e) {
return false;
}
}






public function set(string $name, mixed $value)
{
$target = &$this->get($name);
if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
$target->setValue($value);
} elseif (\is_array($value)) {
$registry = new static();
$registry->base = $name;
$registry->fields = $value;
foreach ($registry->all() as $k => $v) {
$this->set($k, $v);
}
} else {
throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name));
}
}






public function all(): array
{
return $this->walk($this->fields, $this->base);
}




private function walk(array $array, ?string $base = '', array &$output = []): array
{
foreach ($array as $k => $v) {
$path = empty($base) ? $k : sprintf('%s[%s]', $base, $k);
if (\is_array($v)) {
$this->walk($v, $path, $output);
} else {
$output[$path] = $v;
}
}

return $output;
}








private function getSegments(string $name): array
{
if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
$segments = [$m['base']];
while (!empty($m['extra'])) {
$extra = $m['extra'];
if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $extra, $m)) {
$segments[] = $m['segment'];
} else {
$segments[] = $extra;
}
}

return $segments;
}

return [$name];
}
}
<?php










namespace Symfony\Component\DomCrawler;

use Masterminds\HTML5;
use Symfony\Component\CssSelector\CssSelectorConverter;








class Crawler implements \Countable, \IteratorAggregate
{



protected $uri;




private string $defaultNamespacePrefix = 'default';






private array $namespaces = [];




private \ArrayObject $cachedNamespaces;

private ?string $baseHref;
private ?\DOMDocument $document = null;




private array $nodes = [];




private bool $isHtml = true;

private $html5Parser;




public function __construct(\DOMNodeList|\DOMNode|array|string $node = null, string $uri = null, string $baseHref = null)
{
$this->uri = $uri;
$this->baseHref = $baseHref ?: $uri;
$this->html5Parser = class_exists(HTML5::class) ? new HTML5(['disable_html_ns' => true]) : null;
$this->cachedNamespaces = new \ArrayObject();

$this->add($node);
}




public function getUri(): ?string
{
return $this->uri;
}




public function getBaseHref(): ?string
{
return $this->baseHref;
}




public function clear()
{
$this->nodes = [];
$this->document = null;
$this->cachedNamespaces = new \ArrayObject();
}











public function add(\DOMNodeList|\DOMNode|array|string|null $node)
{
if ($node instanceof \DOMNodeList) {
$this->addNodeList($node);
} elseif ($node instanceof \DOMNode) {
$this->addNode($node);
} elseif (\is_array($node)) {
$this->addNodes($node);
} elseif (\is_string($node)) {
$this->addContent($node);
} elseif (null !== $node) {
throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', get_debug_type($node)));
}
}








public function addContent(string $content, string $type = null)
{
if (empty($type)) {
$type = str_starts_with($content, '<?xml') ? 'application/xml' : 'text/html';
}


if (!preg_match('/(x|ht)ml/i', $type, $xmlMatches)) {
return;
}

$charset = preg_match('//u', $content) ? 'UTF-8' : 'ISO-8859-1';



$content = preg_replace_callback('/(charset *= *["\']?)([a-zA-Z\-0-9_:.]+)/i', function ($m) use (&$charset) {
if ('charset=' === $this->convertToHtmlEntities('charset=', $m[2])) {
$charset = $m[2];
}

return $m[1].$charset;
}, $content, 1);

if ('x' === $xmlMatches[1]) {
$this->addXmlContent($content, $charset);
} else {
$this->addHtmlContent($content, $charset);
}
}











public function addHtmlContent(string $content, string $charset = 'UTF-8')
{
$dom = $this->parseHtmlString($content, $charset);
$this->addDocument($dom);

$base = $this->filterRelativeXPath('descendant-or-self::base')->extract(['href']);

$baseHref = current($base);
if (\count($base) && !empty($baseHref)) {
if ($this->baseHref) {
$linkNode = $dom->createElement('a');
$linkNode->setAttribute('href', $baseHref);
$link = new Link($linkNode, $this->baseHref);
$this->baseHref = $link->getUri();
} else {
$this->baseHref = $baseHref;
}
}
}















public function addXmlContent(string $content, string $charset = 'UTF-8', int $options = \LIBXML_NONET)
{

if (!preg_match('/xmlns:/', $content)) {
$content = str_replace('xmlns', 'ns', $content);
}

$internalErrors = libxml_use_internal_errors(true);

$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;

if ('' !== trim($content)) {
@$dom->loadXML($content, $options);
}

libxml_use_internal_errors($internalErrors);

$this->addDocument($dom);

$this->isHtml = false;
}






public function addDocument(\DOMDocument $dom)
{
if ($dom->documentElement) {
$this->addNode($dom->documentElement);
}
}






public function addNodeList(\DOMNodeList $nodes)
{
foreach ($nodes as $node) {
if ($node instanceof \DOMNode) {
$this->addNode($node);
}
}
}






public function addNodes(array $nodes)
{
foreach ($nodes as $node) {
$this->add($node);
}
}






public function addNode(\DOMNode $node)
{
if ($node instanceof \DOMDocument) {
$node = $node->documentElement;
}

if (null !== $this->document && $this->document !== $node->ownerDocument) {
throw new \InvalidArgumentException('Attaching DOM nodes from multiple documents in the same crawler is forbidden.');
}

if (null === $this->document) {
$this->document = $node->ownerDocument;
}


if (\in_array($node, $this->nodes, true)) {
return;
}

$this->nodes[] = $node;
}




public function eq(int $position): static
{
if (isset($this->nodes[$position])) {
return $this->createSubCrawler($this->nodes[$position]);
}

return $this->createSubCrawler(null);
}

















public function each(\Closure $closure): array
{
$data = [];
foreach ($this->nodes as $i => $node) {
$data[] = $closure($this->createSubCrawler($node), $i);
}

return $data;
}




public function slice(int $offset = 0, int $length = null): static
{
return $this->createSubCrawler(\array_slice($this->nodes, $offset, $length));
}








public function reduce(\Closure $closure): static
{
$nodes = [];
foreach ($this->nodes as $i => $node) {
if (false !== $closure($this->createSubCrawler($node), $i)) {
$nodes[] = $node;
}
}

return $this->createSubCrawler($nodes);
}




public function first(): static
{
return $this->eq(0);
}




public function last(): static
{
return $this->eq(\count($this->nodes) - 1);
}






public function siblings(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild));
}

public function matches(string $selector): bool
{
if (!$this->nodes) {
return false;
}

$converter = $this->createCssSelectorConverter();
$xpath = $converter->toXPath($selector, 'self::');

return 0 !== $this->filterRelativeXPath($xpath)->count();
}








public function closest(string $selector): ?self
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$domNode = $this->getNode(0);

while (\XML_ELEMENT_NODE === $domNode->nodeType) {
$node = $this->createSubCrawler($domNode);
if ($node->matches($selector)) {
return $node;
}

$domNode = $node->getNode(0)->parentNode;
}

return null;
}






public function nextAll(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

return $this->createSubCrawler($this->sibling($this->getNode(0)));
}






public function previousAll(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling'));
}






public function ancestors(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);
$nodes = [];

while ($node = $node->parentNode) {
if (\XML_ELEMENT_NODE === $node->nodeType) {
$nodes[] = $node;
}
}

return $this->createSubCrawler($nodes);
}







public function children(string $selector = null): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

if (null !== $selector) {
$converter = $this->createCssSelectorConverter();
$xpath = $converter->toXPath($selector, 'child::');

return $this->filterRelativeXPath($xpath);
}

$node = $this->getNode(0)->firstChild;

return $this->createSubCrawler($node ? $this->sibling($node) : []);
}






public function attr(string $attribute): ?string
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);

return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null;
}






public function nodeName(): string
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

return $this->getNode(0)->nodeName;
}











public function text(string $default = null, bool $normalizeWhitespace = true): string
{
if (!$this->nodes) {
if (null !== $default) {
return $default;
}

throw new \InvalidArgumentException('The current node list is empty.');
}

$text = $this->getNode(0)->nodeValue;

if ($normalizeWhitespace) {
return trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $text), " \n\r\t\x0C");
}

return $text;
}




public function innerText(): string
{
return $this->filterXPath('.//text()')->text();
}








public function html(string $default = null): string
{
if (!$this->nodes) {
if (null !== $default) {
return $default;
}

throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);
$owner = $node->ownerDocument;

if (null !== $this->html5Parser && '<!DOCTYPE html>' === $owner->saveXML($owner->childNodes[0])) {
$owner = $this->html5Parser;
}

$html = '';
foreach ($node->childNodes as $child) {
$html .= $owner->saveHTML($child);
}

return $html;
}

public function outerHtml(): string
{
if (!\count($this)) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);
$owner = $node->ownerDocument;

if (null !== $this->html5Parser && '<!DOCTYPE html>' === $owner->saveXML($owner->childNodes[0])) {
$owner = $this->html5Parser;
}

return $owner->saveHTML($node);
}







public function evaluate(string $xpath): array|Crawler
{
if (null === $this->document) {
throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.');
}

$data = [];
$domxpath = $this->createDOMXPath($this->document, $this->findNamespacePrefixes($xpath));

foreach ($this->nodes as $node) {
$data[] = $domxpath->evaluate($xpath, $node);
}

if (isset($data[0]) && $data[0] instanceof \DOMNodeList) {
return $this->createSubCrawler($data);
}

return $data;
}










public function extract(array $attributes): array
{
$count = \count($attributes);

$data = [];
foreach ($this->nodes as $node) {
$elements = [];
foreach ($attributes as $attribute) {
if ('_text' === $attribute) {
$elements[] = $node->nodeValue;
} elseif ('_name' === $attribute) {
$elements[] = $node->nodeName;
} else {
$elements[] = $node->getAttribute($attribute);
}
}

$data[] = 1 === $count ? $elements[0] : $elements;
}

return $data;
}









public function filterXPath(string $xpath): static
{
$xpath = $this->relativize($xpath);


if ('' === $xpath) {
return $this->createSubCrawler(null);
}

return $this->filterRelativeXPath($xpath);
}








public function filter(string $selector): static
{
$converter = $this->createCssSelectorConverter();


return $this->filterRelativeXPath($converter->toXPath($selector));
}




public function selectLink(string $value): static
{
return $this->filterRelativeXPath(
sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %1$s) or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %1$s)]]', static::xpathLiteral(' '.$value.' '))
);
}




public function selectImage(string $value): static
{
$xpath = sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value));

return $this->filterRelativeXPath($xpath);
}




public function selectButton(string $value): static
{
return $this->filterRelativeXPath(
sprintf('descendant-or-self::input[((contains(%1$s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %2$s)) or (contains(%1$s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %2$s)) or @id=%3$s or @name=%3$s] | descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %2$s) or @id=%3$s or @name=%3$s]', 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value))
);
}






public function link(string $method = 'get'): Link
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);

if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node)));
}

return new Link($node, $this->baseHref, $method);
}








public function links(): array
{
$links = [];
foreach ($this->nodes as $node) {
if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_debug_type($node)));
}

$links[] = new Link($node, $this->baseHref, 'get');
}

return $links;
}






public function image(): Image
{
if (!\count($this)) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);

if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node)));
}

return new Image($node, $this->baseHref);
}






public function images(): array
{
$images = [];
foreach ($this as $node) {
if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_debug_type($node)));
}

$images[] = new Image($node, $this->baseHref);
}

return $images;
}






public function form(array $values = null, string $method = null): Form
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}

$node = $this->getNode(0);

if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node)));
}

$form = new Form($node, $this->uri, $method, $this->baseHref);

if (null !== $values) {
$form->setValues($values);
}

return $form;
}




public function setDefaultNamespacePrefix(string $prefix)
{
$this->defaultNamespacePrefix = $prefix;
}

public function registerNamespace(string $prefix, string $namespace)
{
$this->namespaces[$prefix] = $namespace;
}

















public static function xpathLiteral(string $s): string
{
if (!str_contains($s, "'")) {
return sprintf("'%s'", $s);
}

if (!str_contains($s, '"')) {
return sprintf('"%s"', $s);
}

$string = $s;
$parts = [];
while (true) {
if (false !== $pos = strpos($string, "'")) {
$parts[] = sprintf("'%s'", substr($string, 0, $pos));
$parts[] = "\"'\"";
$string = substr($string, $pos + 1);
} else {
$parts[] = "'$string'";
break;
}
}

return sprintf('concat(%s)', implode(', ', $parts));
}






private function filterRelativeXPath(string $xpath): static
{
$crawler = $this->createSubCrawler(null);
if (null === $this->document) {
return $crawler;
}

$domxpath = $this->createDOMXPath($this->document, $this->findNamespacePrefixes($xpath));

foreach ($this->nodes as $node) {
$crawler->add($domxpath->query($xpath, $node));
}

return $crawler;
}







private function relativize(string $xpath): string
{
$expressions = [];



$nonMatchingExpression = 'a[name() = "b"]';

$xpathLen = \strlen($xpath);
$openedBrackets = 0;
$startPosition = strspn($xpath, " \t\n\r\0\x0B");

for ($i = $startPosition; $i <= $xpathLen; ++$i) {
$i += strcspn($xpath, '"\'[]|', $i);

if ($i < $xpathLen) {
switch ($xpath[$i]) {
case '"':
case "'":
if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) {
return $xpath; 
}
continue 2;
case '[':
++$openedBrackets;
continue 2;
case ']':
--$openedBrackets;
continue 2;
}
}
if ($openedBrackets) {
continue;
}

if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) {


$j = 1 + strspn($xpath, "( \t\n\r\0\x0B", $startPosition + 1);
$parenthesis = substr($xpath, $startPosition, $j);
$startPosition += $j;
} else {
$parenthesis = '';
}
$expression = rtrim(substr($xpath, $startPosition, $i - $startPosition));

if (str_starts_with($expression, 'self::*/')) {
$expression = './'.substr($expression, 8);
}


if ('' === $expression) {
$expression = $nonMatchingExpression;
} elseif (str_starts_with($expression, '//')) {
$expression = 'descendant-or-self::'.substr($expression, 2);
} elseif (str_starts_with($expression, './/')) {
$expression = 'descendant-or-self::'.substr($expression, 3);
} elseif (str_starts_with($expression, './')) {
$expression = 'self::'.substr($expression, 2);
} elseif (str_starts_with($expression, 'child::')) {
$expression = 'self::'.substr($expression, 7);
} elseif ('/' === $expression[0] || '.' === $expression[0] || str_starts_with($expression, 'self::')) {
$expression = $nonMatchingExpression;
} elseif (str_starts_with($expression, 'descendant::')) {
$expression = 'descendant-or-self::'.substr($expression, 12);
} elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) {

$expression = $nonMatchingExpression;
} elseif (!str_starts_with($expression, 'descendant-or-self::')) {
$expression = 'self::'.$expression;
}
$expressions[] = $parenthesis.$expression;

if ($i === $xpathLen) {
return implode(' | ', $expressions);
}

$i += strspn($xpath, " \t\n\r\0\x0B", $i + 1);
$startPosition = $i + 1;
}

return $xpath; 
}

public function getNode(int $position): ?\DOMNode
{
return $this->nodes[$position] ?? null;
}

public function count(): int
{
return \count($this->nodes);
}




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

protected function sibling(\DOMNode $node, string $siblingDir = 'nextSibling'): array
{
$nodes = [];

$currentNode = $this->getNode(0);
do {
if ($node !== $currentNode && \XML_ELEMENT_NODE === $node->nodeType) {
$nodes[] = $node;
}
} while ($node = $node->$siblingDir);

return $nodes;
}

private function parseHtml5(string $htmlContent, string $charset = 'UTF-8'): \DOMDocument
{
return $this->html5Parser->parse($this->convertToHtmlEntities($htmlContent, $charset));
}

private function parseXhtml(string $htmlContent, string $charset = 'UTF-8'): \DOMDocument
{
$htmlContent = $this->convertToHtmlEntities($htmlContent, $charset);

$internalErrors = libxml_use_internal_errors(true);

$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;

if ('' !== trim($htmlContent)) {
@$dom->loadHTML($htmlContent);
}

libxml_use_internal_errors($internalErrors);

return $dom;
}




private function convertToHtmlEntities(string $htmlContent, string $charset = 'UTF-8'): string
{
set_error_handler(function () { throw new \Exception(); });

try {
return mb_encode_numericentity($htmlContent, [0x80, 0x10FFFF, 0, 0x1FFFFF], $charset);
} catch (\Exception|\ValueError $e) {
try {
$htmlContent = iconv($charset, 'UTF-8', $htmlContent);
$htmlContent = mb_encode_numericentity($htmlContent, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8');
} catch (\Exception|\ValueError $e) {
}

return $htmlContent;
} finally {
restore_error_handler();
}
}




private function createDOMXPath(\DOMDocument $document, array $prefixes = []): \DOMXPath
{
$domxpath = new \DOMXPath($document);

foreach ($prefixes as $prefix) {
$namespace = $this->discoverNamespace($domxpath, $prefix);
if (null !== $namespace) {
$domxpath->registerNamespace($prefix, $namespace);
}
}

return $domxpath;
}




private function discoverNamespace(\DOMXPath $domxpath, string $prefix): ?string
{
if (\array_key_exists($prefix, $this->namespaces)) {
return $this->namespaces[$prefix];
}

if ($this->cachedNamespaces->offsetExists($prefix)) {
return $this->cachedNamespaces[$prefix];
}


$namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix));

return $this->cachedNamespaces[$prefix] = ($node = $namespaces->item(0)) ? $node->nodeValue : null;
}

private function findNamespacePrefixes(string $xpath): array
{
if (preg_match_all('/(?P<prefix>[a-z_][a-z_0-9\-\.]*+):[^"\/:]/i', $xpath, $matches)) {
return array_unique($matches['prefix']);
}

return [];
}






private function createSubCrawler(\DOMNodeList|\DOMNode|array|string|null $nodes): static
{
$crawler = new static($nodes, $this->uri, $this->baseHref);
$crawler->isHtml = $this->isHtml;
$crawler->document = $this->document;
$crawler->namespaces = $this->namespaces;
$crawler->cachedNamespaces = $this->cachedNamespaces;
$crawler->html5Parser = $this->html5Parser;

return $crawler;
}




private function createCssSelectorConverter(): CssSelectorConverter
{
if (!class_exists(CssSelectorConverter::class)) {
throw new \LogicException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.');
}

return new CssSelectorConverter($this->isHtml);
}





private function parseHtmlString(string $content, string $charset): \DOMDocument
{
if ($this->canParseHtml5String($content)) {
return $this->parseHtml5($content, $charset);
}

return $this->parseXhtml($content, $charset);
}

private function canParseHtml5String(string $content): bool
{
if (null === $this->html5Parser) {
return false;
}
if (false === ($pos = stripos($content, '<!doctype html>'))) {
return false;
}
$header = substr($content, 0, $pos);

return '' === $header || $this->isValidHtml5Heading($header);
}

private function isValidHtml5Heading(string $heading): bool
{
return 1 === preg_match('/^\x{FEFF}?\s*(<!--[^>]*?-->\s*)*$/u', $heading);
}
}
<?php










namespace Symfony\Component\DomCrawler;








class UriResolver
{








public static function resolve(string $uri, ?string $baseUri): string
{
$uri = trim($uri);


if (null !== parse_url($uri, \PHP_URL_SCHEME)) {
return $uri;
}

if (null === $baseUri) {
throw new \InvalidArgumentException('The URI is relative, so you must define its base URI passing an absolute URL.');
}


if (!$uri) {
return $baseUri;
}


if ('#' === $uri[0]) {
return self::cleanupAnchor($baseUri).$uri;
}

$baseUriCleaned = self::cleanupUri($baseUri);

if ('?' === $uri[0]) {
return $baseUriCleaned.$uri;
}


if (0 === strpos($uri, '//')) {
return preg_replace('#^([^/]*)//.*$#', '$1', $baseUriCleaned).$uri;
}

$baseUriCleaned = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUriCleaned);


if ('/' === $uri[0]) {
return $baseUriCleaned.$uri;
}


$path = parse_url(substr($baseUri, \strlen($baseUriCleaned)), \PHP_URL_PATH);
$path = self::canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);

return $baseUriCleaned.('' === $path || '/' !== $path[0] ? '/' : '').$path;
}




private static function canonicalizePath(string $path): string
{
if ('' === $path || '/' === $path) {
return $path;
}

if ('.' === substr($path, -1)) {
$path .= '/';
}

$output = [];

foreach (explode('/', $path) as $segment) {
if ('..' === $segment) {
array_pop($output);
} elseif ('.' !== $segment) {
$output[] = $segment;
}
}

return implode('/', $output);
}




private static function cleanupUri(string $uri): string
{
return self::cleanupQuery(self::cleanupAnchor($uri));
}




private static function cleanupQuery(string $uri): string
{
if (false !== $pos = strpos($uri, '?')) {
return substr($uri, 0, $pos);
}

return $uri;
}




private static function cleanupAnchor(string $uri): string
{
if (false !== $pos = strpos($uri, '#')) {
return substr($uri, 0, $pos);
}

return $uri;
}
}
<?php










namespace Symfony\Component\DomCrawler\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\DomCrawler\Crawler;

final class CrawlerSelectorAttributeValueSame extends Constraint
{
private string $selector;
private string $attribute;
private string $expectedText;

public function __construct(string $selector, string $attribute, string $expectedText)
{
$this->selector = $selector;
$this->attribute = $attribute;
$this->expectedText = $expectedText;
}




public function toString(): string
{
return sprintf('has a node matching selector "%s" with attribute "%s" of value "%s"', $this->selector, $this->attribute, $this->expectedText);
}






protected function matches($crawler): bool
{
$crawler = $crawler->filter($this->selector);
if (!\count($crawler)) {
return false;
}

return $this->expectedText === trim($crawler->attr($this->attribute) ?? '');
}






protected function failureDescription($crawler): string
{
return 'the Crawler '.$this->toString();
}
}
<?php










namespace Symfony\Component\DomCrawler\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\DomCrawler\Crawler;

final class CrawlerSelectorTextSame extends Constraint
{
private string $selector;
private string $expectedText;

public function __construct(string $selector, string $expectedText)
{
$this->selector = $selector;
$this->expectedText = $expectedText;
}




public function toString(): string
{
return sprintf('has a node matching selector "%s" with content "%s"', $this->selector, $this->expectedText);
}






protected function matches($crawler): bool
{
$crawler = $crawler->filter($this->selector);
if (!\count($crawler)) {
return false;
}

return $this->expectedText === trim($crawler->text(null, true));
}






protected function failureDescription($crawler): string
{
return 'the Crawler '.$this->toString();
}
}
<?php










namespace Symfony\Component\DomCrawler\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\DomCrawler\Crawler;

final class CrawlerSelectorExists extends Constraint
{
private string $selector;

public function __construct(string $selector)
{
$this->selector = $selector;
}




public function toString(): string
{
return sprintf('matches selector "%s"', $this->selector);
}






protected function matches($crawler): bool
{
return 0 < \count($crawler->filter($this->selector));
}






protected function failureDescription($crawler): string
{
return 'the Crawler '.$this->toString();
}
}
<?php










namespace Symfony\Component\DomCrawler\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\DomCrawler\Crawler;

final class CrawlerSelectorTextContains extends Constraint
{
private string $selector;
private string $expectedText;
private bool $hasNode = false;
private string $nodeText;

public function __construct(string $selector, string $expectedText)
{
$this->selector = $selector;
$this->expectedText = $expectedText;
}




public function toString(): string
{
if ($this->hasNode) {
return sprintf('the text "%s" of the node matching selector "%s" contains "%s"', $this->nodeText, $this->selector, $this->expectedText);
}

return sprintf('the Crawler has a node matching selector "%s"', $this->selector);
}






protected function matches($crawler): bool
{
$crawler = $crawler->filter($this->selector);
if (!\count($crawler)) {
$this->hasNode = false;

return false;
}

$this->hasNode = true;
$this->nodeText = $crawler->text(null, true);

return false !== mb_strpos($this->nodeText, $this->expectedText);
}






protected function failureDescription($crawler): string
{
return $this->toString();
}
}
<?php










namespace Symfony\Component\DomCrawler;






abstract class AbstractUriElement
{



protected $node;




protected $method;




protected $currentUri;








public function __construct(\DOMElement $node, string $currentUri = null, ?string $method = 'GET')
{
$this->setNode($node);
$this->method = $method ? strtoupper($method) : null;
$this->currentUri = $currentUri;

$elementUriIsRelative = null === parse_url(trim($this->getRawUri()), \PHP_URL_SCHEME);
$baseUriIsAbsolute = null !== $this->currentUri && \in_array(strtolower(substr($this->currentUri, 0, 4)), ['http', 'file']);
if ($elementUriIsRelative && !$baseUriIsAbsolute) {
throw new \InvalidArgumentException(sprintf('The URL of the element is relative, so you must define its base URI passing an absolute URL to the constructor of the "%s" class ("%s" was passed).', __CLASS__, $this->currentUri));
}
}




public function getNode(): \DOMElement
{
return $this->node;
}




public function getMethod(): string
{
return $this->method ?? 'GET';
}




public function getUri(): string
{
return UriResolver::resolve($this->getRawUri(), $this->currentUri);
}




abstract protected function getRawUri(): string;






protected function canonicalizePath(string $path): string
{
if ('' === $path || '/' === $path) {
return $path;
}

if (str_ends_with($path, '.')) {
$path .= '/';
}

$output = [];

foreach (explode('/', $path) as $segment) {
if ('..' === $segment) {
array_pop($output);
} elseif ('.' !== $segment) {
$output[] = $segment;
}
}

return implode('/', $output);
}








abstract protected function setNode(\DOMElement $node);
}
<?php










namespace Symfony\Component\DomCrawler;

use Symfony\Component\DomCrawler\Field\ChoiceFormField;
use Symfony\Component\DomCrawler\Field\FormField;






class Form extends Link implements \ArrayAccess
{
private \DOMElement $button;
private $fields;
private ?string $baseHref;









public function __construct(\DOMElement $node, string $currentUri = null, string $method = null, string $baseHref = null)
{
parent::__construct($node, $currentUri, $method);
$this->baseHref = $baseHref;

$this->initialize();
}




public function getFormNode(): \DOMElement
{
return $this->node;
}








public function setValues(array $values): static
{
foreach ($values as $name => $value) {
$this->fields->set($name, $value);
}

return $this;
}






public function getValues(): array
{
$values = [];
foreach ($this->fields->all() as $name => $field) {
if ($field->isDisabled()) {
continue;
}

if (!$field instanceof Field\FileFormField && $field->hasValue()) {
$values[$name] = $field->getValue();
}
}

return $values;
}




public function getFiles(): array
{
if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
return [];
}

$files = [];

foreach ($this->fields->all() as $name => $field) {
if ($field->isDisabled()) {
continue;
}

if ($field instanceof Field\FileFormField) {
$files[$name] = $field->getValue();
}
}

return $files;
}







public function getPhpValues(): array
{
$values = [];
foreach ($this->getValues() as $name => $value) {
$qs = http_build_query([$name => $value], '', '&');
if (!empty($qs)) {
parse_str($qs, $expandedValue);
$varName = substr($name, 0, \strlen(key($expandedValue)));
$values[] = [$varName => current($expandedValue)];
}
}

return array_replace_recursive([], ...$values);
}











public function getPhpFiles(): array
{
$values = [];
foreach ($this->getFiles() as $name => $value) {
$qs = http_build_query([$name => $value], '', '&');
if (!empty($qs)) {
parse_str($qs, $expandedValue);
$varName = substr($name, 0, \strlen(key($expandedValue)));

array_walk_recursive(
$expandedValue,
function (&$value, $key) {
if (ctype_digit($value) && ('size' === $key || 'error' === $key)) {
$value = (int) $value;
}
}
);

reset($expandedValue);

$values[] = [$varName => current($expandedValue)];
}
}

return array_replace_recursive([], ...$values);
}








public function getUri(): string
{
$uri = parent::getUri();

if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
$query = parse_url($uri, \PHP_URL_QUERY);
$currentParameters = [];
if ($query) {
parse_str($query, $currentParameters);
}

$queryString = http_build_query(array_merge($currentParameters, $this->getValues()), '', '&');

$pos = strpos($uri, '?');
$base = false === $pos ? $uri : substr($uri, 0, $pos);
$uri = rtrim($base.'?'.$queryString, '?');
}

return $uri;
}

protected function getRawUri(): string
{

if ($this->button !== $this->node && $this->button->getAttribute('formaction')) {
return $this->button->getAttribute('formaction');
}

return $this->node->getAttribute('action');
}






public function getMethod(): string
{
if (null !== $this->method) {
return $this->method;
}


if ($this->button !== $this->node && $this->button->getAttribute('formmethod')) {
return strtoupper($this->button->getAttribute('formmethod'));
}

return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET';
}






public function getName(): string
{
return $this->node->getAttribute('name');
}




public function has(string $name): bool
{
return $this->fields->has($name);
}




public function remove(string $name)
{
$this->fields->remove($name);
}








public function get(string $name): FormField|array
{
return $this->fields->get($name);
}




public function set(FormField $field)
{
$this->fields->add($field);
}






public function all(): array
{
return $this->fields->all();
}






public function offsetExists(mixed $name): bool
{
return $this->has($name);
}










public function offsetGet(mixed $name): FormField|array
{
return $this->fields->get($name);
}









public function offsetSet(mixed $name, mixed $value): void
{
$this->fields->set($name, $value);
}






public function offsetUnset(mixed $name): void
{
$this->fields->remove($name);
}






public function disableValidation(): static
{
foreach ($this->fields->all() as $field) {
if ($field instanceof Field\ChoiceFormField) {
$field->disableValidation();
}
}

return $this;
}








protected function setNode(\DOMElement $node)
{
$this->button = $node;
if ('button' === $node->nodeName || ('input' === $node->nodeName && \in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image']))) {
if ($node->hasAttribute('form')) {

$formId = $node->getAttribute('form');
$form = $node->ownerDocument->getElementById($formId);
if (null === $form) {
throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId));
}
$this->node = $form;

return;
}

do {
if (null === $node = $node->parentNode) {
throw new \LogicException('The selected node does not have a form ancestor.');
}
} while ('form' !== $node->nodeName);
} elseif ('form' !== $node->nodeName) {
throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
}

$this->node = $node;
}








private function initialize()
{
$this->fields = new FormFieldRegistry();

$xpath = new \DOMXPath($this->node->ownerDocument);


if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) {
if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) {
$name = $this->button->getAttribute('name');
$this->button->setAttribute('value', '0');


$this->button->setAttribute('name', $name.'.x');
$this->set(new Field\InputFormField($this->button));


$this->button->setAttribute('name', $name.'.y');
$this->set(new Field\InputFormField($this->button));


$this->button->setAttribute('name', $name);
} else {
$this->set(new Field\InputFormField($this->button));
}
}


if ($this->node->hasAttribute('id')) {

$formId = Crawler::xpathLiteral($this->node->getAttribute('id'));

$fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[not(ancestor::template)]', $formId));
foreach ($fieldNodes as $node) {
$this->addField($node);
}
} else {


$fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[not(ancestor::template)]', $this->node);
foreach ($fieldNodes as $node) {
$this->addField($node);
}
}

if ($this->baseHref && '' !== $this->node->getAttribute('action')) {
$this->currentUri = $this->baseHref;
}
}

private function addField(\DOMElement $node)
{
if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
return;
}

$nodeName = $node->nodeName;
if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) {
$this->set(new Field\ChoiceFormField($node));
} elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) {


if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) {
$this->get($node->getAttribute('name'))->addChoice($node);
} else {
$this->set(new Field\ChoiceFormField($node));
}
} elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) {
$this->set(new Field\FileFormField($node));
} elseif ('input' == $nodeName && !\in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image'])) {
$this->set(new Field\InputFormField($node));
} elseif ('textarea' == $nodeName) {
$this->set(new Field\TextareaFormField($node));
}
}
}
<?php










namespace Symfony\Component\DomCrawler;






class Link extends AbstractUriElement
{
protected function getRawUri(): string
{
return $this->node->getAttribute('href');
}

protected function setNode(\DOMElement $node)
{
if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) {
throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName));
}

$this->node = $node;
}
}
<?php










namespace Symfony\Component\DomCrawler\Field;






abstract class FormField
{



protected $node;



protected $name;



protected $value;



protected $document;



protected $xpath;



protected $disabled;




public function __construct(\DOMElement $node)
{
$this->node = $node;
$this->name = $node->getAttribute('name');
$this->xpath = new \DOMXPath($node->ownerDocument);

$this->initialize();
}




public function getLabel(): ?\DOMElement
{
$xpath = new \DOMXPath($this->node->ownerDocument);

if ($this->node->hasAttribute('id')) {
$labels = $xpath->query(sprintf('descendant::label[@for="%s"]', $this->node->getAttribute('id')));
if ($labels->length > 0) {
return $labels->item(0);
}
}

$labels = $xpath->query('ancestor::label[1]', $this->node);

return $labels->length > 0 ? $labels->item(0) : null;
}




public function getName(): string
{
return $this->name;
}




public function getValue(): string|array|null
{
return $this->value;
}




public function setValue(?string $value)
{
$this->value = $value ?? '';
}




public function hasValue(): bool
{
return true;
}




public function isDisabled(): bool
{
return $this->node->hasAttribute('disabled');
}




abstract protected function initialize();
}
<?php










namespace Symfony\Component\DomCrawler\Field;






class FileFormField extends FormField
{







public function setErrorCode(int $error)
{
$codes = [\UPLOAD_ERR_INI_SIZE, \UPLOAD_ERR_FORM_SIZE, \UPLOAD_ERR_PARTIAL, \UPLOAD_ERR_NO_FILE, \UPLOAD_ERR_NO_TMP_DIR, \UPLOAD_ERR_CANT_WRITE, \UPLOAD_ERR_EXTENSION];
if (!\in_array($error, $codes)) {
throw new \InvalidArgumentException(sprintf('The error code "%s" is not valid.', $error));
}

$this->value = ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0];
}




public function upload(?string $value)
{
$this->setValue($value);
}




public function setValue(?string $value)
{
if (null !== $value && is_readable($value)) {
$error = \UPLOAD_ERR_OK;
$size = filesize($value);
$info = pathinfo($value);
$name = $info['basename'];


$tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), 0, 7), '/', '_');
if (\array_key_exists('extension', $info)) {
$tmp .= '.'.$info['extension'];
}
if (is_file($tmp)) {
unlink($tmp);
}
copy($value, $tmp);
$value = $tmp;
} else {
$error = \UPLOAD_ERR_NO_FILE;
$size = 0;
$name = '';
$value = '';
}

$this->value = ['name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size];
}




public function setFilePath(string $path)
{
parent::setValue($path);
}






protected function initialize()
{
if ('input' !== $this->node->nodeName) {
throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName));
}

if ('file' !== strtolower($this->node->getAttribute('type'))) {
throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is "%s").', $this->node->getAttribute('type')));
}

$this->setValue(null);
}
}
<?php










namespace Symfony\Component\DomCrawler\Field;






class TextareaFormField extends FormField
{





protected function initialize()
{
if ('textarea' !== $this->node->nodeName) {
throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName));
}

$this->value = '';
foreach ($this->node->childNodes as $node) {
$this->value .= $node->wholeText;
}
}
}
<?php










namespace Symfony\Component\DomCrawler\Field;








class ChoiceFormField extends FormField
{
private string $type;
private bool $multiple;
private array $options;
private bool $validationDisabled = false;






public function hasValue(): bool
{

if (\in_array($this->type, ['checkbox', 'radio']) && null === $this->value) {
return false;
}

return true;
}




public function isDisabled(): bool
{
if (parent::isDisabled() && 'select' === $this->type) {
return true;
}

foreach ($this->options as $option) {
if ($option['value'] == $this->value && $option['disabled']) {
return true;
}
}

return false;
}




public function select(string|array|bool $value)
{
$this->setValue($value);
}






public function tick()
{
if ('checkbox' !== $this->type) {
throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
}

$this->setValue(true);
}






public function untick()
{
if ('checkbox' !== $this->type) {
throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
}

$this->setValue(false);
}






public function setValue(string|array|bool|null $value)
{
if ('checkbox' === $this->type && false === $value) {

$this->value = null;
} elseif ('checkbox' === $this->type && true === $value) {

$this->value = $this->options[0]['value'];
} else {
if (\is_array($value)) {
if (!$this->multiple) {
throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
}

foreach ($value as $v) {
if (!$this->containsOption($v, $this->options)) {
throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $v, implode('", "', $this->availableOptionValues())));
}
}
} elseif (!$this->containsOption($value, $this->options)) {
throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $value, implode('", "', $this->availableOptionValues())));
}

if ($this->multiple) {
$value = (array) $value;
}

if (\is_array($value)) {
$this->value = $value;
} else {
parent::setValue($value);
}
}
}








public function addChoice(\DOMElement $node)
{
if (!$this->multiple && 'radio' !== $this->type) {
throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
}

$option = $this->buildOptionValue($node);
$this->options[] = $option;

if ($node->hasAttribute('checked')) {
$this->value = $option['value'];
}
}




public function getType(): string
{
return $this->type;
}




public function isMultiple(): bool
{
return $this->multiple;
}






protected function initialize()
{
if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) {
throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName));
}

if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) {
throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is "%s").', $this->node->getAttribute('type')));
}

$this->value = null;
$this->options = [];
$this->multiple = false;

if ('input' == $this->node->nodeName) {
$this->type = strtolower($this->node->getAttribute('type'));
$optionValue = $this->buildOptionValue($this->node);
$this->options[] = $optionValue;

if ($this->node->hasAttribute('checked')) {
$this->value = $optionValue['value'];
}
} else {
$this->type = 'select';
if ($this->node->hasAttribute('multiple')) {
$this->multiple = true;
$this->value = [];
$this->name = str_replace('[]', '', $this->name);
}

$found = false;
foreach ($this->xpath->query('descendant::option', $this->node) as $option) {
$optionValue = $this->buildOptionValue($option);
$this->options[] = $optionValue;

if ($option->hasAttribute('selected')) {
$found = true;
if ($this->multiple) {
$this->value[] = $optionValue['value'];
} else {
$this->value = $optionValue['value'];
}
}
}


if (!$found && !$this->multiple && !empty($this->options)) {
$this->value = $this->options[0]['value'];
}
}
}




private function buildOptionValue(\DOMElement $node): array
{
$option = [];

$defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on';
$defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue;
$option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue;
$option['disabled'] = $node->hasAttribute('disabled');

return $option;
}






public function containsOption(string $optionValue, array $options): bool
{
if ($this->validationDisabled) {
return true;
}

foreach ($options as $option) {
if ($option['value'] == $optionValue) {
return true;
}
}

return false;
}






public function availableOptionValues(): array
{
$values = [];

foreach ($this->options as $option) {
$values[] = $option['value'];
}

return $values;
}








public function disableValidation(): static
{
$this->validationDisabled = true;

return $this;
}
}
<?php










namespace Symfony\Component\DomCrawler\Field;









class InputFormField extends FormField
{





protected function initialize()
{
if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) {
throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName));
}

$type = strtolower($this->node->getAttribute('type'));
if ('checkbox' === $type) {
throw new \LogicException('Checkboxes should be instances of ChoiceFormField.');
}

if ('file' === $type) {
throw new \LogicException('File inputs should be instances of FileFormField.');
}

$this->value = $this->node->getAttribute('value');
}
}
<?php










namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Tag\TaggedValue;








class Parser
{
public const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)';
public const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
public const REFERENCE_PATTERN = '#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u';

private ?string $filename = null;
private int $offset = 0;
private int $numberOfParsedLines = 0;
private ?int $totalNumberOfLines = null;
private array $lines = [];
private int $currentLineNb = -1;
private string $currentLine = '';
private array $refs = [];
private array $skippedLineNumbers = [];
private array $locallySkippedLineNumbers = [];
private array $refsBeingParsed = [];









public function parseFile(string $filename, int $flags = 0): mixed
{
if (!is_file($filename)) {
throw new ParseException(sprintf('File "%s" does not exist.', $filename));
}

if (!is_readable($filename)) {
throw new ParseException(sprintf('File "%s" cannot be read.', $filename));
}

$this->filename = $filename;

try {
return $this->parse(file_get_contents($filename), $flags);
} finally {
$this->filename = null;
}
}









public function parse(string $value, int $flags = 0): mixed
{
if (false === preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename);
}

$this->refs = [];

try {
$data = $this->doParse($value, $flags);
} finally {
$this->refsBeingParsed = [];
$this->offset = 0;
$this->lines = [];
$this->currentLine = '';
$this->numberOfParsedLines = 0;
$this->refs = [];
$this->skippedLineNumbers = [];
$this->locallySkippedLineNumbers = [];
$this->totalNumberOfLines = null;
}

return $data;
}

private function doParse(string $value, int $flags)
{
$this->currentLineNb = -1;
$this->currentLine = '';
$value = $this->cleanup($value);
$this->lines = explode("\n", $value);
$this->numberOfParsedLines = \count($this->lines);
$this->locallySkippedLineNumbers = [];

if (null === $this->totalNumberOfLines) {
$this->totalNumberOfLines = $this->numberOfParsedLines;
}

if (!$this->moveToNextLine()) {
return null;
}

$data = [];
$context = null;
$allowOverwrite = false;

while ($this->isCurrentLineEmpty()) {
if (!$this->moveToNextLine()) {
return null;
}
}


if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) {
return new TaggedValue($tag, '');
}

do {
if ($this->isCurrentLineEmpty()) {
continue;
}


if ("\t" === $this->currentLine[0]) {
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename);

$isRef = $mergeNode = false;
if ('-' === $this->currentLine[0] && self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a sequence item when in a mapping.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
$context = 'sequence';

if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) {
$isRef = $matches['ref'];
$this->refsBeingParsed[] = $isRef;
$values['value'] = $matches['value'];
}

if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) {
throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}


if (isset($values['value']) && 0 === strpos(ltrim($values['value'], ' '), '-')) {

$currentLineNumber = $this->getRealCurrentLineNb();

$sequenceIndentation = \strlen($values['leadspaces']) + 1;
$sequenceYaml = substr($this->currentLine, $sequenceIndentation);
$sequenceYaml .= "\n".$this->getNextEmbedBlock($sequenceIndentation, true);

$data[] = $this->parseBlock($currentLineNumber, rtrim($sequenceYaml), $flags);
} elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
$data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true) ?? '', $flags);
} elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) {
$data[] = new TaggedValue(
$subTag,
$this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags)
);
} else {
if (
isset($values['leadspaces'])
&& (
'!' === $values['value'][0]
|| self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches)
)
) {

$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1);
}

$data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
} else {
$data[] = $this->parseValue($values['value'], $flags, $context);
}
}
if ($isRef) {
$this->refs[$isRef] = end($data);
array_pop($this->refsBeingParsed);
}
} elseif (
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(( |\t)++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
&& (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"]))
) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
}
$context = 'mapping';

try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}

if (!\is_string($key) && !\is_int($key)) {
throw new ParseException((is_numeric($key) ? 'Numeric' : 'Non-string').' keys are not supported. Quote your evaluable mapping keys instead.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}


if (\is_float($key)) {
$key = (string) $key;
}

if ('<<' === $key && (!isset($values['value']) || '&' !== $values['value'][0] || !self::preg_match('#^&(?P<ref>[^ ]+)#u', $values['value'], $refMatches))) {
$mergeNode = true;
$allowOverwrite = true;
if (isset($values['value'][0]) && '*' === $values['value'][0]) {
$refName = substr(rtrim($values['value']), 1);
if (!\array_key_exists($refName, $this->refs)) {
if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) {
throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$refName])), $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename);
}

throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

$refValue = $this->refs[$refName];

if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) {
$refValue = (array) $refValue;
}

if (!\is_array($refValue)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

$data += $refValue; 
} else {
if (isset($values['value']) && '' !== $values['value']) {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
$parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);

if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) {
$parsed = (array) $parsed;
}

if (!\is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

if (isset($parsed[0])) {



foreach ($parsed as $parsedItem) {
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) {
$parsedItem = (array) $parsedItem;
}

if (!\is_array($parsedItem)) {
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename);
}

$data += $parsedItem; 
}
} else {


$data += $parsed; 
}
}
} elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) {
$isRef = $matches['ref'];
$this->refsBeingParsed[] = $isRef;
$values['value'] = $matches['value'];
}

$subTag = null;
if ($mergeNode) {

} elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) {


if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {


if ($allowOverwrite || !isset($data[$key])) {
if (null !== $subTag) {
$data[$key] = new TaggedValue($subTag, '');
} else {
$data[$key] = null;
}
} else {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
} else {

$realCurrentLineNbKey = $this->getRealCurrentLineNb();
$value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
if ('<<' === $key) {
$this->refs[$refMatches['ref']] = $value;

if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) {
$value = (array) $value;
}

$data += $value;
} elseif ($allowOverwrite || !isset($data[$key])) {


if (null !== $subTag) {
$data[$key] = new TaggedValue($subTag, $value);
} else {
$data[$key] = $value;
}
} else {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine);
}
}
} else {
$value = $this->parseValue(rtrim($values['value']), $flags, $context);


if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
} else {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
if ($isRef) {
$this->refs[$isRef] = $data[$key];
array_pop($this->refsBeingParsed);
}
} elseif ('"' === $this->currentLine[0] || "'" === $this->currentLine[0]) {
if (null !== $context) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

try {
return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}
} elseif ('{' === $this->currentLine[0]) {
if (null !== $context) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

try {
$parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs);

while ($this->moveToNextLine()) {
if (!$this->isCurrentLineEmpty()) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
}

return $parsedMapping;
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}
} elseif ('[' === $this->currentLine[0]) {
if (null !== $context) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

try {
$parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs);

while ($this->moveToNextLine()) {
if (!$this->isCurrentLineEmpty()) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
}

return $parsedSequence;
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}
} else {

if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
}

if ($deprecatedUsage = (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1])) {
throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}


if (\is_string($value) && $this->lines[0] === trim($value)) {
try {
$value = Inline::parse($this->lines[0], $flags, $this->refs);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}

return $value;
}


if (0 === $this->currentLineNb) {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = false;
$value = '';

foreach ($this->lines as $line) {
$trimmedLine = trim($line);
if ('#' === ($trimmedLine[0] ?? '')) {
continue;
}

if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

if (false !== strpos($line, ': ')) {
throw new ParseException('Mapping values are not allowed in multi-line blocks.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}

if ('' === $trimmedLine) {
$value .= "\n";
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
$value .= ' ';
}

if ('' !== $trimmedLine && '\\' === substr($line, -1)) {
$value .= ltrim(substr($line, 0, -1));
} elseif ('' !== $trimmedLine) {
$value .= $trimmedLine;
}

if ('' === $trimmedLine) {
$previousLineWasNewline = true;
$previousLineWasTerminatedWithBackslash = false;
} elseif ('\\' === substr($line, -1)) {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = true;
} else {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = false;
}
}

try {
return Inline::parse(trim($value));
} catch (ParseException $e) {

}
}

throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
} while ($this->moveToNextLine());

if (null !== $tag) {
$data = new TaggedValue($tag, $data);
}

if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && 'mapping' === $context && !\is_object($data)) {
$object = new \stdClass();

foreach ($data as $key => $value) {
$object->$key = $value;
}

$data = $object;
}

return empty($data) ? null : $data;
}

private function parseBlock(int $offset, string $yaml, int $flags)
{
$skippedLineNumbers = $this->skippedLineNumbers;

foreach ($this->locallySkippedLineNumbers as $lineNumber) {
if ($lineNumber < $offset) {
continue;
}

$skippedLineNumbers[] = $lineNumber;
}

$parser = new self();
$parser->offset = $offset;
$parser->totalNumberOfLines = $this->totalNumberOfLines;
$parser->skippedLineNumbers = $skippedLineNumbers;
$parser->refs = &$this->refs;
$parser->refsBeingParsed = $this->refsBeingParsed;

return $parser->doParse($yaml, $flags);
}






public function getRealCurrentLineNb(): int
{
$realCurrentLineNumber = $this->currentLineNb + $this->offset;

foreach ($this->skippedLineNumbers as $skippedLineNumber) {
if ($skippedLineNumber > $realCurrentLineNumber) {
break;
}

++$realCurrentLineNumber;
}

return $realCurrentLineNumber;
}

private function getCurrentLineIndentation(): int
{
if (' ' !== ($this->currentLine[0] ?? '')) {
return 0;
}

return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' '));
}









private function getNextEmbedBlock(int $indentation = null, bool $inSequence = false): string
{
$oldLineIndentation = $this->getCurrentLineIndentation();

if (!$this->moveToNextLine()) {
return '';
}

if (null === $indentation) {
$newIndent = null;
$movements = 0;

do {
$EOF = false;


if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
$EOF = !$this->moveToNextLine();

if (!$EOF) {
++$movements;
}
} else {
$newIndent = $this->getCurrentLineIndentation();
}
} while (!$EOF && null === $newIndent);

for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
}

$unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();

if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
} else {
$newIndent = $indentation;
}

$data = [];

if ($this->getCurrentLineIndentation() >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent ?? 0);
} elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
$data[] = $this->currentLine;
} else {
$this->moveToPreviousLine();

return '';
}

if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) {


$this->moveToPreviousLine();

return '';
}

$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
$isItComment = $this->isCurrentLineComment();

while ($this->moveToNextLine()) {
if ($isItComment && !$isItUnindentedCollection) {
$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
$isItComment = $this->isCurrentLineComment();
}

$indent = $this->getCurrentLineIndentation();

if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
$this->moveToPreviousLine();
break;
}

if ($this->isCurrentLineBlank()) {
$data[] = substr($this->currentLine, $newIndent);
continue;
}

if ($indent >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} elseif ($this->isCurrentLineComment()) {
$data[] = $this->currentLine;
} elseif (0 == $indent) {
$this->moveToPreviousLine();

break;
} else {
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
}

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

private function hasMoreLines(): bool
{
return (\count($this->lines) - 1) > $this->currentLineNb;
}




private function moveToNextLine(): bool
{
if ($this->currentLineNb >= $this->numberOfParsedLines - 1) {
return false;
}

$this->currentLine = $this->lines[++$this->currentLineNb];

return true;
}




private function moveToPreviousLine(): bool
{
if ($this->currentLineNb < 1) {
return false;
}

$this->currentLine = $this->lines[--$this->currentLineNb];

return true;
}










private function parseValue(string $value, int $flags, string $context): mixed
{
if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) {
$value = substr($value, 1, $pos - 2);
} else {
$value = substr($value, 1);
}

if (!\array_key_exists($value, $this->refs)) {
if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) {
throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$value])), $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
}

throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
}

return $this->refs[$value];
}

if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
$modifiers = $matches['modifiers'] ?? '';

$data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers));

if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
if ('!!binary' === $matches['tag']) {
return Inline::evaluateBinaryScalar($data);
}

return new TaggedValue(substr($matches['tag'], 1), $data);
}

return $data;
}

try {
if ('' !== $value && '{' === $value[0]) {
$cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));

return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs);
} elseif ('' !== $value && '[' === $value[0]) {
$cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));

return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs);
}

switch ($value[0] ?? '') {
case '"':
case "'":
$cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value));
$parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs);

if (isset($this->currentLine[$cursor]) && preg_replace('/\s*(#.*)?$/A', '', substr($this->currentLine, $cursor))) {
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor)));
}

return $parsedValue;
default:
$lines = [];

while ($this->moveToNextLine()) {

if (0 === $this->getCurrentLineIndentation()) {
$this->moveToPreviousLine();

break;
}

$lines[] = trim($this->currentLine);
}

for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
if ('' === $lines[$i]) {
$value .= "\n";
$previousLineBlank = true;
} elseif ($previousLineBlank) {
$value .= $lines[$i];
$previousLineBlank = false;
} else {
$value .= ' '.$lines[$i];
$previousLineBlank = false;
}
}

Inline::$parsedLineNumber = $this->getRealCurrentLineNb();

$parsedValue = Inline::parse($value, $flags, $this->refs);

if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}

return $parsedValue;
}
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

throw $e;
}
}








private function parseBlockScalar(string $style, string $chomping = '', int $indentation = 0): string
{
$notEOF = $this->moveToNextLine();
if (!$notEOF) {
return '';
}

$isCurrentLineBlank = $this->isCurrentLineBlank();
$blockLines = [];


while ($notEOF && $isCurrentLineBlank) {

if ($notEOF = $this->moveToNextLine()) {
$blockLines[] = '';
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}


if (0 === $indentation) {
$currentLineLength = \strlen($this->currentLine);

for ($i = 0; $i < $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) {
++$indentation;
}
}

if ($indentation > 0) {
$pattern = sprintf('/^ {%d}(.*)$/', $indentation);

while (
$notEOF && (
$isCurrentLineBlank ||
self::preg_match($pattern, $this->currentLine, $matches)
)
) {
if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
$blockLines[] = substr($this->currentLine, $indentation);
} elseif ($isCurrentLineBlank) {
$blockLines[] = '';
} else {
$blockLines[] = $matches[1];
}


if ($notEOF = $this->moveToNextLine()) {
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
} elseif ($notEOF) {
$blockLines[] = '';
}

if ($notEOF) {
$blockLines[] = '';
$this->moveToPreviousLine();
} elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
$blockLines[] = '';
}


if ('>' === $style) {
$text = '';
$previousLineIndented = false;
$previousLineBlank = false;

for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) {
$text .= "\n";
$previousLineIndented = false;
$previousLineBlank = true;
} elseif (' ' === $blockLines[$i][0]) {
$text .= "\n".$blockLines[$i];
$previousLineIndented = true;
$previousLineBlank = false;
} elseif ($previousLineIndented) {
$text .= "\n".$blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
} elseif ($previousLineBlank || 0 === $i) {
$text .= $blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
} else {
$text .= ' '.$blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
}
}
} else {
$text = implode("\n", $blockLines);
}


if ('' === $chomping) {
$text = preg_replace('/\n+$/', "\n", $text);
} elseif ('-' === $chomping) {
$text = preg_replace('/\n+$/', '', $text);
}

return $text;
}




private function isNextLineIndented(): bool
{
$currentIndentation = $this->getCurrentLineIndentation();
$movements = 0;

do {
$EOF = !$this->moveToNextLine();

if (!$EOF) {
++$movements;
}
} while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));

if ($EOF) {
return false;
}

$ret = $this->getCurrentLineIndentation() > $currentIndentation;

for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
}

return $ret;
}

private function isCurrentLineEmpty(): bool
{
return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
}

private function isCurrentLineBlank(): bool
{
return '' === $this->currentLine || '' === trim($this->currentLine, ' ');
}

private function isCurrentLineComment(): bool
{

$ltrimmedLine = '' !== $this->currentLine && ' ' === $this->currentLine[0] ? ltrim($this->currentLine, ' ') : $this->currentLine;

return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
}

private function isCurrentLineLastLineInDocument(): bool
{
return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1);
}

private function cleanup(string $value): string
{
$value = str_replace(["\r\n", "\r"], "\n", $value);


$count = 0;
$value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count);
$this->offset += $count;


$trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
if (1 === $count) {

$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
$value = $trimmedValue;
}


$trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
if (1 === $count) {

$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
$value = $trimmedValue;


$value = preg_replace('#\.\.\.\s*$#', '', $value);
}

return $value;
}

private function isNextLineUnIndentedCollection(): bool
{
$currentIndentation = $this->getCurrentLineIndentation();
$movements = 0;

do {
$EOF = !$this->moveToNextLine();

if (!$EOF) {
++$movements;
}
} while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));

if ($EOF) {
return false;
}

$ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();

for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
}

return $ret;
}

private function isStringUnIndentedCollectionItem(): bool
{
return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- ');
}














public static function preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int
{
if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) {
switch (preg_last_error()) {
case \PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case \PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case \PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case \PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case \PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Error.';
}

throw new ParseException($error);
}

return $ret;
}







private function trimTag(string $value): string
{
if ('!' === $value[0]) {
return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' ');
}

return $value;
}

private function getLineTag(string $value, int $flags, bool $nextLineCheck = true): ?string
{
if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) {
return null;
}

if ($nextLineCheck && !$this->isNextLineIndented()) {
return null;
}

$tag = substr($matches['tag'], 1);


if ($tag && '!' === $tag[0]) {
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}

if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
return $tag;
}

throw new ParseException(sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}

private function lexInlineQuotedString(int &$cursor = 0): string
{
$quotation = $this->currentLine[$cursor];
$value = $quotation;
++$cursor;

$previousLineWasNewline = true;
$previousLineWasTerminatedWithBackslash = false;
$lineNumber = 0;

do {
if (++$lineNumber > 1) {
$cursor += strspn($this->currentLine, ' ', $cursor);
}

if ($this->isCurrentLineBlank()) {
$value .= "\n";
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
$value .= ' ';
}

for (; \strlen($this->currentLine) > $cursor; ++$cursor) {
switch ($this->currentLine[$cursor]) {
case '\\':
if ("'" === $quotation) {
$value .= '\\';
} elseif (isset($this->currentLine[++$cursor])) {
$value .= '\\'.$this->currentLine[$cursor];
}

break;
case $quotation:
++$cursor;

if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) {
$value .= "''";
break;
}

return $value.$quotation;
default:
$value .= $this->currentLine[$cursor];
}
}

if ($this->isCurrentLineBlank()) {
$previousLineWasNewline = true;
$previousLineWasTerminatedWithBackslash = false;
} elseif ('\\' === $this->currentLine[-1]) {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = true;
} else {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = false;
}

if ($this->hasMoreLines()) {
$cursor = 0;
}
} while ($this->moveToNextLine());

throw new ParseException('Malformed inline YAML string.');
}

private function lexUnquotedString(int &$cursor): string
{
$offset = $cursor;
$cursor += strcspn($this->currentLine, '[]{},: ', $cursor);

if ($cursor === $offset) {
throw new ParseException('Malformed unquoted YAML string.');
}

return substr($this->currentLine, $offset, $cursor - $offset);
}

private function lexInlineMapping(int &$cursor = 0): string
{
return $this->lexInlineStructure($cursor, '}');
}

private function lexInlineSequence(int &$cursor = 0): string
{
return $this->lexInlineStructure($cursor, ']');
}

private function lexInlineStructure(int &$cursor, string $closingTag): string
{
$value = $this->currentLine[$cursor];
++$cursor;

do {
$this->consumeWhitespaces($cursor);

while (isset($this->currentLine[$cursor])) {
switch ($this->currentLine[$cursor]) {
case '"':
case "'":
$value .= $this->lexInlineQuotedString($cursor);
break;
case ':':
case ',':
$value .= $this->currentLine[$cursor];
++$cursor;
break;
case '{':
$value .= $this->lexInlineMapping($cursor);
break;
case '[':
$value .= $this->lexInlineSequence($cursor);
break;
case $closingTag:
$value .= $this->currentLine[$cursor];
++$cursor;

return $value;
case '#':
break 2;
default:
$value .= $this->lexUnquotedString($cursor);
}

if ($this->consumeWhitespaces($cursor)) {
$value .= ' ';
}
}

if ($this->hasMoreLines()) {
$cursor = 0;
}
} while ($this->moveToNextLine());

throw new ParseException('Malformed inline YAML string.');
}

private function consumeWhitespaces(int &$cursor): bool
{
$whitespacesConsumed = 0;

do {
$whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor);
$whitespacesConsumed += $whitespaceOnlyTokenLength;
$cursor += $whitespaceOnlyTokenLength;

if (isset($this->currentLine[$cursor])) {
return 0 < $whitespacesConsumed;
}

if ($this->hasMoreLines()) {
$cursor = 0;
}
} while ($this->moveToNextLine());

return 0 < $whitespacesConsumed;
}
}
<?php










namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;








class Yaml
{
public const DUMP_OBJECT = 1;
public const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
public const PARSE_OBJECT = 4;
public const PARSE_OBJECT_FOR_MAP = 8;
public const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
public const PARSE_DATETIME = 32;
public const DUMP_OBJECT_AS_MAP = 64;
public const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
public const PARSE_CONSTANT = 256;
public const PARSE_CUSTOM_TAGS = 512;
public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
public const DUMP_NULL_AS_TILDE = 2048;














public static function parseFile(string $filename, int $flags = 0): mixed
{
$yaml = new Parser();

return $yaml->parseFile($filename, $flags);
}















public static function parse(string $input, int $flags = 0): mixed
{
$yaml = new Parser();

return $yaml->parse($input, $flags);
}












public static function dump(mixed $input, int $inline = 2, int $indent = 4, int $flags = 0): string
{
$yaml = new Dumper($indent);

return $yaml->dump($input, $inline, 0, $flags);
}
}
<?php










namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Tag\TaggedValue;








class Inline
{
public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';

public static int $parsedLineNumber = -1;
public static ?string $parsedFilename = null;

private static bool $exceptionOnInvalidType = false;
private static bool $objectSupport = false;
private static bool $objectForMap = false;
private static bool $constantSupport = false;

public static function initialize(int $flags, int $parsedLineNumber = null, string $parsedFilename = null)
{
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
self::$parsedFilename = $parsedFilename;

if (null !== $parsedLineNumber) {
self::$parsedLineNumber = $parsedLineNumber;
}
}









public static function parse(string $value = null, int $flags = 0, array &$references = []): mixed
{
self::initialize($flags);

$value = trim($value);

if ('' === $value) {
return '';
}

$i = 0;
$tag = self::parseTag($value, $i, $flags);
switch ($value[$i]) {
case '[':
$result = self::parseSequence($value, $flags, $i, $references);
++$i;
break;
case '{':
$result = self::parseMapping($value, $flags, $i, $references);
++$i;
break;
default:
$result = self::parseScalar($value, $flags, null, $i, true, $references);
}


if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) {
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}

if (null !== $tag && '' !== $tag) {
return new TaggedValue($tag, $result);
}

return $result;
}









public static function dump(mixed $value, int $flags = 0): string
{
switch (true) {
case \is_resource($value):
if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
}

return self::dumpNull($flags);
case $value instanceof \DateTimeInterface:
return $value->format('c');
case $value instanceof \UnitEnum:
return sprintf('!php/const %s::%s', \get_class($value), $value->name);
case \is_object($value):
if ($value instanceof TaggedValue) {
return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
}

if (Yaml::DUMP_OBJECT & $flags) {
return '!php/object '.self::dump(serialize($value));
}

if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
$output = [];

foreach ($value as $key => $val) {
$output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
}

return sprintf('{ %s }', implode(', ', $output));
}

if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
throw new DumpException('Object support when dumping a YAML file has been disabled.');
}

return self::dumpNull($flags);
case \is_array($value):
return self::dumpArray($value, $flags);
case null === $value:
return self::dumpNull($flags);
case true === $value:
return 'true';
case false === $value:
return 'false';
case \is_int($value):
return $value;
case is_numeric($value) && false === strpbrk($value, "\f\n\r\t\v"):
$locale = setlocale(\LC_NUMERIC, 0);
if (false !== $locale) {
setlocale(\LC_NUMERIC, 'C');
}
if (\is_float($value)) {
$repr = (string) $value;
if (is_infinite($value)) {
$repr = str_ireplace('INF', '.Inf', $repr);
} elseif (floor($value) == $value && $repr == $value) {

if (false === strpos($repr, 'E')) {
$repr = $repr.'.0';
}
}
} else {
$repr = \is_string($value) ? "'$value'" : (string) $value;
}
if (false !== $locale) {
setlocale(\LC_NUMERIC, $locale);
}

return $repr;
case '' == $value:
return "''";
case self::isBinaryString($value):
return '!!binary '.base64_encode($value);
case Escaper::requiresDoubleQuoting($value):
return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value):
case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value):
case Parser::preg_match(self::getHexRegex(), $value):
case Parser::preg_match(self::getTimestampRegex(), $value):
return Escaper::escapeWithSingleQuotes($value);
default:
return $value;
}
}




public static function isHash(array|\ArrayObject|\stdClass $value): bool
{
if ($value instanceof \stdClass || $value instanceof \ArrayObject) {
return true;
}

$expectedKey = 0;

foreach ($value as $key => $val) {
if ($key !== $expectedKey++) {
return true;
}
}

return false;
}







private static function dumpArray(array $value, int $flags): string
{

if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) {
$output = [];
foreach ($value as $val) {
$output[] = self::dump($val, $flags);
}

return sprintf('[%s]', implode(', ', $output));
}


$output = [];
foreach ($value as $key => $val) {
$output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
}

return sprintf('{ %s }', implode(', ', $output));
}

private static function dumpNull(int $flags): string
{
if (Yaml::DUMP_NULL_AS_TILDE & $flags) {
return '~';
}

return 'null';
}






public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = [], bool &$isQuoted = null): mixed
{
if (\in_array($scalar[$i], ['"', "'"], true)) {

$isQuoted = true;
$output = self::parseQuotedScalar($scalar, $i);

if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), " \n");
if ('' === $tmp) {
throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (!\in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
}
} else {

$isQuoted = false;

if (!$delimiters) {
$output = substr($scalar, $i);
$i += \strlen($output);


if (Parser::preg_match('/[ \t]+#/', $output, $match, \PREG_OFFSET_CAPTURE)) {
$output = substr($output, 0, $match[0][1]);
}
} elseif (Parser::preg_match('/^(.*?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
$output = $match[1];
$i += \strlen($output);
$output = trim($output);
} else {
throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename);
}


if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0] || '%' === $output[0])) {
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename);
}

if ($evaluate) {
$output = self::evaluateScalar($output, $flags, $references, $isQuoted);
}
}

return $output;
}






private static function parseQuotedScalar(string $scalar, int &$i = 0): string
{
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string: "%s".', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

$output = substr($match[0], 1, -1);

$unescaper = new Unescaper();
if ('"' == $scalar[$i]) {
$output = $unescaper->unescapeDoubleQuotedString($output);
} else {
$output = $unescaper->unescapeSingleQuotedString($output);
}

$i += \strlen($match[0]);

return $output;
}






private static function parseSequence(string $sequence, int $flags, int &$i = 0, array &$references = []): array
{
$output = [];
$len = \strlen($sequence);
++$i;


while ($i < $len) {
if (']' === $sequence[$i]) {
return $output;
}
if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
++$i;

continue;
}

$tag = self::parseTag($sequence, $i, $flags);
switch ($sequence[$i]) {
case '[':

$value = self::parseSequence($sequence, $flags, $i, $references);
break;
case '{':

$value = self::parseMapping($sequence, $flags, $i, $references);
break;
default:
$value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references, $isQuoted);


if (\is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {

try {
$pos = 0;
$value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
} catch (\InvalidArgumentException $e) {

}
}

if (!$isQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) {
$references[$matches['ref']] = $matches['value'];
$value = $matches['value'];
}

--$i;
}

if (null !== $tag && '' !== $tag) {
$value = new TaggedValue($tag, $value);
}

$output[] = $value;

++$i;
}

throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename);
}






private static function parseMapping(string $mapping, int $flags, int &$i = 0, array &$references = []): array|\stdClass
{
$output = [];
$len = \strlen($mapping);
++$i;
$allowOverwrite = false;


while ($i < $len) {
switch ($mapping[$i]) {
case ' ':
case ',':
case "\n":
++$i;
continue 2;
case '}':
if (self::$objectForMap) {
return (object) $output;
}

return $output;
}


$offsetBeforeKeyParsing = $i;
$isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true);
$key = self::parseScalar($mapping, $flags, [':', ' '], $i, false);

if ($offsetBeforeKeyParsing === $i) {
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
}

if ('!php/const' === $key) {
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false);
$key = self::evaluateScalar($key, $flags);
}

if (false === $i = strpos($mapping, ':', $i)) {
break;
}

if (!$isKeyQuoted) {
$evaluatedKey = self::evaluateScalar($key, $flags, $references);

if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
throw new ParseException('Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead.', self::$parsedLineNumber + 1, $mapping);
}
}

if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], [' ', ',', '[', ']', '{', '}', "\n"], true))) {
throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping);
}

if ('<<' === $key) {
$allowOverwrite = true;
}

while ($i < $len) {
if (':' === $mapping[$i] || ' ' === $mapping[$i] || "\n" === $mapping[$i]) {
++$i;

continue;
}

$tag = self::parseTag($mapping, $i, $flags);
switch ($mapping[$i]) {
case '[':

$value = self::parseSequence($mapping, $flags, $i, $references);




if ('<<' === $key) {
foreach ($value as $parsedValue) {
$output += $parsedValue;
}
} elseif ($allowOverwrite || !isset($output[$key])) {
if (null !== $tag) {
$output[$key] = new TaggedValue($tag, $value);
} else {
$output[$key] = $value;
}
} elseif (isset($output[$key])) {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping);
}
break;
case '{':

$value = self::parseMapping($mapping, $flags, $i, $references);




if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key])) {
if (null !== $tag) {
$output[$key] = new TaggedValue($tag, $value);
} else {
$output[$key] = $value;
}
} elseif (isset($output[$key])) {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping);
}
break;
default:
$value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references, $isValueQuoted);




if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key])) {
if (!$isValueQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) {
$references[$matches['ref']] = $matches['value'];
$value = $matches['value'];
}

if (null !== $tag) {
$output[$key] = new TaggedValue($tag, $value);
} else {
$output[$key] = $value;
}
} elseif (isset($output[$key])) {
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping);
}
--$i;
}
++$i;

continue 2;
}
}

throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename);
}






private static function evaluateScalar(string $scalar, int $flags, array &$references = [], bool &$isQuotedString = null): mixed
{
$isQuotedString = false;
$scalar = trim($scalar);

if (0 === strpos($scalar, '*')) {
if (false !== $pos = strpos($scalar, '#')) {
$value = substr($scalar, 1, $pos - 2);
} else {
$value = substr($scalar, 1);
}


if (false === $value || '' === $value) {
throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}

if (!\array_key_exists($value, $references)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}

return $references[$value];
}

$scalarLower = strtolower($scalar);

switch (true) {
case 'null' === $scalarLower:
case '' === $scalar:
case '~' === $scalar:
return null;
case 'true' === $scalarLower:
return true;
case 'false' === $scalarLower:
return false;
case '!' === $scalar[0]:
switch (true) {
case 0 === strpos($scalar, '!!str '):
$s = (string) substr($scalar, 6);

if (\in_array($s[0] ?? '', ['"', "'"], true)) {
$isQuotedString = true;
$s = self::parseQuotedScalar($s);
}

return $s;
case 0 === strpos($scalar, '! '):
return substr($scalar, 2);
case 0 === strpos($scalar, '!php/object'):
if (self::$objectSupport) {
if (!isset($scalar[12])) {
throw new ParseException('Missing value for tag "!php/object".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

return unserialize(self::parseScalar(substr($scalar, 12)));
}

if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

return null;
case 0 === strpos($scalar, '!php/const'):
if (self::$constantSupport) {
if (!isset($scalar[11])) {
throw new ParseException('Missing value for tag "!php/const".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

$i = 0;
if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
return \constant($const);
}

throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

return null;
case 0 === strpos($scalar, '!!float '):
return (float) substr($scalar, 8);
case 0 === strpos($scalar, '!!binary '):
return self::evaluateBinaryScalar(substr($scalar, 9));
}

throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename);
case preg_match('/^(?:\+|-)?0o(?P<value>[0-7_]++)$/', $scalar, $matches):
$value = str_replace('_', '', $matches['value']);

if ('-' === $scalar[0]) {
return -octdec($value);
}

return octdec($value);
case \in_array($scalar[0], ['+', '-', '.'], true) || is_numeric($scalar[0]):
if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) {
$scalar = str_replace('_', '', $scalar);
}

switch (true) {
case ctype_digit($scalar):
case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
$cast = (int) $scalar;

return ($scalar === (string) $cast) ? $cast : $scalar;
case is_numeric($scalar):
case Parser::preg_match(self::getHexRegex(), $scalar):
$scalar = str_replace('_', '', $scalar);

return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
case '.inf' === $scalarLower:
case '.nan' === $scalarLower:
return -log(0);
case '-.inf' === $scalarLower:
return log(0);
case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
return (float) str_replace('_', '', $scalar);
case Parser::preg_match(self::getTimestampRegex(), $scalar):

$time = new \DateTime($scalar, new \DateTimeZone('UTC'));

if (Yaml::PARSE_DATETIME & $flags) {
return $time;
}

try {
if (false !== $scalar = $time->getTimestamp()) {
return $scalar;
}
} catch (\ValueError $e) {

}

return $time->format('U');
}
}

return (string) $scalar;
}

private static function parseTag(string $value, int &$i, int $flags): ?string
{
if ('!' !== $value[$i]) {
return null;
}

$tagLength = strcspn($value, " \t\n[]{},", $i + 1);
$tag = substr($value, $i + 1, $tagLength);

$nextOffset = $i + $tagLength + 1;
$nextOffset += strspn($value, ' ', $nextOffset);

if ('' === $tag && (!isset($value[$nextOffset]) || \in_array($value[$nextOffset], [']', '}', ','], true))) {
throw new ParseException('Using the unquoted scalar value "!" is not supported. You must quote it.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}


if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || 'str' === $tag || 'php/const' === $tag || 'php/object' === $tag)) {

return null;
}

$i = $nextOffset;


if ('' !== $tag && '!' === $tag[0]) {
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}

if ('' !== $tag && !isset($value[$i])) {
throw new ParseException(sprintf('Missing value for tag "%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}

if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) {
return $tag;
}

throw new ParseException(sprintf('Tags support is not enabled. Enable the "Yaml::PARSE_CUSTOM_TAGS" flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}

public static function evaluateBinaryScalar(string $scalar): string
{
$parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));

if (0 !== (\strlen($parsedBinaryData) % 4)) {
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}

return base64_decode($parsedBinaryData, true);
}

private static function isBinaryString(string $value): bool
{
return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
}






private static function getTimestampRegex(): string
{
return <<<EOF
        ~^
        (?P<year>[0-9][0-9][0-9][0-9])
        -(?P<month>[0-9][0-9]?)
        -(?P<day>[0-9][0-9]?)
        (?:(?:[Tt]|[ \t]+)
        (?P<hour>[0-9][0-9]?)
        :(?P<minute>[0-9][0-9])
        :(?P<second>[0-9][0-9])
        (?:\.(?P<fraction>[0-9]*))?
        (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
        (?::(?P<tz_minute>[0-9][0-9]))?))?)?
        $~x
EOF;
}




private static function getHexRegex(): string
{
return '~^0x[0-9a-f_]++$~i';
}
}
<?php










namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;









class Unescaper
{



public const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';






public function unescapeSingleQuotedString(string $value): string
{
return str_replace('\'\'', '\'', $value);
}






public function unescapeDoubleQuotedString(string $value): string
{
$callback = function ($match) {
return $this->unescapeCharacter($match[0]);
};


return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
}






private function unescapeCharacter(string $value): string
{
switch ($value[1]) {
case '0':
return "\x0";
case 'a':
return "\x7";
case 'b':
return "\x8";
case 't':
return "\t";
case "\t":
return "\t";
case 'n':
return "\n";
case 'v':
return "\xB";
case 'f':
return "\xC";
case 'r':
return "\r";
case 'e':
return "\x1B";
case ' ':
return ' ';
case '"':
return '"';
case '/':
return '/';
case '\\':
return '\\';
case 'N':

return "\xC2\x85";
case '_':

return "\xC2\xA0";
case 'L':

return "\xE2\x80\xA8";
case 'P':

return "\xE2\x80\xA9";
case 'x':
return self::utf8chr(hexdec(substr($value, 2, 2)));
case 'u':
return self::utf8chr(hexdec(substr($value, 2, 4)));
case 'U':
return self::utf8chr(hexdec(substr($value, 2, 8)));
default:
throw new ParseException(sprintf('Found unknown escape character "%s".', $value));
}
}




private static function utf8chr(int $c): string
{
if (0x80 > $c %= 0x200000) {
return \chr($c);
}
if (0x800 > $c) {
return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
}
if (0x10000 > $c) {
return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
}

return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
}
}
<?php










namespace Symfony\Component\Yaml\Tag;





final class TaggedValue
{
private string $tag;
private mixed $value;

public function __construct(string $tag, mixed $value)
{
$this->tag = $tag;
$this->value = $value;
}

public function getTag(): string
{
return $this->tag;
}

public function getValue()
{
return $this->value;
}
}
<?php










namespace Symfony\Component\Yaml\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\CI\GithubActionReporter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Yaml;







#[AsCommand(name: 'lint:yaml', description: 'Lint a YAML file and outputs encountered errors')]
class LintCommand extends Command
{
private $parser;
private ?string $format = null;
private bool $displayCorrectFiles;
private ?\Closure $directoryIteratorProvider;
private ?\Closure $isReadableProvider;

public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null)
{
parent::__construct($name);

$this->directoryIteratorProvider = null === $directoryIteratorProvider || $directoryIteratorProvider instanceof \Closure ? $directoryIteratorProvider : \Closure::fromCallable($directoryIteratorProvider);
$this->isReadableProvider = null === $isReadableProvider || $isReadableProvider instanceof \Closure ? $isReadableProvider : \Closure::fromCallable($isReadableProvider);
}




protected function configure()
{
$this
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude')
->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null)
->setHelp(<<<EOF
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
the first encountered syntax error.

You can validates YAML contents passed from STDIN:

  <info>cat filename | php %command.full_name% -</info>

You can also validate the syntax of a file:

  <info>php %command.full_name% filename</info>

Or of a whole directory:

  <info>php %command.full_name% dirname</info>
  <info>php %command.full_name% dirname --format=json</info>

You can also exclude one or more specific files:

  <info>php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml"</info>

EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$filenames = (array) $input->getArgument('filename');
$excludes = $input->getOption('exclude');
$this->format = $input->getOption('format');
$flags = $input->getOption('parse-tags');

if ('github' === $this->format && !class_exists(GithubActionReporter::class)) {
throw new \InvalidArgumentException('The "github" format is only available since "symfony/console" >= 5.3.');
}

if (null === $this->format) {

$this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt';
}

$flags = $flags ? Yaml::PARSE_CUSTOM_TAGS : 0;

$this->displayCorrectFiles = $output->isVerbose();

if (['-'] === $filenames) {
return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]);
}

if (!$filenames) {
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
}

$filesInfo = [];
foreach ($filenames as $filename) {
if (!$this->isReadable($filename)) {
throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
}

foreach ($this->getFiles($filename) as $file) {
if (!\in_array($file->getPathname(), $excludes, true)) {
$filesInfo[] = $this->validate(file_get_contents($file), $flags, $file);
}
}
}

return $this->display($io, $filesInfo);
}

private function validate(string $content, int $flags, string $file = null)
{
$prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
if (\E_USER_DEPRECATED === $level) {
throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1);
}

return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
});

try {
$this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags);
} catch (ParseException $e) {
return ['file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage()];
} finally {
restore_error_handler();
}

return ['file' => $file, 'valid' => true];
}

private function display(SymfonyStyle $io, array $files): int
{
switch ($this->format) {
case 'txt':
return $this->displayTxt($io, $files);
case 'json':
return $this->displayJson($io, $files);
case 'github':
return $this->displayTxt($io, $files, true);
default:
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
}
}

private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false): int
{
$countFiles = \count($filesInfo);
$erroredFiles = 0;
$suggestTagOption = false;

if ($errorAsGithubAnnotations) {
$githubReporter = new GithubActionReporter($io);
}

foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
} elseif (!$info['valid']) {
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->text(sprintf('<error> >> %s</error>', $info['message']));

if (false !== strpos($info['message'], 'PARSE_CUSTOM_TAGS')) {
$suggestTagOption = true;
}

if ($errorAsGithubAnnotations) {
$githubReporter->error($info['message'], $info['file'] ?? 'php://stdin', $info['line']);
}
}
}

if (0 === $erroredFiles) {
$io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
} else {
$io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.%s', $countFiles - $erroredFiles, $erroredFiles, $suggestTagOption ? ' Use the --parse-tags option if you want parse custom tags.' : ''));
}

return min($erroredFiles, 1);
}

private function displayJson(SymfonyStyle $io, array $filesInfo): int
{
$errors = 0;

array_walk($filesInfo, function (&$v) use (&$errors) {
$v['file'] = (string) $v['file'];
if (!$v['valid']) {
++$errors;
}

if (isset($v['message']) && false !== strpos($v['message'], 'PARSE_CUSTOM_TAGS')) {
$v['message'] .= ' Use the --parse-tags option if you want parse custom tags.';
}
});

$io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));

return min($errors, 1);
}

private function getFiles(string $fileOrDirectory): iterable
{
if (is_file($fileOrDirectory)) {
yield new \SplFileInfo($fileOrDirectory);

return;
}

foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
if (!\in_array($file->getExtension(), ['yml', 'yaml'])) {
continue;
}

yield $file;
}
}

private function getParser(): Parser
{
return $this->parser ??= new Parser();
}

private function getDirectoryIterator(string $directory): iterable
{
$default = function ($directory) {
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
\RecursiveIteratorIterator::LEAVES_ONLY
);
};

if (null !== $this->directoryIteratorProvider) {
return ($this->directoryIteratorProvider)($directory, $default);
}

return $default($directory);
}

private function isReadable(string $fileOrDirectory): bool
{
$default = function ($fileOrDirectory) {
return is_readable($fileOrDirectory);
};

if (null !== $this->isReadableProvider) {
return ($this->isReadableProvider)($fileOrDirectory, $default);
}

return $default($fileOrDirectory);
}

public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['txt', 'json', 'github']);
}
}
}
<?php










namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Tag\TaggedValue;








class Dumper
{



private int $indentation;

public function __construct(int $indentation = 4)
{
if ($indentation < 1) {
throw new \InvalidArgumentException('The indentation must be greater than zero.');
}

$this->indentation = $indentation;
}









public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string
{
$output = '';
$prefix = $indent ? str_repeat(' ', $indent) : '';
$dumpObjectAsInlineMap = true;

if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) {
$dumpObjectAsInlineMap = empty((array) $input);
}

if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) {
$output .= $prefix.Inline::dump($input, $flags);
} elseif ($input instanceof TaggedValue) {
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix);
} else {
$dumpAsMap = Inline::isHash($input);

foreach ($input as $key => $value) {
if ('' !== $output && "\n" !== $output[-1]) {
$output .= "\n";
}

if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && false !== strpos($value, "\n") && false === strpos($value, "\r")) {


$blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : '';

if (isset($value[-2]) && "\n" === $value[-2] && "\n" === $value[-1]) {
$blockChompingIndicator = '+';
} elseif ("\n" === $value[-1]) {
$blockChompingIndicator = '';
} else {
$blockChompingIndicator = '-';
}

$output .= sprintf('%s%s%s |%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator, $blockChompingIndicator);

foreach (explode("\n", $value) as $row) {
if ('' === $row) {
$output .= "\n";
} else {
$output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row);
}
}

continue;
}

if ($value instanceof TaggedValue) {
$output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag());

if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) {


$blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : '';
$output .= sprintf(' |%s', $blockIndentationIndicator);

foreach (explode("\n", $value->getValue()) as $row) {
$output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row);
}

continue;
}

if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
} else {
$output .= "\n";
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags);
}

continue;
}

$dumpObjectAsInlineMap = true;

if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) {
$dumpObjectAsInlineMap = empty((array) $value);
}

$willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || empty($value);

$output .= sprintf('%s%s%s%s',
$prefix,
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
$willBeInlined ? ' ' : "\n",
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
).($willBeInlined ? "\n" : '');
}
}

return $output;
}

private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string
{
$output = sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag());

if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) {


$blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : '';
$output .= sprintf(' |%s', $blockIndentationIndicator);

foreach (explode("\n", $value->getValue()) as $row) {
$output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row);
}

return $output;
}

if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
}

return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags);
}
}
<?php










namespace Symfony\Component\Yaml;









class Escaper
{

public const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\x7f|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";





private const ESCAPEES = ['\\', '\\\\', '\\"', '"',
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
"\x7f",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
];
private const ESCAPED = ['\\\\', '\\"', '\\\\', '\\"',
'\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
'\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
'\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
'\\x7f',
'\\N', '\\_', '\\L', '\\P',
];






public static function requiresDoubleQuoting(string $value): bool
{
return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
}






public static function escapeWithDoubleQuotes(string $value): string
{
return sprintf('"%s"', str_replace(self::ESCAPEES, self::ESCAPED, $value));
}






public static function requiresSingleQuoting(string $value): bool
{


if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) {
return true;
}



return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` \p{Zs}]/xu', $value);
}






public static function escapeWithSingleQuotes(string $value): string
{
return sprintf("'%s'", str_replace('\'', '\'\'', $value));
}
}
<?php










namespace Symfony\Component\Yaml\Exception;






class ParseException extends RuntimeException
{
private ?string $parsedFile;
private int $parsedLine;
private ?string $snippet;
private string $rawMessage;







public function __construct(string $message, int $parsedLine = -1, string $snippet = null, string $parsedFile = null, \Throwable $previous = null)
{
$this->parsedFile = $parsedFile;
$this->parsedLine = $parsedLine;
$this->snippet = $snippet;
$this->rawMessage = $message;

$this->updateRepr();

parent::__construct($this->message, 0, $previous);
}




public function getSnippet(): string
{
return $this->snippet;
}




public function setSnippet(string $snippet)
{
$this->snippet = $snippet;

$this->updateRepr();
}






public function getParsedFile(): string
{
return $this->parsedFile;
}




public function setParsedFile(string $parsedFile)
{
$this->parsedFile = $parsedFile;

$this->updateRepr();
}




public function getParsedLine(): int
{
return $this->parsedLine;
}




public function setParsedLine(int $parsedLine)
{
$this->parsedLine = $parsedLine;

$this->updateRepr();
}

private function updateRepr()
{
$this->message = $this->rawMessage;

$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}

if (null !== $this->parsedFile) {
$this->message .= sprintf(' in %s', json_encode($this->parsedFile, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
}

if ($this->parsedLine >= 0) {
$this->message .= sprintf(' at line %d', $this->parsedLine);
}

if ($this->snippet) {
$this->message .= sprintf(' (near "%s")', $this->snippet);
}

if ($dot) {
$this->message .= '.';
}
}
}
<?php










namespace Symfony\Component\Yaml\Exception;






interface ExceptionInterface extends \Throwable
{
}
<?php










namespace Symfony\Component\Yaml\Exception;






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










namespace Symfony\Component\Yaml\Exception;






class DumpException extends RuntimeException
{
}
<?php





















































namespace phpseclib\Crypt;








class Rijndael extends Base
{














var $cipher_name_mcrypt = 'rijndael-128';









var $password_default_salt = 'phpseclib';








var $w;








var $dw;












var $Nb = 4;












var $key_length = 16;









var $Nk = 4;








var $Nr;







var $c;







var $kl;





















function setKeyLength($length)
{
switch (true) {
case $length <= 128:
$this->key_length = 16;
break;
case $length <= 160:
$this->key_length = 20;
break;
case $length <= 192:
$this->key_length = 24;
break;
case $length <= 224:
$this->key_length = 28;
break;
default:
$this->key_length = 32;
}

parent::setKeyLength($length);
}










function setBlockLength($length)
{
$length >>= 5;
if ($length > 8) {
$length = 8;
} elseif ($length < 4) {
$length = 4;
}
$this->Nb = $length;
$this->block_size = $length << 2;
$this->changed = true;
$this->_setEngine();
}











function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->block_size != 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
$this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode();
break;
case self::ENGINE_MCRYPT:
$this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
if ($this->key_length % 8) { 

return false;
}
}

return parent::isValidEngine($engine);
}








function _encryptBlock($in)
{
static $tables;
if (empty($tables)) {
$tables = &$this->_getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
$t2 = $tables[2];
$t3 = $tables[3];
$sbox = $tables[4];

$state = array();
$words = unpack('N*', $in);

$c = $this->c;
$w = $this->w;
$Nb = $this->Nb;
$Nr = $this->Nr;


$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $w[++$wc];
}









$temp = array();
for ($round = 1; $round < $Nr; ++$round) {
$i = 0; 
$j = $c[1];
$k = $c[2];
$l = $c[3];

while ($i < $Nb) {
$temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
$t1[$state[$j] >> 16 & 0x000000FF] ^
$t2[$state[$k] >> 8 & 0x000000FF] ^
$t3[$state[$l] & 0x000000FF] ^
$w[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}


for ($i = 0; $i < $Nb; ++$i) {
$state[$i] = $sbox[$state[$i] & 0x000000FF] |
($sbox[$state[$i] >> 8 & 0x000000FF] << 8) |
($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
}


$i = 0; 
$j = $c[1];
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = ($state[$i] & intval(0xFF000000)) ^
($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^
$w[$i];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}

switch ($Nb) {
case 8:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
case 7:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
case 6:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
case 5:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
default:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
}
}








function _decryptBlock($in)
{
static $invtables;
if (empty($invtables)) {
$invtables = &$this->_getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
$dt2 = $invtables[2];
$dt3 = $invtables[3];
$isbox = $invtables[4];

$state = array();
$words = unpack('N*', $in);

$c = $this->c;
$dw = $this->dw;
$Nb = $this->Nb;
$Nr = $this->Nr;


$wc = $Nb - 1;
foreach ($words as $word) {
$state[] = $word ^ $dw[++$wc];
}

$temp = array();
for ($round = $Nr - 1; $round > 0; --$round) {
$i = 0; 
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];

while ($i < $Nb) {
$temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
$dt1[$state[$j] >> 16 & 0x000000FF] ^
$dt2[$state[$k] >> 8 & 0x000000FF] ^
$dt3[$state[$l] & 0x000000FF] ^
$dw[++$wc];
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}
$state = $temp;
}


$i = 0; 
$j = $Nb - $c[1];
$k = $Nb - $c[2];
$l = $Nb - $c[3];

while ($i < $Nb) {
$word = ($state[$i] & intval(0xFF000000)) |
($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF);

$temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] |
($isbox[$word >> 8 & 0x000000FF] << 8) |
($isbox[$word >> 16 & 0x000000FF] << 16) |
($isbox[$word >> 24 & 0x000000FF] << 24));
++$i;
$j = ($j + 1) % $Nb;
$k = ($k + 1) % $Nb;
$l = ($l + 1) % $Nb;
}

switch ($Nb) {
case 8:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
case 7:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
case 6:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
case 5:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
default:
return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
}
}







function _setupKey()
{


static $rcon;

if (!isset($rcon)) {
$rcon = array(0,
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
);
$rcon = array_map('intval', $rcon);
}

if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {

return;
}
$this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size);

$this->Nk = $this->key_length >> 2;

$this->Nr = max($this->Nk, $this->Nb) + 6;





switch ($this->Nb) {
case 4:
case 5:
case 6:
$this->c = array(0, 1, 2, 3);
break;
case 7:
$this->c = array(0, 1, 2, 4);
break;
case 8:
$this->c = array(0, 1, 3, 4);
}

$w = array_values(unpack('N*words', $this->key));

$length = $this->Nb * ($this->Nr + 1);
for ($i = $this->Nk; $i < $length; $i++) {
$temp = $w[$i - 1];
if ($i % $this->Nk == 0) {




$temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); 
$temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
$temp = $this->_subWord($temp);
}
$w[$i] = $w[$i - $this->Nk] ^ $temp;
}








list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
$temp = $this->w = $this->dw = array();
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
if ($col == $this->Nb) {
if ($row == 0) {
$this->dw[0] = $this->w[0];
} else {

$j = 0;
while ($j < $this->Nb) {
$dw = $this->_subWord($this->w[$row][$j]);
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
$dt1[$dw >> 16 & 0x000000FF] ^
$dt2[$dw >> 8 & 0x000000FF] ^
$dt3[$dw & 0x000000FF];
$j++;
}
$this->dw[$row] = $temp;
}

$col = 0;
$row++;
}
$this->w[$row][$col] = $w[$i];
}

$this->dw[$row] = $this->w[$row];


$this->dw = array_reverse($this->dw);
$w = array_pop($this->w);
$dw = array_pop($this->dw);
foreach ($this->w as $r => $wr) {
foreach ($wr as $c => $wc) {
$w[] = $wc;
$dw[] = $this->dw[$r][$c];
}
}
$this->w = $w;
$this->dw = $dw;
}







function _subWord($word)
{
static $sbox;
if (empty($sbox)) {
list(, , , , $sbox) = $this->_getTables();
}

return $sbox[$word & 0x000000FF] |
($sbox[$word >> 8 & 0x000000FF] << 8) |
($sbox[$word >> 16 & 0x000000FF] << 16) |
($sbox[$word >> 24 & 0x000000FF] << 24);
}










function &_getTables()
{
static $tables;
if (empty($tables)) {



$t3 = array_map('intval', array(


0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
));

foreach ($t3 as $t3i) {
$t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF);
$t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
$t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
}

$tables = array(

$t0,
$t1,
$t2,
$t3,

array(
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
)
);
}
return $tables;
}










function &_getInvTables()
{
static $tables;
if (empty($tables)) {
$dt3 = array_map('intval', array(
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
));

foreach ($dt3 as $dt3i) {
$dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF);
$dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF);
$dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF);
};

$tables = array(

$dt0,
$dt1,
$dt2,
$dt3,

array(
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
)
);
}
return $tables;
}







function _setupInlineCrypt()
{




$lambda_functions =& self::_getLambdaFunctions();




$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);


$code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}

if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:

$w = $this->w;
$dw = $this->dw;
$init_encrypt = '';
$init_decrypt = '';
break;
default:
for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) {
$w[] = '$w[' . $i . ']';
$dw[] = '$dw[' . $i . ']';
}
$init_encrypt = '$w  = $self->w;';
$init_decrypt = '$dw = $self->dw;';
}

$Nr = $this->Nr;
$Nb = $this->Nb;
$c = $this->c;


$init_encrypt.= '
                if (empty($tables)) {
                    $tables = &$self->_getTables();
                }
                $t0   = $tables[0];
                $t1   = $tables[1];
                $t2   = $tables[2];
                $t3   = $tables[3];
                $sbox = $tables[4];
            ';

$s = 'e';
$e = 's';
$wc = $Nb - 1;


$encrypt_block = '$in = unpack("N*", $in);'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n";
}


for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = array($e, $s);
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.=
'$'.$e.$i.' =
                        $t0[($'.$s.$i .' >> 24) & 0xff] ^
                        $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^
                        $t2[($'.$s.(($i + $c[2]) % $Nb).' >>  8) & 0xff] ^
                        $t3[ $'.$s.(($i + $c[3]) % $Nb).'        & 0xff] ^
                        '.$w[++$wc].";\n";
}
}


for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.=
'$'.$e.$i.' =
                     $sbox[ $'.$e.$i.'        & 0xff]        |
                    ($sbox[($'.$e.$i.' >>  8) & 0xff] <<  8) |
                    ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
                    ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
}
$encrypt_block .= '$in = pack("N*"'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block.= ',
                    ($'.$e.$i .' & '.((int)0xFF000000).') ^
                    ($'.$e.(($i + $c[1]) % $Nb).' &         0x00FF0000   ) ^
                    ($'.$e.(($i + $c[2]) % $Nb).' &         0x0000FF00   ) ^
                    ($'.$e.(($i + $c[3]) % $Nb).' &         0x000000FF   ) ^
                    '.$w[$i]."\n";
}
$encrypt_block .= ');';


$init_decrypt.= '
                if (empty($invtables)) {
                    $invtables = &$self->_getInvTables();
                }
                $dt0   = $invtables[0];
                $dt1   = $invtables[1];
                $dt2   = $invtables[2];
                $dt3   = $invtables[3];
                $isbox = $invtables[4];
            ';

$s = 'e';
$e = 's';
$wc = $Nb - 1;


$decrypt_block = '$in = unpack("N*", $in);'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n";
}


for ($round = 1; $round < $Nr; ++$round) {
list($s, $e) = array($e, $s);
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.=
'$'.$e.$i.' =
                        $dt0[($'.$s.$i .' >> 24) & 0xff] ^
                        $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^
                        $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >>  8) & 0xff] ^
                        $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).'        & 0xff] ^
                        '.$dw[++$wc].";\n";
}
}


for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.=
'$'.$e.$i.' =
                     $isbox[ $'.$e.$i.'        & 0xff]        |
                    ($isbox[($'.$e.$i.' >>  8) & 0xff] <<  8) |
                    ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
                    ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
}
$decrypt_block .= '$in = pack("N*"'."\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block.= ',
                    ($'.$e.$i. ' & '.((int)0xFF000000).') ^
                    ($'.$e.(($Nb + $i - $c[1]) % $Nb).' &         0x00FF0000   ) ^
                    ($'.$e.(($Nb + $i - $c[2]) % $Nb).' &         0x0000FF00   ) ^
                    ($'.$e.(($Nb + $i - $c[3]) % $Nb).' &         0x000000FF   ) ^
                    '.$dw[$i]."\n";
}
$decrypt_block .= ');';

$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
array(
'init_crypt' => 'static $tables; static $invtables;',
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
<?php





















































































































namespace phpseclib\Crypt;









class Blowfish extends Base
{







var $block_size = 8;








var $cipher_name_mcrypt = 'blowfish';








var $cfb_init_len = 500;








var $sha512;









var $sbox0 = array(
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
);







var $sbox1 = array(
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
);







var $sbox2 = array(
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
);







var $sbox3 = array(
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
);







var $parray = array(
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b
);









var $bctx;







var $kl;












var $key_length = 16;

























function __construct($mode = self::MODE_CBC)
{
parent::__construct($mode);

$this->sbox0 = array_map('intval', $this->sbox0);
$this->sbox1 = array_map('intval', $this->sbox1);
$this->sbox2 = array_map('intval', $this->sbox2);
$this->sbox3 = array_map('intval', $this->sbox3);
$this->parray = array_map('intval', $this->parray);
}









function setKeyLength($length)
{
if ($length < 32) {
$this->key_length = 4;
} elseif ($length > 448) {
$this->key_length = 56;
} else {
$this->key_length = $length >> 3;
}

parent::setKeyLength($length);
}











function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {



if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) {
return false;
}
if ($this->key_length < 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'bf-ecb';
$this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode();
}

return parent::isValidEngine($engine);
}







function _setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key']) {

return;
}
$this->kl = array('key' => $this->key);


$this->bctx = array(
'p' => array(),
'sb' => array(
$this->sbox0,
$this->sbox1,
$this->sbox2,
$this->sbox3
)
);


$key = array_values(unpack('C*', $this->key));
$keyl = count($key);

for ($j = 0, $i = 0; $i < 18; ++$i) {

for ($data = 0, $k = 0; $k < 4; ++$k) {
$data = ($data << 8) | $key[$j];
if (++$j >= $keyl) {
$j = 0;
}
}
$this->bctx['p'][] = $this->parray[$i] ^ intval($data);
}



$data = "\0\0\0\0\0\0\0\0";
for ($i = 0; $i < 18; $i += 2) {
list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data)));
$this->bctx['p'][$i ] = $l;
$this->bctx['p'][$i + 1] = $r;
}
for ($i = 0; $i < 4; ++$i) {
for ($j = 0; $j < 256; $j += 2) {
list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data)));
$this->bctx['sb'][$i][$j ] = $l;
$this->bctx['sb'][$i][$j + 1] = $r;
}
}
}









function _bcrypt_hash($sha2pass, $sha2salt)
{
$p = $this->parray;
$sbox0 = $this->sbox0;
$sbox1 = $this->sbox1;
$sbox2 = $this->sbox2;
$sbox3 = $this->sbox3;

$cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite'));
$sha2pass = array_values(unpack('N*', $sha2pass));
$sha2salt = array_values(unpack('N*', $sha2salt));

$this->_expandstate($sha2salt, $sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 0; $i < 64; $i++) {
$this->_expand0state($sha2salt, $sbox0, $sbox1, $sbox2, $sbox3, $p);
$this->_expand0state($sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

for ($i = 0; $i < 64; $i++) {
for ($j = 0; $j < 8; $j+= 2) { 
list($cdata[$j], $cdata[$j + 1]) = $this->_encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}
}

$output = '';
for ($i = 0; $i < count($cdata); $i++) {
$output.= pack('L*', $cdata[$i]);
}
return $output;
}











function bcrypt_pbkdf($pass, $salt, $keylen, $rounds)
{
if (PHP_INT_SIZE == 4) {
user_error('bcrypt is far too slow to be practical on 32-bit versions of PHP');
return false;
}

if (!isset($this->sha512)) {
$this->sha512 = new Hash('sha512');
}

$sha2pass = $this->sha512->hash($pass);
$results = array();
$count = 1;
while (32 * count($results) < $keylen) {
$countsalt = $salt . pack('N', $count++);
$sha2salt = $this->sha512->hash($countsalt);
$out = $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt);
for ($i = 1; $i < $rounds; $i++) {
$sha2salt = $this->sha512->hash($tmpout);
$tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt);
$out^= $tmpout;
}
$results[] = $out;
}
$output = '';
for ($i = 0; $i < 32; $i++) {
foreach ($results as $result) {
$output.= $result[$i];
}
}
return substr($output, 0, $keylen);
}













function _expand0state($key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p)
{




$p = array(
$p[0] ^ $key[0],
$p[1] ^ $key[1],
$p[2] ^ $key[2],
$p[3] ^ $key[3],
$p[4] ^ $key[4],
$p[5] ^ $key[5],
$p[6] ^ $key[6],
$p[7] ^ $key[7],
$p[8] ^ $key[8],
$p[9] ^ $key[9],
$p[10] ^ $key[10],
$p[11] ^ $key[11],
$p[12] ^ $key[12],
$p[13] ^ $key[13],
$p[14] ^ $key[14],
$p[15] ^ $key[15],
$p[16] ^ $key[0],
$p[17] ^ $key[1]
);


list( $p[0], $p[1]) = $this->_encryptBlockHelperFast( 0, 0, $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($p[ 0], $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($p[ 2], $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($p[ 4], $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($p[ 6], $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[10], $p[11]) = $this->_encryptBlockHelperFast($p[ 8], $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[12], $p[13]) = $this->_encryptBlockHelperFast($p[10], $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[14], $p[15]) = $this->_encryptBlockHelperFast($p[12], $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[16], $p[17]) = $this->_encryptBlockHelperFast($p[14], $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);


list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($p[16], $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2; $i < 256; $i+= 2) {
list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($sbox0[$i - 2], $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($sbox0[254], $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2; $i < 256; $i+= 2) {
list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($sbox1[$i - 2], $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($sbox1[254], $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2; $i < 256; $i+= 2) {
list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($sbox2[$i - 2], $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($sbox2[254], $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2; $i < 256; $i+= 2) {
list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($sbox3[$i - 2], $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}
}














function _expandstate($data, $key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p)
{
$p = array(
$p[0] ^ $key[0],
$p[1] ^ $key[1],
$p[2] ^ $key[2],
$p[3] ^ $key[3],
$p[4] ^ $key[4],
$p[5] ^ $key[5],
$p[6] ^ $key[6],
$p[7] ^ $key[7],
$p[8] ^ $key[8],
$p[9] ^ $key[9],
$p[10] ^ $key[10],
$p[11] ^ $key[11],
$p[12] ^ $key[12],
$p[13] ^ $key[13],
$p[14] ^ $key[14],
$p[15] ^ $key[15],
$p[16] ^ $key[0],
$p[17] ^ $key[1]
);


list( $p[0], $p[1]) = $this->_encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[10], $p[11]) = $this->_encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[12], $p[13]) = $this->_encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[14], $p[15]) = $this->_encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
list($p[16], $p[17]) = $this->_encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);


list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { 
list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox0[$i - 2], $data[$j + 1] ^ $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox0[254], $data[3] ^ $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox1[$i - 2], $data[$j + 1] ^ $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox1[254], $data[3] ^ $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox2[$i - 2], $data[$j + 1] ^ $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}

list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox2[254], $data[3] ^ $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox3[$i - 2], $data[$j + 1] ^ $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
}
}








function _encryptBlock($in)
{
$p = $this->bctx["p"];

$sb_0 = $this->bctx["sb"][0];
$sb_1 = $this->bctx["sb"][1];
$sb_2 = $this->bctx["sb"][2];
$sb_3 = $this->bctx["sb"][3];

$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];

list($r, $l) = PHP_INT_SIZE === 8 ?
$this->_encryptBlockHelperFast($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p) :
$this->_encryptBlockHelperSlow($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p);

return pack("N*", $r, $l);
}














function _encryptBlockHelperFast($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p)
{
$x0 ^= $p[0];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
$x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
$x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];

return array($x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF);
}














function _encryptBlockHelperSlow($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p)
{

$x0^= $p[0];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
$x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
$x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];

return array($x1 ^ $p[17], $x0);
}








function _decryptBlock($in)
{
$p = $this->bctx["p"];
$sb_0 = $this->bctx["sb"][0];
$sb_1 = $this->bctx["sb"][1];
$sb_2 = $this->bctx["sb"][2];
$sb_3 = $this->bctx["sb"][3];

$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];

for ($i = 17; $i > 2; $i-= 2) {
$l^= $p[$i];
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]);

$r^= $p[$i - 1];
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[0], $l ^ $p[1]);
}







function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();




$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);


$code_hash = "Crypt_Blowfish, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}

$safeint = $this->safe_intval_inline();

if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
$p = $this->bctx['p'];
$init_crypt = '
                        static $sb_0, $sb_1, $sb_2, $sb_3;
                        if (!$sb_0) {
                            $sb_0 = $self->bctx["sb"][0];
                            $sb_1 = $self->bctx["sb"][1];
                            $sb_2 = $self->bctx["sb"][2];
                            $sb_3 = $self->bctx["sb"][3];
                        }
                    ';
break;
default:
$p = array();
for ($i = 0; $i < 18; ++$i) {
$p[] = '$p_' . $i;
}
$init_crypt = '
                        list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"];
                        list(' . implode(',', $p) . ') = $self->bctx["p"];

                    ';
}


$encrypt_block = '
                $in = unpack("N*", $in);
                $l = $in[1];
                $r = $in[2];
            ';
for ($i = 0; $i < 16; $i+= 2) {
$encrypt_block.= '
                    $l^= ' . $p[$i] . ';
                    $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
                          $sb_2[$l >>  8 & 0xff]) +
                          $sb_3[$l       & 0xff]') . ';

                    $r^= ' . $p[$i + 1] . ';
                    $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . '  ^
                          $sb_2[$r >>  8 & 0xff]) +
                          $sb_3[$r       & 0xff]') . ';
                ';
}
$encrypt_block.= '
                $in = pack("N*",
                    $r ^ ' . $p[17] . ',
                    $l ^ ' . $p[16] . '
                );
            ';


$decrypt_block = '
                $in = unpack("N*", $in);
                $l = $in[1];
                $r = $in[2];
            ';

for ($i = 17; $i > 2; $i-= 2) {
$decrypt_block.= '
                    $l^= ' . $p[$i] . ';
                    $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
                          $sb_2[$l >>  8 & 0xff]) +
                          $sb_3[$l       & 0xff]') . ';

                    $r^= ' . $p[$i - 1] . ';
                    $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
                          $sb_2[$r >>  8 & 0xff]) +
                          $sb_3[$r       & 0xff]') . ';
                ';
}

$decrypt_block.= '
                $in = pack("N*",
                    $r ^ ' . $p[0] . ',
                    $l ^ ' . $p[1] . '
                );
            ';

$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
<?php





































namespace phpseclib\Crypt;

use phpseclib\Math\BigInteger;








class Hash
{







const MODE_INTERNAL = 1;



const MODE_MHASH = 2;



const MODE_HASH = 3;









var $hashParam;








var $b;








var $l = false;








var $hash;








var $key = false;








var $computedKey = false;








var $opad;








var $ipad;








var $engine;








function __construct($hash = 'sha1')
{
if (!defined('CRYPT_HASH_MODE')) {
switch (true) {
case extension_loaded('hash'):
define('CRYPT_HASH_MODE', self::MODE_HASH);
break;
case extension_loaded('mhash'):
define('CRYPT_HASH_MODE', self::MODE_MHASH);
break;
default:
define('CRYPT_HASH_MODE', self::MODE_INTERNAL);
}
}

$this->setHash($hash);
}









function setKey($key = false)
{
$this->key = $key;
$this->_computeKey();
}













function _computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}

if (strlen($this->key) <= $this->b) {
$this->computedKey = $this->key;
return;
}

switch ($this->engine) {
case self::MODE_MHASH:
$this->computedKey = mhash($this->hash, $this->key);
break;
case self::MODE_HASH:
$this->computedKey = hash($this->hash, $this->key, true);
break;
case self::MODE_INTERNAL:
$this->computedKey = call_user_func($this->hash, $this->key);
}
}









function getHash()
{
return $this->hashParam;
}







function setHash($hash)
{
$this->hashParam = $hash = strtolower($hash);
switch ($hash) {
case 'md5-96':
case 'sha1-96':
case 'sha256-96':
case 'sha512-96':
$hash = substr($hash, 0, -3);
$this->l = 12; 
break;
case 'md2':
case 'md5':
$this->l = 16;
break;
case 'sha1':
$this->l = 20;
break;
case 'sha256':
$this->l = 32;
break;
case 'sha384':
$this->l = 48;
break;
case 'sha512':
$this->l = 64;
}

switch ($hash) {
case 'md2-96':
case 'md2':
$this->b = 16;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->b = 64;
break;
default:
$this->b = 128;
}

switch ($hash) {
case 'md2':
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$this->engine = CRYPT_HASH_MODE;
}

switch ($this->engine) {
case self::MODE_MHASH:
switch ($hash) {
case 'md5':
$this->hash = MHASH_MD5;
break;
case 'sha256':
$this->hash = MHASH_SHA256;
break;
case 'sha1':
default:
$this->hash = MHASH_SHA1;
}
$this->_computeKey(self::MODE_MHASH);
return;
case self::MODE_HASH:
switch ($hash) {
case 'md5':
$this->hash = 'md5';
return;
case 'md2':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = $hash;
return;
case 'sha1':
default:
$this->hash = 'sha1';
}
$this->_computeKey(self::MODE_HASH);
return;
}

switch ($hash) {
case 'md2':
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->hash = array($this, '_sha512');
break;
case 'sha1':
default:
$this->hash = array($this, '_sha1');
}

$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);

$this->_computeKey(self::MODE_INTERNAL);
}








function hash($text)
{
if (!empty($this->key) || is_string($this->key)) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text, $this->computedKey);
break;
case self::MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->computedKey, true);
break;
case self::MODE_INTERNAL:
$key = str_pad($this->computedKey, $this->b, chr(0)); 
$temp = $this->ipad ^ $key; 
$temp .= $text; 
$temp = call_user_func($this->hash, $temp); 
$output = $this->opad ^ $key; 
$output.= $temp; 
$output = call_user_func($this->hash, $output); 
}
} else {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text);
break;
case self::MODE_HASH:
$output = hash($this->hash, $text, true);
break;
case self::MODE_INTERNAL:
$output = call_user_func($this->hash, $text);
}
}

return substr($output, 0, $this->l);
}







function getLength()
{
return $this->l;
}







function _md5($m)
{
return pack('H*', md5($m));
}







function _sha1($m)
{
return pack('H*', sha1($m));
}









function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);


$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);

$length = strlen($m);


$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {



$c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
$l = $c[$j];
}
}
$m.= $c;

$length+= 16;


$x = str_repeat(chr(0), 48);


for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$x[$j + 16] = $m[$i + $j];
$x[$j + 32] = $x[$j + 16] ^ $x[$j];
}
$t = chr(0);
for ($j = 0; $j < 18; $j++) {
for ($k = 0; $k < 48; $k++) {
$x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);

}
$t = chr(ord($t) + $j);
}
}


return substr($x, 0, 16);
}









function _sha256($m)
{
if (extension_loaded('suhosin')) {
return pack('H*', sha256($m));
}


$hash = array(
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
);


static $k = array(
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
);


$length = strlen($m);

$m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
$m[$length] = chr(0x80);

$m.= pack('N2', 0, $length << 3);


$chunks = str_split($m, 64);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
$w[] = $temp;
}


for ($i = 16; $i < 64; $i++) {

$s0 = $this->_rightRotate($w[$i - 15], 7) ^
$this->_rightRotate($w[$i - 15], 18) ^
$this->_rightShift( $w[$i - 15], 3);
$s1 = $this->_rightRotate($w[$i - 2], 17) ^
$this->_rightRotate($w[$i - 2], 19) ^
$this->_rightShift( $w[$i - 2], 10);

$w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
}


list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;


for ($i = 0; $i < 64; $i++) {
$s0 = $this->_rightRotate($a, 2) ^
$this->_rightRotate($a, 13) ^
$this->_rightRotate($a, 22);
$maj = ($a & $b) ^
($a & $c) ^
($b & $c);
$t2 = $this->_add($s0, $maj);

$s1 = $this->_rightRotate($e, 6) ^
$this->_rightRotate($e, 11) ^
$this->_rightRotate($e, 25);
$ch = ($e & $f) ^
($this->_not($e) & $g);
$t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);

$h = $g;
$g = $f;
$f = $e;
$e = $this->_add($d, $t1);
$d = $c;
$c = $b;
$b = $a;
$a = $this->_add($t1, $t2);
}


$hash = array(
$this->_add($hash[0], $a),
$this->_add($hash[1], $b),
$this->_add($hash[2], $c),
$this->_add($hash[3], $d),
$this->_add($hash[4], $e),
$this->_add($hash[5], $f),
$this->_add($hash[6], $g),
$this->_add($hash[7], $h)
);
}


return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
}







function _sha512($m)
{
static $init384, $init512, $k;

if (!isset($k)) {

$init384 = array( 
'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
'67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
);
$init512 = array( 
'6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
'510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
);

for ($i = 0; $i < 8; $i++) {
$init384[$i] = new BigInteger($init384[$i], 16);
$init384[$i]->setPrecision(64);
$init512[$i] = new BigInteger($init512[$i], 16);
$init512[$i]->setPrecision(64);
}



$k = array(
'428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
'72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
'2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
'983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
'27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
'650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
'19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
'391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
'748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
'90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
'06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
);

for ($i = 0; $i < 80; $i++) {
$k[$i] = new BigInteger($k[$i], 16);
}
}

$hash = $this->l == 48 ? $init384 : $init512;


$length = strlen($m);

$m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);

$m.= pack('N4', 0, 0, 0, $length << 3);


$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
$temp = new BigInteger($this->_string_shift($chunk, 8), 256);
$temp->setPrecision(64);
$w[] = $temp;
}


for ($i = 16; $i < 80; $i++) {
$temp = array(
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$w[$i] = $w[$i - 16]->copy();
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}


$a = $hash[0]->copy();
$b = $hash[1]->copy();
$c = $hash[2]->copy();
$d = $hash[3]->copy();
$e = $hash[4]->copy();
$f = $hash[5]->copy();
$g = $hash[6]->copy();
$h = $hash[7]->copy();


for ($i = 0; $i < 80; $i++) {
$temp = array(
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
);
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);

$temp = array(
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$temp = array(
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
);
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);

$h = $g->copy();
$g = $f->copy();
$f = $e->copy();
$e = $d->add($t1);
$d = $c->copy();
$c = $b->copy();
$b = $a->copy();
$a = $t1->add($t2);
}


$hash = array(
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
$hash[3]->add($d),
$hash[4]->add($e),
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
);
}



$temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}

return $temp;
}










function _rightRotate($int, $amt)
{
$invamt = 32 - $amt;
$mask = (1 << $invamt) - 1;
return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
}










function _rightShift($int, $amt)
{
$mask = (1 << (32 - $amt)) - 1;
return ($int >> $amt) & $mask;
}









function _not($int)
{
return ~$int & 0xFFFFFFFF;
}











function _add()
{
static $mod;
if (!isset($mod)) {
$mod = pow(2, 32);
}

$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument) {
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
}

if (function_exists('php_uname') && is_string(php_uname('m')) && (php_uname('m') & "\xDF\xDF\xDF") != 'ARM') {
return fmod($result, $mod);
}

return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($result / 0x80000000), 2) & 1) << 31);
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}
<?php
















































namespace phpseclib\Crypt;








class AES extends Rijndael
{









function setBlockLength($length)
{
return;
}











function setKeyLength($length)
{
parent::setKeyLength($length);
switch ($this->key_length) {
case 20:
$this->key_length = 24;
break;
case 28:
$this->key_length = 32;
}
}
}
<?php









































namespace phpseclib\Crypt;








class DES extends Base
{








const ENCRYPT = 0;



const DECRYPT = 1;









var $block_size = 8;








var $key_length = 8;








var $cipher_name_mcrypt = 'des';








var $openssl_mode_names = array(
self::MODE_ECB => 'des-ecb',
self::MODE_CBC => 'des-cbc',
self::MODE_CFB => 'des-cfb',
self::MODE_OFB => 'des-ofb'

);








var $cfb_init_len = 500;











var $des_rounds = 1;








var $key_length_max = 8;








var $keys;













var $shuffle = array(
"\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF",
"\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF",
"\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF",
"\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF",
"\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF",
"\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF",
"\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF",
"\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
"\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF",
"\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF",
"\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF",
"\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF",
"\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF",
"\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF",
"\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF",
"\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF",
"\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF",
"\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF",
"\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF",
"\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF",
"\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF",
"\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF",
"\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF",
"\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF",
"\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF",
"\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF",
"\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF",
"\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF",
"\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF",
"\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF",
"\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF",
"\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
"\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF",
"\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF",
"\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF",
"\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF",
"\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF",
"\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF",
"\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF",
"\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF",
"\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF",
"\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF",
"\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF",
"\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF",
"\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF",
"\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF",
"\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF",
"\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF",
"\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF",
"\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF",
"\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF",
"\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF",
"\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF",
"\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF",
"\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF",
"\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF",
"\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF",
"\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF",
"\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF",
"\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF",
"\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF",
"\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
"\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF",
"\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF",
"\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF",
"\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF",
"\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF",
"\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF",
"\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF",
"\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF",
"\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF",
"\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF",
"\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF",
"\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF",
"\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF",
"\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF",
"\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF",
"\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF",
"\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF",
"\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF",
"\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF",
"\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF",
"\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF",
"\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF",
"\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF",
"\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF",
"\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF",
"\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF",
"\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF",
"\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF",
"\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF",
"\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF",
"\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF",
"\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF",
"\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF",
"\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF",
"\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF",
"\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF",
"\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF",
"\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF",
"\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF",
"\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF",
"\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF",
"\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF",
"\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF",
"\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF",
"\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF",
"\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF",
"\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF",
"\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF",
"\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF",
"\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF",
"\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF",
"\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
);









var $ipmap = array(
0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31,
0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33,
0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71,
0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73,
0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35,
0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37,
0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75,
0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77,
0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1,
0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3,
0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1,
0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3,
0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5,
0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7,
0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5,
0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7,
0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39,
0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B,
0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79,
0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B,
0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D,
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D,
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9,
0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB,
0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9,
0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB,
0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD,
0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF,
0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD,
0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF
);








var $invipmap = array(
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
);










var $sbox1 = array(
0x00808200, 0x00000000, 0x00008000, 0x00808202,
0x00808002, 0x00008202, 0x00000002, 0x00008000,
0x00000200, 0x00808200, 0x00808202, 0x00000200,
0x00800202, 0x00808002, 0x00800000, 0x00000002,
0x00000202, 0x00800200, 0x00800200, 0x00008200,
0x00008200, 0x00808000, 0x00808000, 0x00800202,
0x00008002, 0x00800002, 0x00800002, 0x00008002,
0x00000000, 0x00000202, 0x00008202, 0x00800000,
0x00008000, 0x00808202, 0x00000002, 0x00808000,
0x00808200, 0x00800000, 0x00800000, 0x00000200,
0x00808002, 0x00008000, 0x00008200, 0x00800002,
0x00000200, 0x00000002, 0x00800202, 0x00008202,
0x00808202, 0x00008002, 0x00808000, 0x00800202,
0x00800002, 0x00000202, 0x00008202, 0x00808200,
0x00000202, 0x00800200, 0x00800200, 0x00000000,
0x00008002, 0x00008200, 0x00000000, 0x00808002
);







var $sbox2 = array(
0x40084010, 0x40004000, 0x00004000, 0x00084010,
0x00080000, 0x00000010, 0x40080010, 0x40004010,
0x40000010, 0x40084010, 0x40084000, 0x40000000,
0x40004000, 0x00080000, 0x00000010, 0x40080010,
0x00084000, 0x00080010, 0x40004010, 0x00000000,
0x40000000, 0x00004000, 0x00084010, 0x40080000,
0x00080010, 0x40000010, 0x00000000, 0x00084000,
0x00004010, 0x40084000, 0x40080000, 0x00004010,
0x00000000, 0x00084010, 0x40080010, 0x00080000,
0x40004010, 0x40080000, 0x40084000, 0x00004000,
0x40080000, 0x40004000, 0x00000010, 0x40084010,
0x00084010, 0x00000010, 0x00004000, 0x40000000,
0x00004010, 0x40084000, 0x00080000, 0x40000010,
0x00080010, 0x40004010, 0x40000010, 0x00080010,
0x00084000, 0x00000000, 0x40004000, 0x00004010,
0x40000000, 0x40080010, 0x40084010, 0x00084000
);







var $sbox3 = array(
0x00000104, 0x04010100, 0x00000000, 0x04010004,
0x04000100, 0x00000000, 0x00010104, 0x04000100,
0x00010004, 0x04000004, 0x04000004, 0x00010000,
0x04010104, 0x00010004, 0x04010000, 0x00000104,
0x04000000, 0x00000004, 0x04010100, 0x00000100,
0x00010100, 0x04010000, 0x04010004, 0x00010104,
0x04000104, 0x00010100, 0x00010000, 0x04000104,
0x00000004, 0x04010104, 0x00000100, 0x04000000,
0x04010100, 0x04000000, 0x00010004, 0x00000104,
0x00010000, 0x04010100, 0x04000100, 0x00000000,
0x00000100, 0x00010004, 0x04010104, 0x04000100,
0x04000004, 0x00000100, 0x00000000, 0x04010004,
0x04000104, 0x00010000, 0x04000000, 0x04010104,
0x00000004, 0x00010104, 0x00010100, 0x04000004,
0x04010000, 0x04000104, 0x00000104, 0x04010000,
0x00010104, 0x00000004, 0x04010004, 0x00010100
);







var $sbox4 = array(
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x00401040, 0x80400040, 0x80400000, 0x80001000,
0x00000000, 0x00401000, 0x00401000, 0x80401040,
0x80000040, 0x00000000, 0x00400040, 0x80400000,
0x80000000, 0x00001000, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x80001000, 0x00001040,
0x80400040, 0x80000000, 0x00001040, 0x00400040,
0x00001000, 0x00401040, 0x80401040, 0x80000040,
0x00400040, 0x80400000, 0x00401000, 0x80401040,
0x80000040, 0x00000000, 0x00000000, 0x00401000,
0x00001040, 0x00400040, 0x80400040, 0x80000000,
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x80401040, 0x80000040, 0x80000000, 0x00001000,
0x80400000, 0x80001000, 0x00401040, 0x80400040,
0x80001000, 0x00001040, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x00001000, 0x00401040
);







var $sbox5 = array(
0x00000080, 0x01040080, 0x01040000, 0x21000080,
0x00040000, 0x00000080, 0x20000000, 0x01040000,
0x20040080, 0x00040000, 0x01000080, 0x20040080,
0x21000080, 0x21040000, 0x00040080, 0x20000000,
0x01000000, 0x20040000, 0x20040000, 0x00000000,
0x20000080, 0x21040080, 0x21040080, 0x01000080,
0x21040000, 0x20000080, 0x00000000, 0x21000000,
0x01040080, 0x01000000, 0x21000000, 0x00040080,
0x00040000, 0x21000080, 0x00000080, 0x01000000,
0x20000000, 0x01040000, 0x21000080, 0x20040080,
0x01000080, 0x20000000, 0x21040000, 0x01040080,
0x20040080, 0x00000080, 0x01000000, 0x21040000,
0x21040080, 0x00040080, 0x21000000, 0x21040080,
0x01040000, 0x00000000, 0x20040000, 0x21000000,
0x00040080, 0x01000080, 0x20000080, 0x00040000,
0x00000000, 0x20040000, 0x01040080, 0x20000080
);







var $sbox6 = array(
0x10000008, 0x10200000, 0x00002000, 0x10202008,
0x10200000, 0x00000008, 0x10202008, 0x00200000,
0x10002000, 0x00202008, 0x00200000, 0x10000008,
0x00200008, 0x10002000, 0x10000000, 0x00002008,
0x00000000, 0x00200008, 0x10002008, 0x00002000,
0x00202000, 0x10002008, 0x00000008, 0x10200008,
0x10200008, 0x00000000, 0x00202008, 0x10202000,
0x00002008, 0x00202000, 0x10202000, 0x10000000,
0x10002000, 0x00000008, 0x10200008, 0x00202000,
0x10202008, 0x00200000, 0x00002008, 0x10000008,
0x00200000, 0x10002000, 0x10000000, 0x00002008,
0x10000008, 0x10202008, 0x00202000, 0x10200000,
0x00202008, 0x10202000, 0x00000000, 0x10200008,
0x00000008, 0x00002000, 0x10200000, 0x00202008,
0x00002000, 0x00200008, 0x10002008, 0x00000000,
0x10202000, 0x10000000, 0x00200008, 0x10002008
);







var $sbox7 = array(
0x00100000, 0x02100001, 0x02000401, 0x00000000,
0x00000400, 0x02000401, 0x00100401, 0x02100400,
0x02100401, 0x00100000, 0x00000000, 0x02000001,
0x00000001, 0x02000000, 0x02100001, 0x00000401,
0x02000400, 0x00100401, 0x00100001, 0x02000400,
0x02000001, 0x02100000, 0x02100400, 0x00100001,
0x02100000, 0x00000400, 0x00000401, 0x02100401,
0x00100400, 0x00000001, 0x02000000, 0x00100400,
0x02000000, 0x00100400, 0x00100000, 0x02000401,
0x02000401, 0x02100001, 0x02100001, 0x00000001,
0x00100001, 0x02000000, 0x02000400, 0x00100000,
0x02100400, 0x00000401, 0x00100401, 0x02100400,
0x00000401, 0x02000001, 0x02100401, 0x02100000,
0x00100400, 0x00000000, 0x00000001, 0x02100401,
0x00000000, 0x00100401, 0x02100000, 0x00000400,
0x02000001, 0x02000400, 0x00000400, 0x00100001
);







var $sbox8 = array(
0x08000820, 0x00000800, 0x00020000, 0x08020820,
0x08000000, 0x08000820, 0x00000020, 0x08000000,
0x00020020, 0x08020000, 0x08020820, 0x00020800,
0x08020800, 0x00020820, 0x00000800, 0x00000020,
0x08020000, 0x08000020, 0x08000800, 0x00000820,
0x00020800, 0x00020020, 0x08020020, 0x08020800,
0x00000820, 0x00000000, 0x00000000, 0x08020020,
0x08000020, 0x08000800, 0x00020820, 0x00020000,
0x00020820, 0x00020000, 0x08020800, 0x00000800,
0x00000020, 0x08020020, 0x00000800, 0x00020820,
0x08000800, 0x00000020, 0x08000020, 0x08020000,
0x08020020, 0x08000000, 0x00020000, 0x08000820,
0x00000000, 0x08020820, 0x00020020, 0x08000020,
0x08020000, 0x08000800, 0x08000820, 0x00000000,
0x08020820, 0x00020800, 0x00020800, 0x00000820,
0x00000820, 0x00020020, 0x08000000, 0x08020800
);











function isValidEngine($engine)
{
if ($this->key_length_max == 8) {
if ($engine == self::ENGINE_OPENSSL) {



if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
$this->cipher_name_openssl_ecb = 'des-ecb';
$this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode();
}
}

return parent::isValidEngine($engine);
}
















function setKey($key)
{


if (strlen($key) > $this->key_length_max) {
$key = substr($key, 0, $this->key_length_max);
}


parent::setKey($key);
}











function _encryptBlock($in)
{
return $this->_processBlock($in, self::ENCRYPT);
}











function _decryptBlock($in)
{
return $this->_processBlock($in, self::DECRYPT);
}















function _processBlock($block, $mode)
{
static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map("intval", $this->sbox1);
$sbox2 = array_map("intval", $this->sbox2);
$sbox3 = array_map("intval", $this->sbox3);
$sbox4 = array_map("intval", $this->sbox4);
$sbox5 = array_map("intval", $this->sbox5);
$sbox6 = array_map("intval", $this->sbox6);
$sbox7 = array_map("intval", $this->sbox7);
$sbox8 = array_map("intval", $this->sbox8);

for ($i = 0; $i < 256; ++$i) {
$shuffleip[] = $this->shuffle[$this->ipmap[$i]];
$shuffleinvip[] = $this->shuffle[$this->invipmap[$i]];
}
}

$keys = $this->keys[$mode];
$ki = -1;


$t = unpack('Nl/Nr', $block);
list($l, $r) = array($t['l'], $t['r']);
$block = ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");


$t = unpack('Nl/Nr', $block);
list($l, $r) = array($t['l'], $t['r']);

for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {

for ($i = 0; $i < 16; $i++) {



$b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki];
$b2 = (($r >> 31) & 0x00000001) ^ ($r << 1) ^ $keys[++$ki];


$t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
$sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
$sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^
$sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ $l;


$l = $r;
$r = $t;
}


$t = $l;
$l = $r;
$r = $t;
}


return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
($shuffleinvip[($r >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
($shuffleinvip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
($shuffleinvip[ $r & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
($shuffleinvip[ $l & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
}







function _setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) {

return;
}
$this->kl = array('key' => $this->key, 'des_rounds' => $this->des_rounds);

static $shifts = array( 
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
);

static $pc1map = array(
0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C,
0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E,
0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C,
0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E,
0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C,
0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E,
0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C,
0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E,
0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C,
0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E,
0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C,
0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E,
0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C,
0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E,
0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C,
0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E,
0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C,
0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E,
0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C,
0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E,
0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC,
0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE,
0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC,
0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE,
0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC,
0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE,
0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC,
0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE,
0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC,
0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE,
0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC,
0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE
);


static $pc2mapc1 = array(
0x00000000, 0x00000400, 0x00200000, 0x00200400,
0x00000001, 0x00000401, 0x00200001, 0x00200401,
0x02000000, 0x02000400, 0x02200000, 0x02200400,
0x02000001, 0x02000401, 0x02200001, 0x02200401
);
static $pc2mapc2 = array(
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000100, 0x00000900, 0x08000100, 0x08000900,
0x00010100, 0x00010900, 0x08010100, 0x08010900,
0x00000100, 0x00000900, 0x08000100, 0x08000900,
0x00010100, 0x00010900, 0x08010100, 0x08010900,
0x00000010, 0x00000810, 0x08000010, 0x08000810,
0x00010010, 0x00010810, 0x08010010, 0x08010810,
0x00000010, 0x00000810, 0x08000010, 0x08000810,
0x00010010, 0x00010810, 0x08010010, 0x08010810,
0x00000110, 0x00000910, 0x08000110, 0x08000910,
0x00010110, 0x00010910, 0x08010110, 0x08010910,
0x00000110, 0x00000910, 0x08000110, 0x08000910,
0x00010110, 0x00010910, 0x08010110, 0x08010910,
0x00040000, 0x00040800, 0x08040000, 0x08040800,
0x00050000, 0x00050800, 0x08050000, 0x08050800,
0x00040000, 0x00040800, 0x08040000, 0x08040800,
0x00050000, 0x00050800, 0x08050000, 0x08050800,
0x00040100, 0x00040900, 0x08040100, 0x08040900,
0x00050100, 0x00050900, 0x08050100, 0x08050900,
0x00040100, 0x00040900, 0x08040100, 0x08040900,
0x00050100, 0x00050900, 0x08050100, 0x08050900,
0x00040010, 0x00040810, 0x08040010, 0x08040810,
0x00050010, 0x00050810, 0x08050010, 0x08050810,
0x00040010, 0x00040810, 0x08040010, 0x08040810,
0x00050010, 0x00050810, 0x08050010, 0x08050810,
0x00040110, 0x00040910, 0x08040110, 0x08040910,
0x00050110, 0x00050910, 0x08050110, 0x08050910,
0x00040110, 0x00040910, 0x08040110, 0x08040910,
0x00050110, 0x00050910, 0x08050110, 0x08050910,
0x01000000, 0x01000800, 0x09000000, 0x09000800,
0x01010000, 0x01010800, 0x09010000, 0x09010800,
0x01000000, 0x01000800, 0x09000000, 0x09000800,
0x01010000, 0x01010800, 0x09010000, 0x09010800,
0x01000100, 0x01000900, 0x09000100, 0x09000900,
0x01010100, 0x01010900, 0x09010100, 0x09010900,
0x01000100, 0x01000900, 0x09000100, 0x09000900,
0x01010100, 0x01010900, 0x09010100, 0x09010900,
0x01000010, 0x01000810, 0x09000010, 0x09000810,
0x01010010, 0x01010810, 0x09010010, 0x09010810,
0x01000010, 0x01000810, 0x09000010, 0x09000810,
0x01010010, 0x01010810, 0x09010010, 0x09010810,
0x01000110, 0x01000910, 0x09000110, 0x09000910,
0x01010110, 0x01010910, 0x09010110, 0x09010910,
0x01000110, 0x01000910, 0x09000110, 0x09000910,
0x01010110, 0x01010910, 0x09010110, 0x09010910,
0x01040000, 0x01040800, 0x09040000, 0x09040800,
0x01050000, 0x01050800, 0x09050000, 0x09050800,
0x01040000, 0x01040800, 0x09040000, 0x09040800,
0x01050000, 0x01050800, 0x09050000, 0x09050800,
0x01040100, 0x01040900, 0x09040100, 0x09040900,
0x01050100, 0x01050900, 0x09050100, 0x09050900,
0x01040100, 0x01040900, 0x09040100, 0x09040900,
0x01050100, 0x01050900, 0x09050100, 0x09050900,
0x01040010, 0x01040810, 0x09040010, 0x09040810,
0x01050010, 0x01050810, 0x09050010, 0x09050810,
0x01040010, 0x01040810, 0x09040010, 0x09040810,
0x01050010, 0x01050810, 0x09050010, 0x09050810,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910
);
static $pc2mapc3 = array(
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
0x00000020, 0x00000024, 0x00001020, 0x00001024,
0x00000020, 0x00000024, 0x00001020, 0x00001024,
0x10000020, 0x10000024, 0x10001020, 0x10001024,
0x10000020, 0x10000024, 0x10001020, 0x10001024,
0x00080000, 0x00080004, 0x00081000, 0x00081004,
0x00080000, 0x00080004, 0x00081000, 0x00081004,
0x10080000, 0x10080004, 0x10081000, 0x10081004,
0x10080000, 0x10080004, 0x10081000, 0x10081004,
0x00080020, 0x00080024, 0x00081020, 0x00081024,
0x00080020, 0x00080024, 0x00081020, 0x00081024,
0x10080020, 0x10080024, 0x10081020, 0x10081024,
0x10080020, 0x10080024, 0x10081020, 0x10081024,
0x20000000, 0x20000004, 0x20001000, 0x20001004,
0x20000000, 0x20000004, 0x20001000, 0x20001004,
0x30000000, 0x30000004, 0x30001000, 0x30001004,
0x30000000, 0x30000004, 0x30001000, 0x30001004,
0x20000020, 0x20000024, 0x20001020, 0x20001024,
0x20000020, 0x20000024, 0x20001020, 0x20001024,
0x30000020, 0x30000024, 0x30001020, 0x30001024,
0x30000020, 0x30000024, 0x30001020, 0x30001024,
0x20080000, 0x20080004, 0x20081000, 0x20081004,
0x20080000, 0x20080004, 0x20081000, 0x20081004,
0x30080000, 0x30080004, 0x30081000, 0x30081004,
0x30080000, 0x30080004, 0x30081000, 0x30081004,
0x20080020, 0x20080024, 0x20081020, 0x20081024,
0x20080020, 0x20080024, 0x20081020, 0x20081024,
0x30080020, 0x30080024, 0x30081020, 0x30081024,
0x30080020, 0x30080024, 0x30081020, 0x30081024,
0x00000002, 0x00000006, 0x00001002, 0x00001006,
0x00000002, 0x00000006, 0x00001002, 0x00001006,
0x10000002, 0x10000006, 0x10001002, 0x10001006,
0x10000002, 0x10000006, 0x10001002, 0x10001006,
0x00000022, 0x00000026, 0x00001022, 0x00001026,
0x00000022, 0x00000026, 0x00001022, 0x00001026,
0x10000022, 0x10000026, 0x10001022, 0x10001026,
0x10000022, 0x10000026, 0x10001022, 0x10001026,
0x00080002, 0x00080006, 0x00081002, 0x00081006,
0x00080002, 0x00080006, 0x00081002, 0x00081006,
0x10080002, 0x10080006, 0x10081002, 0x10081006,
0x10080002, 0x10080006, 0x10081002, 0x10081006,
0x00080022, 0x00080026, 0x00081022, 0x00081026,
0x00080022, 0x00080026, 0x00081022, 0x00081026,
0x10080022, 0x10080026, 0x10081022, 0x10081026,
0x10080022, 0x10080026, 0x10081022, 0x10081026,
0x20000002, 0x20000006, 0x20001002, 0x20001006,
0x20000002, 0x20000006, 0x20001002, 0x20001006,
0x30000002, 0x30000006, 0x30001002, 0x30001006,
0x30000002, 0x30000006, 0x30001002, 0x30001006,
0x20000022, 0x20000026, 0x20001022, 0x20001026,
0x20000022, 0x20000026, 0x20001022, 0x20001026,
0x30000022, 0x30000026, 0x30001022, 0x30001026,
0x30000022, 0x30000026, 0x30001022, 0x30001026,
0x20080002, 0x20080006, 0x20081002, 0x20081006,
0x20080002, 0x20080006, 0x20081002, 0x20081006,
0x30080002, 0x30080006, 0x30081002, 0x30081006,
0x30080002, 0x30080006, 0x30081002, 0x30081006,
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026
);
static $pc2mapc4 = array(
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x04000000, 0x04100000, 0x04000008, 0x04100008,
0x04000200, 0x04100200, 0x04000208, 0x04100208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x00002000, 0x00102000, 0x00002008, 0x00102008,
0x00002200, 0x00102200, 0x00002208, 0x00102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x04002000, 0x04102000, 0x04002008, 0x04102008,
0x04002200, 0x04102200, 0x04002208, 0x04102208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x00020000, 0x00120000, 0x00020008, 0x00120008,
0x00020200, 0x00120200, 0x00020208, 0x00120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x04020000, 0x04120000, 0x04020008, 0x04120008,
0x04020200, 0x04120200, 0x04020208, 0x04120208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x00022000, 0x00122000, 0x00022008, 0x00122008,
0x00022200, 0x00122200, 0x00022208, 0x00122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208
);
static $pc2mapd1 = array(
0x00000000, 0x00000001, 0x08000000, 0x08000001,
0x00200000, 0x00200001, 0x08200000, 0x08200001,
0x00000002, 0x00000003, 0x08000002, 0x08000003,
0x00200002, 0x00200003, 0x08200002, 0x08200003
);
static $pc2mapd2 = array(
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x00000004, 0x00100004, 0x00000804, 0x00100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x04000004, 0x04100004, 0x04000804, 0x04100804,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x00020000, 0x00120000, 0x00020800, 0x00120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x04020000, 0x04120000, 0x04020800, 0x04120800,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x00020004, 0x00120004, 0x00020804, 0x00120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x04020004, 0x04120004, 0x04020804, 0x04120804,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04
);
static $pc2mapd3 = array(
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
0x00040020, 0x00050020, 0x02040020, 0x02050020,
0x00002000, 0x00012000, 0x02002000, 0x02012000,
0x00002020, 0x00012020, 0x02002020, 0x02012020,
0x00042000, 0x00052000, 0x02042000, 0x02052000,
0x00042020, 0x00052020, 0x02042020, 0x02052020,
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
0x00040020, 0x00050020, 0x02040020, 0x02050020,
0x00002000, 0x00012000, 0x02002000, 0x02012000,
0x00002020, 0x00012020, 0x02002020, 0x02012020,
0x00042000, 0x00052000, 0x02042000, 0x02052000,
0x00042020, 0x00052020, 0x02042020, 0x02052020,
0x00000010, 0x00010010, 0x02000010, 0x02010010,
0x00000030, 0x00010030, 0x02000030, 0x02010030,
0x00040010, 0x00050010, 0x02040010, 0x02050010,
0x00040030, 0x00050030, 0x02040030, 0x02050030,
0x00002010, 0x00012010, 0x02002010, 0x02012010,
0x00002030, 0x00012030, 0x02002030, 0x02012030,
0x00042010, 0x00052010, 0x02042010, 0x02052010,
0x00042030, 0x00052030, 0x02042030, 0x02052030,
0x00000010, 0x00010010, 0x02000010, 0x02010010,
0x00000030, 0x00010030, 0x02000030, 0x02010030,
0x00040010, 0x00050010, 0x02040010, 0x02050010,
0x00040030, 0x00050030, 0x02040030, 0x02050030,
0x00002010, 0x00012010, 0x02002010, 0x02012010,
0x00002030, 0x00012030, 0x02002030, 0x02012030,
0x00042010, 0x00052010, 0x02042010, 0x02052010,
0x00042030, 0x00052030, 0x02042030, 0x02052030,
0x20000000, 0x20010000, 0x22000000, 0x22010000,
0x20000020, 0x20010020, 0x22000020, 0x22010020,
0x20040000, 0x20050000, 0x22040000, 0x22050000,
0x20040020, 0x20050020, 0x22040020, 0x22050020,
0x20002000, 0x20012000, 0x22002000, 0x22012000,
0x20002020, 0x20012020, 0x22002020, 0x22012020,
0x20042000, 0x20052000, 0x22042000, 0x22052000,
0x20042020, 0x20052020, 0x22042020, 0x22052020,
0x20000000, 0x20010000, 0x22000000, 0x22010000,
0x20000020, 0x20010020, 0x22000020, 0x22010020,
0x20040000, 0x20050000, 0x22040000, 0x22050000,
0x20040020, 0x20050020, 0x22040020, 0x22050020,
0x20002000, 0x20012000, 0x22002000, 0x22012000,
0x20002020, 0x20012020, 0x22002020, 0x22012020,
0x20042000, 0x20052000, 0x22042000, 0x22052000,
0x20042020, 0x20052020, 0x22042020, 0x22052020,
0x20000010, 0x20010010, 0x22000010, 0x22010010,
0x20000030, 0x20010030, 0x22000030, 0x22010030,
0x20040010, 0x20050010, 0x22040010, 0x22050010,
0x20040030, 0x20050030, 0x22040030, 0x22050030,
0x20002010, 0x20012010, 0x22002010, 0x22012010,
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030,
0x20000010, 0x20010010, 0x22000010, 0x22010010,
0x20000030, 0x20010030, 0x22000030, 0x22010030,
0x20040010, 0x20050010, 0x22040010, 0x22050010,
0x20040030, 0x20050030, 0x22040030, 0x22050030,
0x20002010, 0x20012010, 0x22002010, 0x22012010,
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030
);
static $pc2mapd4 = array(
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
0x10000000, 0x10000400, 0x11000000, 0x11000400,
0x10000000, 0x10000400, 0x11000000, 0x11000400,
0x10000100, 0x10000500, 0x11000100, 0x11000500,
0x10000100, 0x10000500, 0x11000100, 0x11000500,
0x00080000, 0x00080400, 0x01080000, 0x01080400,
0x00080000, 0x00080400, 0x01080000, 0x01080400,
0x00080100, 0x00080500, 0x01080100, 0x01080500,
0x00080100, 0x00080500, 0x01080100, 0x01080500,
0x10080000, 0x10080400, 0x11080000, 0x11080400,
0x10080000, 0x10080400, 0x11080000, 0x11080400,
0x10080100, 0x10080500, 0x11080100, 0x11080500,
0x10080100, 0x10080500, 0x11080100, 0x11080500,
0x00000008, 0x00000408, 0x01000008, 0x01000408,
0x00000008, 0x00000408, 0x01000008, 0x01000408,
0x00000108, 0x00000508, 0x01000108, 0x01000508,
0x00000108, 0x00000508, 0x01000108, 0x01000508,
0x10000008, 0x10000408, 0x11000008, 0x11000408,
0x10000008, 0x10000408, 0x11000008, 0x11000408,
0x10000108, 0x10000508, 0x11000108, 0x11000508,
0x10000108, 0x10000508, 0x11000108, 0x11000508,
0x00080008, 0x00080408, 0x01080008, 0x01080408,
0x00080008, 0x00080408, 0x01080008, 0x01080408,
0x00080108, 0x00080508, 0x01080108, 0x01080508,
0x00080108, 0x00080508, 0x01080108, 0x01080508,
0x10080008, 0x10080408, 0x11080008, 0x11080408,
0x10080008, 0x10080408, 0x11080008, 0x11080408,
0x10080108, 0x10080508, 0x11080108, 0x11080508,
0x10080108, 0x10080508, 0x11080108, 0x11080508,
0x00001000, 0x00001400, 0x01001000, 0x01001400,
0x00001000, 0x00001400, 0x01001000, 0x01001400,
0x00001100, 0x00001500, 0x01001100, 0x01001500,
0x00001100, 0x00001500, 0x01001100, 0x01001500,
0x10001000, 0x10001400, 0x11001000, 0x11001400,
0x10001000, 0x10001400, 0x11001000, 0x11001400,
0x10001100, 0x10001500, 0x11001100, 0x11001500,
0x10001100, 0x10001500, 0x11001100, 0x11001500,
0x00081000, 0x00081400, 0x01081000, 0x01081400,
0x00081000, 0x00081400, 0x01081000, 0x01081400,
0x00081100, 0x00081500, 0x01081100, 0x01081500,
0x00081100, 0x00081500, 0x01081100, 0x01081500,
0x10081000, 0x10081400, 0x11081000, 0x11081400,
0x10081000, 0x10081400, 0x11081000, 0x11081400,
0x10081100, 0x10081500, 0x11081100, 0x11081500,
0x10081100, 0x10081500, 0x11081100, 0x11081500,
0x00001008, 0x00001408, 0x01001008, 0x01001408,
0x00001008, 0x00001408, 0x01001008, 0x01001408,
0x00001108, 0x00001508, 0x01001108, 0x01001508,
0x00001108, 0x00001508, 0x01001108, 0x01001508,
0x10001008, 0x10001408, 0x11001008, 0x11001408,
0x10001008, 0x10001408, 0x11001008, 0x11001408,
0x10001108, 0x10001508, 0x11001108, 0x11001508,
0x10001108, 0x10001508, 0x11001108, 0x11001508,
0x00081008, 0x00081408, 0x01081008, 0x01081408,
0x00081008, 0x00081408, 0x01081008, 0x01081408,
0x00081108, 0x00081508, 0x01081108, 0x01081508,
0x00081108, 0x00081508, 0x01081108, 0x01081508,
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081108, 0x10081508, 0x11081108, 0x11081508,
0x10081108, 0x10081508, 0x11081108, 0x11081508
);

$keys = array();
for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {

$key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0");


$t = unpack('Nl/Nr', $key);
list($l, $r) = array($t['l'], $t['r']);
$key = ($this->shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") |
($this->shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") |
($this->shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") |
($this->shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") |
($this->shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") |
($this->shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") |
($this->shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") |
($this->shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00");
$key = unpack('Nc/Nd', $key);
$c = ( $key['c'] >> 4) & 0x0FFFFFFF;
$d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F);

$keys[$des_round] = array(
self::ENCRYPT => array(),
self::DECRYPT => array_fill(0, 32, 0)
);
for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) {
$c <<= $shifts[$i];
$c = ($c | ($c >> 28)) & 0x0FFFFFFF;
$d <<= $shifts[$i];
$d = ($d | ($d >> 28)) & 0x0FFFFFFF;


$cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c >> 16) & 0xFF] |
$pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[ $c & 0xFF];
$dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d >> 16) & 0xFF] |
$pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF];


$val1 = ( $cp & intval(0xFF000000)) | (($cp << 8) & 0x00FF0000) |
(($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF);
$val2 = (($cp << 8) & intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) |
(($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF);
$keys[$des_round][self::ENCRYPT][ ] = $val1;
$keys[$des_round][self::DECRYPT][$ki - 1] = $val1;
$keys[$des_round][self::ENCRYPT][ ] = $val2;
$keys[$des_round][self::DECRYPT][$ki ] = $val2;
}
}

switch ($this->des_rounds) {
case 3: 
$this->keys = array(
self::ENCRYPT => array_merge(
$keys[0][self::ENCRYPT],
$keys[1][self::DECRYPT],
$keys[2][self::ENCRYPT]
),
self::DECRYPT => array_merge(
$keys[2][self::DECRYPT],
$keys[1][self::ENCRYPT],
$keys[0][self::DECRYPT]
)
);
break;

default:
$this->keys = array(
self::ENCRYPT => $keys[0][self::ENCRYPT],
self::DECRYPT => $keys[0][self::DECRYPT]
);
}
}







function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();




$des_rounds = $this->des_rounds;





$gen_hi_opt_code = (bool)( count($lambda_functions) < 10 );


$code_hash = "Crypt_DES, $des_rounds, {$this->mode}";
if ($gen_hi_opt_code) {





$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}


if (!isset($lambda_functions[$code_hash])) {

$init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
                if (!$sbox1) {
                    $sbox1 = array_map("intval", $self->sbox1);
                    $sbox2 = array_map("intval", $self->sbox2);
                    $sbox3 = array_map("intval", $self->sbox3);
                    $sbox4 = array_map("intval", $self->sbox4);
                    $sbox5 = array_map("intval", $self->sbox5);
                    $sbox6 = array_map("intval", $self->sbox6);
                    $sbox7 = array_map("intval", $self->sbox7);
                    $sbox8 = array_map("intval", $self->sbox8);'
 . '
                    for ($i = 0; $i < 256; ++$i) {
                        $shuffleip[]    =  $self->shuffle[$self->ipmap[$i]];
                        $shuffleinvip[] =  $self->shuffle[$self->invipmap[$i]];
                    }
                }
            ';

switch (true) {
case $gen_hi_opt_code:



$k = array(
self::ENCRYPT => $this->keys[self::ENCRYPT],
self::DECRYPT => $this->keys[self::DECRYPT]
);
$init_encrypt = '';
$init_decrypt = '';
break;
default:


$k = array(
self::ENCRYPT => array(),
self::DECRYPT => array()
);
for ($i = 0, $c = count($this->keys[self::ENCRYPT]); $i < $c; ++$i) {
$k[self::ENCRYPT][$i] = '$ke[' . $i . ']';
$k[self::DECRYPT][$i] = '$kd[' . $i . ']';
}
$init_encrypt = '$ke = $self->keys[$self::ENCRYPT];';
$init_decrypt = '$kd = $self->keys[$self::DECRYPT];';
break;
}


$crypt_block = array();
foreach (array(self::ENCRYPT, self::DECRYPT) as $c) {

$crypt_block[$c] = '
                    $in = unpack("N*", $in);
                    $l  = $in[1];
                    $r  = $in[2];
                    $in = unpack("N*",
                        ($shuffleip[ $r        & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                        ($shuffleip[($r >>  8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                        ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                        ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                        ($shuffleip[ $l        & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                        ($shuffleip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                        ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                        ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01")
                    );
                    ' .  '
                    $l = $in[1];
                    $r = $in[2];
                ';

$l = '$l';
$r = '$r';


for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) {

for ($i = 0; $i < 16; ++$i) {



$crypt_block[$c].= '
                            $b1 = ((' . $r . ' >>  3) & 0x1FFFFFFF)  ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . ';
                            $b2 = ((' . $r . ' >> 31) & 0x00000001)  ^ (' . $r . ' <<  1) ^ ' . $k[$c][++$ki] . ';' .

$l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
                                     $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
                                     $sbox5[($b1 >>  8) & 0x3F] ^ $sbox6[($b2 >>  8) & 0x3F] ^
                                     $sbox7[ $b1        & 0x3F] ^ $sbox8[ $b2        & 0x3F] ^ ' . $l . ';
                        ';



list($l, $r) = array($r, $l);
}
list($l, $r) = array($r, $l);
}


$crypt_block[$c].= '$in =
                    ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                    ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                    ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                    ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                    ($shuffleinvip[($l >>  8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                    ($shuffleinvip[($r >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                    ($shuffleinvip[ $l        & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                    ($shuffleinvip[ $r        & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
                ';
}


$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => $init_encrypt,
'init_decrypt' => $init_decrypt,
'encrypt_block' => $crypt_block[self::ENCRYPT],
'decrypt_block' => $crypt_block[self::DECRYPT]
)
);
}


$this->inline_crypt = $lambda_functions[$code_hash];
}
}
<?php




































namespace phpseclib\Crypt;









class Twofish extends Base
{







var $cipher_name_mcrypt = 'twofish';








var $cfb_init_len = 800;







var $q0 = array(
0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
);







var $q1 = array(
0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
);







var $m0 = array(
0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8,
0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B,
0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1,
0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F,
0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D,
0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5,
0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3,
0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51,
0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796,
0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C,
0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70,
0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8,
0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC,
0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2,
0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9,
0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17,
0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3,
0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E,
0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149,
0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9,
0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01,
0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48,
0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519,
0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64,
0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5,
0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969,
0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E,
0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC,
0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB,
0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2,
0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91
);







var $m1 = array(
0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4,
0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A,
0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141,
0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E,
0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060,
0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757,
0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D,
0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7,
0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656,
0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B,
0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8,
0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3,
0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D,
0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB,
0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282,
0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7,
0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B,
0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC,
0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E,
0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9,
0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272,
0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F,
0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED,
0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5,
0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7,
0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2,
0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3,
0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323,
0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA,
0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF,
0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000,
0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8
);







var $m2 = array(
0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA,
0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7,
0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783,
0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE,
0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0,
0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA,
0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065,
0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F,
0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07,
0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF,
0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C,
0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96,
0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF,
0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E,
0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD,
0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC,
0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71,
0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85,
0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101,
0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5,
0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B,
0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A,
0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45,
0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D,
0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6,
0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929,
0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D,
0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB,
0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F,
0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9,
0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746,
0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF
);







var $m3 = array(
0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF,
0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836,
0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77,
0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A,
0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5,
0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216,
0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63,
0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123,
0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7,
0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197,
0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB,
0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C,
0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20,
0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137,
0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE,
0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730,
0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252,
0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4,
0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F,
0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A,
0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB,
0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D,
0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0,
0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8,
0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6,
0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38,
0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA,
0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439,
0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6,
0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D,
0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000,
0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8
);







var $K = array();







var $S0 = array();







var $S1 = array();







var $S2 = array();







var $S3 = array();







var $kl;








var $key_length = 16;

























function __construct($mode = self::MODE_CBC)
{
parent::__construct($mode);

$this->m0 = array_map('intval', $this->m0);
$this->m1 = array_map('intval', $this->m1);
$this->m2 = array_map('intval', $this->m2);
$this->m3 = array_map('intval', $this->m3);
$this->q0 = array_map('intval', $this->q0);
$this->q1 = array_map('intval', $this->q1);
}









function setKeyLength($length)
{
switch (true) {
case $length <= 128:
$this->key_length = 16;
break;
case $length <= 192:
$this->key_length = 24;
break;
default:
$this->key_length = 32;
}

parent::setKeyLength($length);
}







function _setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key']) {

return;
}
$this->kl = array('key' => $this->key);


$le_longs = unpack('V*', $this->key);
$key = unpack('C*', $this->key);
$m0 = $this->m0;
$m1 = $this->m1;
$m2 = $this->m2;
$m3 = $this->m3;
$q0 = $this->q0;
$q1 = $this->q1;

$K = $S0 = $S1 = $S2 = $S3 = array();

switch (strlen($this->key)) {
case 16:
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3];
}
break;
case 24:
list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]);
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3];
}
break;
default: 
list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]);
list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]);
list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]);
list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]);
for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
$A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
$m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]];
$B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^
$m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^
$m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0];
$S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1];
$S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2];
$S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3];
}
}

$this->K = $K;
$this->S0 = $S0;
$this->S1 = $S1;
$this->S2 = $S2;
$this->S3 = $S3;
}









function _mdsrem($A, $B)
{

for ($i = 0; $i < 8; ++$i) {

$t = 0xff & ($B >> 24);


$B = ($B << 8) | (0xff & ($A >> 24));
$A<<= 8;

$u = $t << 1;


if ($t & 0x80) {
$u^= 0x14d;
}


$B ^= $t ^ ($u << 16);


$u^= 0x7fffffff & ($t >> 1);


if ($t & 0x01) {
$u^= 0xa6 ;
}


$B^= ($u << 24) | ($u << 8);
}

return array(
0xff & $B >> 24,
0xff & $B >> 16,
0xff & $B >> 8,
0xff & $B);
}








function _encryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
$S2 = $this->S2;
$S3 = $this->S3;
$K = $this->K;

$in = unpack("V4", $in);
$R0 = $K[0] ^ $in[1];
$R1 = $K[1] ^ $in[2];
$R2 = $K[2] ^ $in[3];
$R3 = $K[3] ^ $in[4];

$ki = 7;
while ($ki < 39) {
$t0 = $S0[ $R0 & 0xff] ^
$S1[($R0 >> 8) & 0xff] ^
$S2[($R0 >> 16) & 0xff] ^
$S3[($R0 >> 24) & 0xff];
$t1 = $S0[($R1 >> 24) & 0xff] ^
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);

$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
$S2[($R2 >> 16) & 0xff] ^
$S3[($R2 >> 24) & 0xff];
$t1 = $S0[($R3 >> 24) & 0xff] ^
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
}


return pack("V4", $K[4] ^ $R2,
$K[5] ^ $R3,
$K[6] ^ $R0,
$K[7] ^ $R1);

}








function _decryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
$S2 = $this->S2;
$S3 = $this->S3;
$K = $this->K;

$in = unpack("V4", $in);
$R0 = $K[4] ^ $in[1];
$R1 = $K[5] ^ $in[2];
$R2 = $K[6] ^ $in[3];
$R3 = $K[7] ^ $in[4];

$ki = 40;
while ($ki > 8) {
$t0 = $S0[$R0 & 0xff] ^
$S1[$R0 >> 8 & 0xff] ^
$S2[$R0 >> 16 & 0xff] ^
$S3[$R0 >> 24 & 0xff];
$t1 = $S0[$R1 >> 24 & 0xff] ^
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);

$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
$S2[$R2 >> 16 & 0xff] ^
$S3[$R2 >> 24 & 0xff];
$t1 = $S0[$R3 >> 24 & 0xff] ^
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);
}


return pack("V4", $K[0] ^ $R2,
$K[1] ^ $R3,
$K[2] ^ $R0,
$K[3] ^ $R1);

}







function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();



$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);


$code_hash = "Crypt_Twofish, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}

$safeint = $this->safe_intval_inline();

if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
$K = $this->K;
$init_crypt = '
                        static $S0, $S1, $S2, $S3;
                        if (!$S0) {
                            for ($i = 0; $i < 256; ++$i) {
                                $S0[] = (int)$self->S0[$i];
                                $S1[] = (int)$self->S1[$i];
                                $S2[] = (int)$self->S2[$i];
                                $S3[] = (int)$self->S3[$i];
                            }
                        }
                    ';
break;
default:
$K = array();
for ($i = 0; $i < 40; ++$i) {
$K[] = '$K_' . $i;
}
$init_crypt = '
                        $S0 = $self->S0;
                        $S1 = $self->S1;
                        $S2 = $self->S2;
                        $S3 = $self->S3;
                        list(' . implode(',', $K) . ') = $self->K;
                    ';
}


$encrypt_block = '
                $in = unpack("V4", $in);
                $R0 = '.$K[0].' ^ $in[1];
                $R1 = '.$K[1].' ^ $in[2];
                $R2 = '.$K[2].' ^ $in[3];
                $R3 = '.$K[3].' ^ $in[4];
            ';
for ($ki = 7, $i = 0; $i < 8; ++$i) {
$encrypt_block.= '
                    $t0 = $S0[ $R0        & 0xff] ^
                          $S1[($R0 >>  8) & 0xff] ^
                          $S2[($R0 >> 16) & 0xff] ^
                          $S3[($R0 >> 24) & 0xff];
                    $t1 = $S0[($R1 >> 24) & 0xff] ^
                          $S1[ $R1        & 0xff] ^
                          $S2[($R1 >>  8) & 0xff] ^
                          $S3[($R1 >> 16) & 0xff];
                    $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
                    $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
                    $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';

                    $t0 = $S0[ $R2        & 0xff] ^
                          $S1[($R2 >>  8) & 0xff] ^
                          $S2[($R2 >> 16) & 0xff] ^
                          $S3[($R2 >> 24) & 0xff];
                    $t1 = $S0[($R3 >> 24) & 0xff] ^
                          $S1[ $R3        & 0xff] ^
                          $S2[($R3 >>  8) & 0xff] ^
                          $S3[($R3 >> 16) & 0xff];
                    $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . ';
                    $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
                    $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
                ';
}
$encrypt_block.= '
                $in = pack("V4", ' . $K[4] . ' ^ $R2,
                                 ' . $K[5] . ' ^ $R3,
                                 ' . $K[6] . ' ^ $R0,
                                 ' . $K[7] . ' ^ $R1);
            ';


$decrypt_block = '
                $in = unpack("V4", $in);
                $R0 = '.$K[4].' ^ $in[1];
                $R1 = '.$K[5].' ^ $in[2];
                $R2 = '.$K[6].' ^ $in[3];
                $R3 = '.$K[7].' ^ $in[4];
            ';
for ($ki = 40, $i = 0; $i < 8; ++$i) {
$decrypt_block.= '
                    $t0 = $S0[$R0       & 0xff] ^
                          $S1[$R0 >>  8 & 0xff] ^
                          $S2[$R0 >> 16 & 0xff] ^
                          $S3[$R0 >> 24 & 0xff];
                    $t1 = $S0[$R1 >> 24 & 0xff] ^
                          $S1[$R1       & 0xff] ^
                          $S2[$R1 >>  8 & 0xff] ^
                          $S3[$R1 >> 16 & 0xff];
                    $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
                    $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
                    $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';

                    $t0 = $S0[$R2       & 0xff] ^
                          $S1[$R2 >>  8 & 0xff] ^
                          $S2[$R2 >> 16 & 0xff] ^
                          $S3[$R2 >> 24 & 0xff];
                    $t1 = $S0[$R3 >> 24 & 0xff] ^
                          $S1[$R3       & 0xff] ^
                          $S2[$R3 >>  8 & 0xff] ^
                          $S3[$R3 >> 16 & 0xff];
                    $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
                    $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
                    $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';
                ';
}
$decrypt_block.= '
                $in = pack("V4", ' . $K[0] . ' ^ $R2,
                                 ' . $K[1] . ' ^ $R3,
                                 ' . $K[2] . ' ^ $R0,
                                 ' . $K[3] . ' ^ $R1);
            ';

$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}
$this->inline_crypt = $lambda_functions[$code_hash];
}
}
<?php



































namespace phpseclib\Crypt;








abstract class Base
{












const MODE_CTR = -1;





const MODE_ECB = 1;





const MODE_CBC = 2;





const MODE_CFB = 3;



const MODE_CFB8 = 6;



const MODE_OFB8 = 7;





const MODE_OFB = 4;



const MODE_STREAM = 5;









static $WHIRLPOOL_AVAILABLE;








const ENGINE_INTERNAL = 1;



const ENGINE_MCRYPT = 2;



const ENGINE_OPENSSL = 3;









var $mode;







var $block_size = 16;








var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";








var $iv = '';









var $encryptIV;









var $decryptIV;








var $continuousBuffer = false;









var $enbuffer;









var $debuffer;











var $enmcrypt;











var $demcrypt;









var $enchanged = true;









var $dechanged = true;


















var $ecb;





















var $cfb_init_len = 600;










var $changed = true;








var $padding = true;








var $paddable = false;
















var $engine;









var $preferredEngine;












var $cipher_name_mcrypt;










var $cipher_name_openssl;











var $cipher_name_openssl_ecb;








var $password_default_salt = 'phpseclib/salt';














var $inline_crypt;










var $use_inline_crypt = true;








var $openssl_emulate_ctr = false;








var $openssl_options;








var $explicit_key_length = false;








var $skip_key_adjustment = false;























function __construct($mode = self::MODE_CBC)
{

switch ($mode) {
case self::MODE_ECB:
$this->paddable = true;
$this->mode = self::MODE_ECB;
break;
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB8:
case self::MODE_OFB:
case self::MODE_STREAM:
$this->mode = $mode;
break;
case self::MODE_CBC:
default:
$this->paddable = true;
$this->mode = self::MODE_CBC;
}

$this->_setEngine();


if ($this->use_inline_crypt !== false) {
$this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function');
}

if (!defined('PHP_INT_SIZE')) {
define('PHP_INT_SIZE', 4);
}

if (!defined('CRYPT_BASE_USE_REG_INTVAL')) {
switch (true) {

case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
case !function_exists('php_uname'):
case !is_string(php_uname('m')):
case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
case PHP_INT_SIZE == 8:
define('CRYPT_BASE_USE_REG_INTVAL', true);
break;
case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM':
switch (true) {









case PHP_VERSION_ID >= 70000 && PHP_VERSION_ID <= 70123:
case PHP_VERSION_ID >= 70200 && PHP_VERSION_ID <= 70211:
define('CRYPT_BASE_USE_REG_INTVAL', false);
break;
default:
define('CRYPT_BASE_USE_REG_INTVAL', true);
}
}
}
}











function setIV($iv)
{
if ($this->mode == self::MODE_ECB) {
return;
}

$this->iv = $iv;
$this->changed = true;
}









function setKeyLength($length)
{
$this->explicit_key_length = true;
$this->changed = true;
$this->_setEngine();
}







function getKeyLength()
{
return $this->key_length << 3;
}







function getBlockLength()
{
return $this->block_size << 3;
}















function setKey($key)
{
if (!$this->explicit_key_length) {
$this->setKeyLength(strlen($key) << 3);
$this->explicit_key_length = false;
}

$this->key = $key;
$this->changed = true;
$this->_setEngine();
}





















function setPassword($password, $method = 'pbkdf2')
{
$key = '';

switch ($method) {
case 'bcrypt':
$func_args = func_get_args();

if (!isset($func_args[2])) {
return false;
}

$salt = $func_args[2];

$rounds = isset($func_args[3]) ? $func_args[3] : 16;
$keylen = isset($func_args[4]) ? $func_args[4] : $this->key_length;

$bf = new Blowfish();
$key = $bf->bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds);
if (!$key) {
return false;
}

$this->setKey(substr($key, 0, $keylen));
$this->setIV(substr($key, $keylen));

return true;
default: 
$func_args = func_get_args();


$hash = isset($func_args[2]) ? $func_args[2] : 'sha1';


$salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt;



$count = isset($func_args[4]) ? $func_args[4] : 1000;


if (isset($func_args[5])) {
$dkLen = $func_args[5];
} else {
$dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length;
}

switch (true) {
case $method == 'pbkdf1':
$hashObj = new Hash();
$hashObj->setHash($hash);
if ($dkLen > $hashObj->getLength()) {
user_error('Derived key too long');
return false;
}
$t = $password . $salt;
for ($i = 0; $i < $count; ++$i) {
$t = $hashObj->hash($t);
}
$key = substr($t, 0, $dkLen);

$this->setKey(substr($key, 0, $dkLen >> 1));
$this->setIV(substr($key, $dkLen >> 1));

return true;

case !function_exists('hash_pbkdf2'):
case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
while (strlen($key) < $dkLen) {
$f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u);
$f^= $u;
}
$key.= $f;
}
$key = substr($key, 0, $dkLen);
break;
default:
$key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true);
}
}

$this->setKey($key);

return true;
}





















function encrypt($plaintext)
{
if ($this->paddable) {
$plaintext = $this->_pad($plaintext);
}

if ($this->engine === self::ENGINE_OPENSSL) {
if ($this->changed) {
$this->_clearBuffers();
$this->changed = false;
}
switch ($this->mode) {
case self::MODE_STREAM:
return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB:
$result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
if (!defined('OPENSSL_RAW_DATA')) {
$result = substr($result, 0, -$this->block_size);
}
if ($this->continuousBuffer) {
$this->encryptIV = substr($result, -$this->block_size);
}
return $result;
case self::MODE_CTR:
return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer);
case self::MODE_CFB:


$ciphertext = '';
if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}

$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
$plaintext = substr($plaintext, $i);
}

$overflow = $len % $this->block_size;

if ($overflow) {
$ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
$iv = $this->_string_pop($ciphertext, $this->block_size);

$size = $len - $overflow;
$block = $iv ^ substr($plaintext, -$overflow);
$iv = substr_replace($iv, $block, 0, $overflow);
$ciphertext.= $block;
$pos = $overflow;
} elseif ($len) {
$ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
$iv = substr($ciphertext, -$this->block_size);
}

return $ciphertext;
case self::MODE_CFB8:
$ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >= $this->block_size) {
$this->encryptIV = substr($ciphertext, -$this->block_size);
} else {
$this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
}
}
return $ciphertext;
case self::MODE_OFB8:

$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;

for ($i = 0; $i < $len; ++$i) {
$xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
$ciphertext.= $plaintext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}

if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
}
}

if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));

if ($this->changed) {
$this->_setupMcrypt();
$this->changed = false;
}
if ($this->enchanged) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
$this->enchanged = false;
}




if ($this->mode == self::MODE_CFB && $this->continuousBuffer) {
$block_size = $this->block_size;
$iv = &$this->encryptIV;
$pos = &$this->enbuffer['pos'];
$len = strlen($plaintext);
$ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
$this->enbuffer['enmcrypt_init'] = true;
}
if ($len >= $block_size) {
if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
if ($this->enbuffer['enmcrypt_init'] === true) {
mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
$this->enbuffer['enmcrypt_init'] = false;
}
$ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
$iv = substr($ciphertext, -$block_size);
$len%= $block_size;
} else {
while ($len >= $block_size) {
$iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size);
$ciphertext.= $iv;
$len-= $block_size;
$i+= $block_size;
}
}
}

if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}

restore_error_handler();

return $ciphertext;
}

$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);

if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
}

restore_error_handler();

return $ciphertext;
}

if ($this->changed) {
$this->_setup();
$this->changed = false;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('encrypt', $this, $plaintext);
}

$buffer = &$this->enbuffer;
$block_size = $this->block_size;
$ciphertext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$block = $this->_encryptBlock($block ^ $xor);
$xor = $block;
$ciphertext.= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].= $this->_encryptBlock($xor);
$this->_increment_str($xor);
}
$key = $this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$key = $this->_encryptBlock($xor);
$this->_increment_str($xor);
$ciphertext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:


if ($this->continuousBuffer) {
$iv = &$this->encryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->encryptIV;
$pos = 0;
}
$len = strlen($plaintext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}

$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
}
while ($len >= $block_size) {
$iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
$ciphertext.= $iv;
$len-= $block_size;
$i+= $block_size;
}
if ($len) {
$iv = $this->_encryptBlock($iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
}
break;
case self::MODE_CFB8:


$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;

for ($i = 0; $i < $len; ++$i) {
$ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}

if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->encryptIV = substr($ciphertext, -$block_size);
} else {
$this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;

for ($i = 0; $i < $len; ++$i) {
$xor = $this->_encryptBlock($iv);
$ciphertext.= $plaintext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}

if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['xor'])) {
$xor = $this->_encryptBlock($xor);
$buffer['xor'].= $xor;
}
$key = $this->_string_shift($buffer['xor'], $block_size);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['xor'] = substr($key, $start) . $buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$ciphertext = $this->_encryptBlock($plaintext);
break;
}

return $ciphertext;
}













function decrypt($ciphertext)
{
if ($this->paddable) {


$ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
}

if ($this->engine === self::ENGINE_OPENSSL) {
if ($this->changed) {
$this->_clearBuffers();
$this->changed = false;
}
switch ($this->mode) {
case self::MODE_STREAM:
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) {
$ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
}
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size);
$ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
$offset = 2 * $this->block_size;
} else {
$offset = $this->block_size;
}
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -$offset, $this->block_size);
}
break;
case self::MODE_CTR:
$plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer);
break;
case self::MODE_CFB:


$plaintext = '';
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $this->block_size - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}

$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
$ciphertext = substr($ciphertext, $i);
}
$overflow = $len % $this->block_size;
if ($overflow) {
$plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
if ($len - $overflow) {
$iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow);
}
$iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$overflow);
$iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow);
$pos = $overflow;
} elseif ($len) {
$plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
$iv = substr($ciphertext, -$this->block_size);
}
break;
case self::MODE_CFB8:
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >= $this->block_size) {
$this->decryptIV = substr($ciphertext, -$this->block_size);
} else {
$this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;

for ($i = 0; $i < $len; ++$i) {
$xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
$plaintext.= $ciphertext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}

if ($this->continuousBuffer) {
$this->decryptIV = $iv;
}
break;
case self::MODE_OFB:
$plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
}

return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
}

if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
$block_size = $this->block_size;
if ($this->changed) {
$this->_setupMcrypt();
$this->changed = false;
}
if ($this->dechanged) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
$this->dechanged = false;
}

if ($this->mode == self::MODE_CFB && $this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$this->debuffer['pos'];
$len = strlen($ciphertext);
$plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}

$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
}
if ($len >= $block_size) {
$cb = substr($ciphertext, $i, $len - $len % $block_size);
$plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
$iv = substr($cb, -$block_size);
$len%= $block_size;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
$pos = $len;
}

restore_error_handler();

return $plaintext;
}

$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);

if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}

restore_error_handler();

return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
}

if ($this->changed) {
$this->_setup();
$this->changed = false;
}
if ($this->use_inline_crypt) {
$inline = $this->inline_crypt;
return $inline('decrypt', $this, $ciphertext);
}

$block_size = $this->block_size;

$buffer = &$this->debuffer;
$plaintext = '';
switch ($this->mode) {
case self::MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
$plaintext.= $this->_decryptBlock($block) ^ $xor;
$xor = $block;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case self::MODE_CTR:
$xor = $this->decryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].= $this->_encryptBlock($xor);
$this->_increment_str($xor);
}
$key = $this->_string_shift($buffer['ciphertext'], $block_size);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
$key = $this->_encryptBlock($xor);
$this->_increment_str($xor);
$plaintext.= $block ^ $key;
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
}
}
break;
case self::MODE_CFB:
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
$pos = &$buffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
}
$len = strlen($ciphertext);
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
}

$plaintext = substr($iv, $orig_pos) ^ $ciphertext;
$iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
}
while ($len >= $block_size) {
$iv = $this->_encryptBlock($iv);
$cb = substr($ciphertext, $i, $block_size);
$plaintext.= $iv ^ $cb;
$iv = $cb;
$len-= $block_size;
$i+= $block_size;
}
if ($len) {
$iv = $this->_encryptBlock($iv);
$plaintext.= $iv ^ substr($ciphertext, $i);
$iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
$pos = $len;
}
break;
case self::MODE_CFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;

for ($i = 0; $i < $len; ++$i) {
$plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}

if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->decryptIV = substr($ciphertext, -$block_size);
} else {
$this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;

for ($i = 0; $i < $len; ++$i) {
$xor = $this->_encryptBlock($iv);
$plaintext.= $ciphertext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}

if ($this->continuousBuffer) {
$this->decryptIV = $iv;
}
break;
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) > strlen($buffer['xor'])) {
$xor = $this->_encryptBlock($xor);
$buffer['xor'].= $xor;
}
$key = $this->_string_shift($buffer['xor'], $block_size);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer['xor'] = substr($key, $start) . $buffer['xor'];
}
}
break;
case self::MODE_STREAM:
$plaintext = $this->_decryptBlock($ciphertext);
break;
}
return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
}

















function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer)
{
$ciphertext = '';

$block_size = $this->block_size;
$key = $this->key;

if ($this->openssl_emulate_ctr) {
$xor = $encryptIV;
if (strlen($buffer['ciphertext'])) {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result;
}
$this->_increment_str($xor);
$otp = $this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $otp;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor);
$ciphertext.= $block ^ $otp;
}
}
if ($this->continuousBuffer) {
$encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
}
}

return $ciphertext;
}

if (strlen($buffer['ciphertext'])) {
$ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext));
$plaintext = substr($plaintext, strlen($ciphertext));

if (!strlen($plaintext)) {
return $ciphertext;
}
}

$overflow = strlen($plaintext) % $block_size;
if ($overflow) {
$plaintext2 = $this->_string_pop($plaintext, $overflow); 
$encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
$temp = $this->_string_pop($encrypted, $block_size);
$ciphertext.= $encrypted . ($plaintext2 ^ $temp);
if ($this->continuousBuffer) {
$buffer['ciphertext'] = substr($temp, $overflow);
$encryptIV = $temp;
}
} elseif (!strlen($buffer['ciphertext'])) {
$ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
$temp = $this->_string_pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $temp;
}
}
if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) {
$encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
}
$encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) {
$this->_increment_str($encryptIV);
}
}

return $ciphertext;
}
















function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer)
{
if (strlen($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}

$block_size = $this->block_size;

$len = strlen($plaintext);
$key = $this->key;
$overflow = $len % $block_size;

if (strlen($plaintext)) {
if ($overflow) {
$ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
$xor = $this->_string_pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $xor;
}
$ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow);
if ($this->continuousBuffer) {
$buffer['xor'] = $xor;
}
} else {
$ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
if ($this->continuousBuffer) {
$encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size);
}
}
}

return $ciphertext;
}









function _openssl_translate_mode()
{
switch ($this->mode) {
case self::MODE_ECB:
return 'ecb';
case self::MODE_CBC:
return 'cbc';
case self::MODE_CTR:
return 'ctr';
case self::MODE_CFB:
return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB:
return 'ofb';
}
}
















function enablePadding()
{
$this->padding = true;
}







function disablePadding()
{
$this->padding = false;
}







































function enableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}

$this->continuousBuffer = true;

$this->_setEngine();
}










function disableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
if (!$this->continuousBuffer) {
return;
}

$this->continuousBuffer = false;
$this->changed = true;

$this->_setEngine();
}









function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) {
return false;
}
$this->openssl_emulate_ctr = false;
$result = $this->cipher_name_openssl &&
extension_loaded('openssl') &&

version_compare(PHP_VERSION, '5.3.3', '>=');
if (!$result) {
return false;
}



if (!defined('OPENSSL_RAW_DATA')) {
$this->openssl_options = true;
} else {
$this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
}

$methods = openssl_get_cipher_methods();
if (in_array($this->cipher_name_openssl, $methods)) {
return true;
}


switch ($this->mode) {
case self::MODE_CTR:
if (in_array($this->cipher_name_openssl_ecb, $methods)) {
$this->openssl_emulate_ctr = true;
return true;
}
}
return false;
case self::ENGINE_MCRYPT:
set_error_handler(array($this, 'do_nothing'));
$result = $this->cipher_name_mcrypt &&
extension_loaded('mcrypt') &&
in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms());
restore_error_handler();
return $result;
case self::ENGINE_INTERNAL:
return true;
}

return false;
}


















function setPreferredEngine($engine)
{
switch ($engine) {

case self::ENGINE_MCRYPT:
case self::ENGINE_INTERNAL:
$this->preferredEngine = $engine;
break;
default:
$this->preferredEngine = self::ENGINE_OPENSSL;
}

$this->_setEngine();
}







function getEngine()
{
return $this->engine;
}







function _setEngine()
{
$this->engine = null;

$candidateEngines = array(
$this->preferredEngine,
self::ENGINE_OPENSSL,
self::ENGINE_MCRYPT
);
foreach ($candidateEngines as $engine) {
if ($this->isValidEngine($engine)) {
$this->engine = $engine;
break;
}
}
if (!$this->engine) {
$this->engine = self::ENGINE_INTERNAL;
}

if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) {
set_error_handler(array($this, 'do_nothing'));


mcrypt_module_close($this->enmcrypt);
mcrypt_module_close($this->demcrypt);
$this->enmcrypt = null;
$this->demcrypt = null;

if ($this->ecb) {
mcrypt_module_close($this->ecb);
$this->ecb = null;
}
restore_error_handler();
}

$this->changed = true;
}










abstract function _encryptBlock($in);










abstract function _decryptBlock($in);











abstract function _setupKey();

























function _setup()
{
$this->_clearBuffers();
$this->_setupKey();

if ($this->use_inline_crypt) {
$this->_setupInlineCrypt();
}
}
























function _setupMcrypt()
{
$this->_clearBuffers();
$this->enchanged = $this->dechanged = true;

if (!isset($this->enmcrypt)) {
static $mcrypt_modes = array(
self::MODE_CTR => 'ctr',
self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_OFB8 => MCRYPT_MODE_OFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM,
);

$this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
$this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');




if ($this->mode == self::MODE_CFB) {
$this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
}
} 

if ($this->mode == self::MODE_CFB) {
mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
}
}
















function _pad($text)
{
$length = strlen($text);

if (!$this->padding) {
if ($length % $this->block_size == 0) {
return $text;
} else {
user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})");
$this->padding = true;
}
}

$pad = $this->block_size - ($length % $this->block_size);

return str_pad($text, $length + $pad, chr($pad));
}












function _unpad($text)
{
if (!$this->padding) {
return $text;
}

$length = ord($text[strlen($text) - 1]);

if (!$length || $length > $this->block_size) {
return false;
}

return substr($text, 0, -$length);
}











function _clearBuffers()
{
$this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);



$this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");

if (!$this->skip_key_adjustment) {
$this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
}
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}











function _string_pop(&$string, $index = 1)
{
$substr = substr($string, -$index);
$string = substr($string, 0, -$index);
return $substr;
}









function _increment_str(&$var)
{
if (function_exists('sodium_increment')) {
$var = strrev($var);
sodium_increment($var);
$var = strrev($var);
return;
}

for ($i = 4; $i <= strlen($var); $i+= 4) {
$temp = substr($var, -$i, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
break;
case "\x7F\xFF\xFF\xFF":
$var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
return;
default:
$temp = unpack('Nnum', $temp);
$var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
return;
}
}

$remainder = strlen($var) % 4;

if ($remainder == 0) {
return;
}

$temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
$temp = substr(pack('N', $temp['num'] + 1), -$remainder);
$var = substr_replace($var, $temp, 0, $remainder);
}





























































function _setupInlineCrypt()
{






$this->use_inline_crypt = false;
}
















































































































function _createInlineCryptFunction($cipher_code)
{
$block_size = $this->block_size;


$init_crypt = isset($cipher_code['init_crypt']) ? $cipher_code['init_crypt'] : '';
$init_encrypt = isset($cipher_code['init_encrypt']) ? $cipher_code['init_encrypt'] : '';
$init_decrypt = isset($cipher_code['init_decrypt']) ? $cipher_code['init_decrypt'] : '';

$encrypt_block = $cipher_code['encrypt_block'];
$decrypt_block = $cipher_code['decrypt_block'];




switch ($this->mode) {
case self::MODE_ECB:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);

                    for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                        $in = substr($_text, $_i, '.$block_size.');
                        '.$encrypt_block.'
                        $_ciphertext.= $in;
                    }

                    return $_ciphertext;
                    ';

$decrypt = $init_decrypt . '
                    $_plaintext = "";
                    $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
                    $_ciphertext_len = strlen($_text);

                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                        $in = substr($_text, $_i, '.$block_size.');
                        '.$decrypt_block.'
                        $_plaintext.= $in;
                    }

                    return $self->_unpad($_plaintext);
                    ';
break;
case self::MODE_CTR:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);
                    $_xor = $self->encryptIV;
                    $_buffer = &$self->enbuffer;
                    if (strlen($_buffer["ciphertext"])) {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $self->_increment_str($_xor);
                                $_buffer["ciphertext"].= $in;
                            }
                            $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
                            $_ciphertext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            $in = $_xor;
                            '.$encrypt_block.'
                            $self->_increment_str($_xor);
                            $_key = $in;
                            $_ciphertext.= $_block ^ $_key;
                        }
                    }
                    if ($self->continuousBuffer) {
                        $self->encryptIV = $_xor;
                        if ($_start = $_plaintext_len % '.$block_size.') {
                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
                        }
                    }

                    return $_ciphertext;
                ';

$decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_ciphertext_len = strlen($_text);
                    $_xor = $self->decryptIV;
                    $_buffer = &$self->debuffer;

                    if (strlen($_buffer["ciphertext"])) {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $self->_increment_str($_xor);
                                $_buffer["ciphertext"].= $in;
                            }
                            $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
                            $_plaintext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            $in = $_xor;
                            '.$encrypt_block.'
                            $self->_increment_str($_xor);
                            $_key = $in;
                            $_plaintext.= $_block ^ $_key;
                        }
                    }
                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_xor;
                        if ($_start = $_ciphertext_len % '.$block_size.') {
                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
                        }
                    }

                    return $_plaintext;
                    ';
break;
case self::MODE_CFB:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_buffer = &$self->enbuffer;

                    if ($self->continuousBuffer) {
                        $_iv = &$self->encryptIV;
                        $_pos = &$_buffer["pos"];
                    } else {
                        $_iv = $self->encryptIV;
                        $_pos = 0;
                    }
                    $_len = strlen($_text);
                    $_i = 0;
                    if ($_pos) {
                        $_orig_pos = $_pos;
                        $_max = '.$block_size.' - $_pos;
                        if ($_len >= $_max) {
                            $_i = $_max;
                            $_len-= $_max;
                            $_pos = 0;
                        } else {
                            $_i = $_len;
                            $_pos+= $_len;
                            $_len = 0;
                        }
                        $_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
                        $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i);
                    }
                    while ($_len >= '.$block_size.') {
                        $in = $_iv;
                        '.$encrypt_block.';
                        $_iv = $in ^ substr($_text, $_i, '.$block_size.');
                        $_ciphertext.= $_iv;
                        $_len-= '.$block_size.';
                        $_i+= '.$block_size.';
                    }
                    if ($_len) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_iv = $in;
                        $_block = $_iv ^ substr($_text, $_i);
                        $_iv = substr_replace($_iv, $_block, 0, $_len);
                        $_ciphertext.= $_block;
                        $_pos = $_len;
                    }
                    return $_ciphertext;
                ';

$decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_buffer = &$self->debuffer;

                    if ($self->continuousBuffer) {
                        $_iv = &$self->decryptIV;
                        $_pos = &$_buffer["pos"];
                    } else {
                        $_iv = $self->decryptIV;
                        $_pos = 0;
                    }
                    $_len = strlen($_text);
                    $_i = 0;
                    if ($_pos) {
                        $_orig_pos = $_pos;
                        $_max = '.$block_size.' - $_pos;
                        if ($_len >= $_max) {
                            $_i = $_max;
                            $_len-= $_max;
                            $_pos = 0;
                        } else {
                            $_i = $_len;
                            $_pos+= $_len;
                            $_len = 0;
                        }
                        $_plaintext = substr($_iv, $_orig_pos) ^ $_text;
                        $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i);
                    }
                    while ($_len >= '.$block_size.') {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_iv = $in;
                        $cb = substr($_text, $_i, '.$block_size.');
                        $_plaintext.= $_iv ^ $cb;
                        $_iv = $cb;
                        $_len-= '.$block_size.';
                        $_i+= '.$block_size.';
                    }
                    if ($_len) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_iv = $in;
                        $_plaintext.= $_iv ^ substr($_text, $_i);
                        $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len);
                        $_pos = $_len;
                    }

                    return $_plaintext;
                    ';
break;
case self::MODE_CFB8:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_len = strlen($_text);
                    $_iv = $self->encryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_ciphertext.= ($_c = $_text[$_i] ^ $in);
                        $_iv = substr($_iv, 1) . $_c;
                    }

                    if ($self->continuousBuffer) {
                        if ($_len >= '.$block_size.') {
                            $self->encryptIV = substr($_ciphertext, -'.$block_size.');
                        } else {
                            $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len);
                        }
                    }

                    return $_ciphertext;
                    ';
$decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_len = strlen($_text);
                    $_iv = $self->decryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_plaintext.= $_text[$_i] ^ $in;
                        $_iv = substr($_iv, 1) . $_text[$_i];
                    }

                    if ($self->continuousBuffer) {
                        if ($_len >= '.$block_size.') {
                            $self->decryptIV = substr($_text, -'.$block_size.');
                        } else {
                            $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len);
                        }
                    }

                    return $_plaintext;
                    ';
break;
case self::MODE_OFB8:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_len = strlen($_text);
                    $_iv = $self->encryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_ciphertext.= $_text[$_i] ^ $in;
                        $_iv = substr($_iv, 1) . $in[0];
                    }

                    if ($self->continuousBuffer) {
                        $self->encryptIV = $_iv;
                    }

                    return $_ciphertext;
                    ';
$decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_len = strlen($_text);
                    $_iv = $self->decryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_plaintext.= $_text[$_i] ^ $in;
                        $_iv = substr($_iv, 1) . $in[0];
                    }

                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_iv;
                    }

                    return $_plaintext;
                    ';
break;
case self::MODE_OFB:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);
                    $_xor = $self->encryptIV;
                    $_buffer = &$self->enbuffer;

                    if (strlen($_buffer["xor"])) {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["xor"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $_xor = $in;
                                $_buffer["xor"].= $_xor;
                            }
                            $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
                            $_ciphertext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $in = $_xor;
                            '.$encrypt_block.'
                            $_xor = $in;
                            $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
                        }
                        $_key = $_xor;
                    }
                    if ($self->continuousBuffer) {
                        $self->encryptIV = $_xor;
                        if ($_start = $_plaintext_len % '.$block_size.') {
                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
                        }
                    }
                    return $_ciphertext;
                    ';

$decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_ciphertext_len = strlen($_text);
                    $_xor = $self->decryptIV;
                    $_buffer = &$self->debuffer;

                    if (strlen($_buffer["xor"])) {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["xor"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $_xor = $in;
                                $_buffer["xor"].= $_xor;
                            }
                            $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
                            $_plaintext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $in = $_xor;
                            '.$encrypt_block.'
                            $_xor = $in;
                            $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
                        }
                        $_key = $_xor;
                    }
                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_xor;
                        if ($_start = $_ciphertext_len % '.$block_size.') {
                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
                        }
                    }
                    return $_plaintext;
                    ';
break;
case self::MODE_STREAM:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    '.$encrypt_block.'
                    return $_ciphertext;
                    ';
$decrypt = $init_decrypt . '
                    $_plaintext = "";
                    '.$decrypt_block.'
                    return $_plaintext;
                    ';
break;

default:
$encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);

                    $in = $self->encryptIV;

                    for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                        $in = substr($_text, $_i, '.$block_size.') ^ $in;
                        '.$encrypt_block.'
                        $_ciphertext.= $in;
                    }

                    if ($self->continuousBuffer) {
                        $self->encryptIV = $in;
                    }

                    return $_ciphertext;
                    ';

$decrypt = $init_decrypt . '
                    $_plaintext = "";
                    $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
                    $_ciphertext_len = strlen($_text);

                    $_iv = $self->decryptIV;

                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                        $in = $_block = substr($_text, $_i, '.$block_size.');
                        '.$decrypt_block.'
                        $_plaintext.= $in ^ $_iv;
                        $_iv = $_block;
                    }

                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_iv;
                    }

                    return $self->_unpad($_plaintext);
                    ';
break;
}


eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };');
return $func;
}
















function &_getLambdaFunctions()
{
static $functions = array();
return $functions;
}









function _hashInlineCryptFunction($bytes)
{
if (!isset(self::$WHIRLPOOL_AVAILABLE)) {
self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos());
}

$result = '';
$hash = $bytes;

switch (true) {
case self::$WHIRLPOOL_AVAILABLE:
foreach (str_split($bytes, 64) as $t) {
$hash = hash('whirlpool', $hash, true);
$result .= $t ^ $hash;
}
return $result . hash('whirlpool', $hash, true);
default:
$len = strlen($bytes);
for ($i = 0; $i < $len; $i+=20) {
$t = substr($bytes, $i, 20);
$hash = pack('H*', sha1($hash));
$result .= $t ^ $hash;
}
return $result . pack('H*', sha1($hash));
}
}










function safe_intval($x)
{
if (is_int($x)) {
return $x;
}
return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($x / 0x80000000), 2) & 1) << 31);
}







function safe_intval_inline()
{
if (CRYPT_BASE_USE_REG_INTVAL) {
return PHP_INT_SIZE == 4 ? 'intval(%s)' : '%s';
}

$safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
}






function do_nothing()
{
}







function continuousBufferEnabled()
{
return $this->continuousBuffer;
}
}
<?php











































namespace phpseclib\Crypt;








class RC4 extends Base
{




const ENCRYPT = 0;
const DECRYPT = 1;












var $block_size = 0;








var $key_length = 128; 








var $cipher_name_mcrypt = 'arcfour';








var $use_inline_crypt = false; 








var $key;








var $stream;










function __construct()
{
parent::__construct(Base::MODE_STREAM);
}











function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {



if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
$this->cipher_name_openssl = 'rc4-40';
} else {
switch (strlen($this->key)) {
case 5:
$this->cipher_name_openssl = 'rc4-40';
break;
case 8:
$this->cipher_name_openssl = 'rc4-64';
break;
case 16:
$this->cipher_name_openssl = 'rc4';
break;
default:
return false;
}
}
}

return parent::isValidEngine($engine);
}




















function setIV($iv)
{
}









function setKeyLength($length)
{
if ($length < 8) {
$this->key_length = 1;
} elseif ($length > 2048) {
$this->key_length = 256;
} else {
$this->key_length = $length >> 3;
}

parent::setKeyLength($length);
}










function encrypt($plaintext)
{
if ($this->engine != self::ENGINE_INTERNAL) {
return parent::encrypt($plaintext);
}
return $this->_crypt($plaintext, self::ENCRYPT);
}













function decrypt($ciphertext)
{
if ($this->engine != self::ENGINE_INTERNAL) {
return parent::decrypt($ciphertext);
}
return $this->_crypt($ciphertext, self::DECRYPT);
}







function _encryptBlock($in)
{

}







function _decryptBlock($in)
{

}







function _setupKey()
{
$key = $this->key;
$keyLength = strlen($key);
$keyStream = range(0, 255);
$j = 0;
for ($i = 0; $i < 256; $i++) {
$j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
$temp = $keyStream[$i];
$keyStream[$i] = $keyStream[$j];
$keyStream[$j] = $temp;
}

$this->stream = array();
$this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array(
0, 
0, 
$keyStream
);
}











function _crypt($text, $mode)
{
if ($this->changed) {
$this->_setup();
$this->changed = false;
}

$stream = &$this->stream[$mode];
if ($this->continuousBuffer) {
$i = &$stream[0];
$j = &$stream[1];
$keyStream = &$stream[2];
} else {
$i = $stream[0];
$j = $stream[1];
$keyStream = $stream[2];
}

$len = strlen($text);
for ($k = 0; $k < $len; ++$k) {
$i = ($i + 1) & 255;
$ksi = $keyStream[$i];
$j = ($j + $ksi) & 255;
$ksj = $keyStream[$j];

$keyStream[$i] = $ksj;
$keyStream[$j] = $ksi;
$text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]);
}

return $text;
}
}
<?php



































namespace phpseclib\Crypt;








class TripleDES extends DES
{





const MODE_3CBC = -2;






const MODE_CBC3 = self::MODE_CBC;








var $key_length = 24;









var $password_default_salt = 'phpseclib';









var $cipher_name_mcrypt = 'tripledes';








var $cfb_init_len = 750;









var $key_length_max = 24;







var $mode_3cbc;









var $des;



























function __construct($mode = self::MODE_CBC)
{
switch ($mode) {


case self::MODE_3CBC:
parent::__construct(self::MODE_CBC);
$this->mode_3cbc = true;


$this->des = array(
new DES(self::MODE_CBC),
new DES(self::MODE_CBC),
new DES(self::MODE_CBC),
);


$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
break;

default:
parent::__construct($mode);
}
}











function isValidEngine($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
$this->cipher_name_openssl_ecb = 'des-ede3';
$mode = $this->_openssl_translate_mode();
$this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode;
}

return parent::isValidEngine($engine);
}











function setIV($iv)
{
parent::setIV($iv);
if ($this->mode_3cbc) {
$this->des[0]->setIV($iv);
$this->des[1]->setIV($iv);
$this->des[2]->setIV($iv);
}
}










function setKeyLength($length)
{
$length >>= 3;
switch (true) {
case $length <= 8:
$this->key_length = 8;
break;
case $length <= 16:
$this->key_length = 16;
break;
default:
$this->key_length = 24;
}

parent::setKeyLength($length);
}
















function setKey($key)
{
$length = $this->explicit_key_length ? $this->key_length : strlen($key);
if ($length > 8) {
$key = str_pad(substr($key, 0, 24), 24, chr(0));


$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
} else {
$key = str_pad($key, 8, chr(0));
}
parent::setKey($key);





if ($this->mode_3cbc && $length > 8) {
$this->des[0]->setKey(substr($key, 0, 8));
$this->des[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8));
}
}









function encrypt($plaintext)
{




if ($this->mode_3cbc && strlen($this->key) > 8) {
return $this->des[2]->encrypt(
$this->des[1]->decrypt(
$this->des[0]->encrypt(
$this->_pad($plaintext)
)
)
);
}

return parent::encrypt($plaintext);
}









function decrypt($ciphertext)
{
if ($this->mode_3cbc && strlen($this->key) > 8) {
return $this->_unpad(
$this->des[0]->decrypt(
$this->des[1]->encrypt(
$this->des[2]->decrypt(
str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0")
)
)
)
);
}

return parent::decrypt($ciphertext);
}







































function enableContinuousBuffer()
{
parent::enableContinuousBuffer();
if ($this->mode_3cbc) {
$this->des[0]->enableContinuousBuffer();
$this->des[1]->enableContinuousBuffer();
$this->des[2]->enableContinuousBuffer();
}
}










function disableContinuousBuffer()
{
parent::disableContinuousBuffer();
if ($this->mode_3cbc) {
$this->des[0]->disableContinuousBuffer();
$this->des[1]->disableContinuousBuffer();
$this->des[2]->disableContinuousBuffer();
}
}








function _setupKey()
{
switch (true) {


case strlen($this->key) <= 8:
$this->des_rounds = 1;
break;


default:
$this->des_rounds = 3;


if ($this->mode_3cbc) {
$this->des[0]->_setupKey();
$this->des[1]->_setupKey();
$this->des[2]->_setupKey();



return;
}
}

parent::_setupKey();
}










function setPreferredEngine($engine)
{
if ($this->mode_3cbc) {
$this->des[0]->setPreferredEngine($engine);
$this->des[1]->setPreferredEngine($engine);
$this->des[2]->setPreferredEngine($engine);
}

return parent::setPreferredEngine($engine);
}
}
<?php


















































namespace phpseclib\Crypt;

use phpseclib\Math\BigInteger;








class RSA
{














const ENCRYPTION_OAEP = 1;






const ENCRYPTION_PKCS1 = 2;






const ENCRYPTION_NONE = 3;
















const SIGNATURE_PSS = 1;






const SIGNATURE_PKCS1 = 2;









const ASN1_INTEGER = 2;



const ASN1_BITSTRING = 3;



const ASN1_OCTETSTRING = 4;



const ASN1_OBJECT = 6;



const ASN1_SEQUENCE = 48;









const MODE_INTERNAL = 1;





const MODE_OPENSSL = 2;












const PRIVATE_FORMAT_PKCS1 = 0;



const PRIVATE_FORMAT_PUTTY = 1;



const PRIVATE_FORMAT_XML = 2;



const PRIVATE_FORMAT_PKCS8 = 8;



const PRIVATE_FORMAT_OPENSSH = 9;




















const PUBLIC_FORMAT_RAW = 3;











const PUBLIC_FORMAT_PKCS1 = 4;
const PUBLIC_FORMAT_PKCS1_RAW = 4;



const PUBLIC_FORMAT_XML = 5;





const PUBLIC_FORMAT_OPENSSH = 6;













const PUBLIC_FORMAT_PKCS8 = 7;








var $zero;







var $one;







var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;







var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;







var $modulus;







var $k;







var $exponent;







var $primes;







var $exponents;







var $coefficients;







var $hashName;







var $hash;







var $hLen;







var $sLen;







var $mgfHash;







var $mgfHLen;







var $encryptionMode = self::ENCRYPTION_OAEP;







var $signatureMode = self::SIGNATURE_PSS;







var $publicExponent = false;







var $password = false;











var $components = array();











var $current;









var $configFile;







var $comment = 'phpseclib-generated-key';











function __construct()
{
$this->configFile = dirname(__FILE__) . '/../openssl.cnf';

if (!defined('CRYPT_RSA_MODE')) {
switch (true) {



case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
break;
case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile):

$versions = array();


if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();

preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);

if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));


if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
$versions[$matches[1][$i]] = $fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}


switch (true) {
case !isset($versions['Header']):
case !isset($versions['Library']):
case $versions['Header'] == $versions['Library']:
case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
break;
default:
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
}
break;
default:
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
}
}

$this->zero = new BigInteger();
$this->one = new BigInteger(1);

$this->hash = new Hash('sha1');
$this->hLen = $this->hash->getLength();
$this->hashName = 'sha1';
$this->mgfHash = new Hash('sha1');
$this->mgfHLen = $this->mgfHash->getLength();
}















function createKey($bits = 1024, $timeout = false, $partial = array())
{
if (!defined('CRYPT_RSA_EXPONENT')) {

define('CRYPT_RSA_EXPONENT', '65537');
}






if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
}


if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
$config = array();
if (isset($this->configFile)) {
$config['config'] = $this->configFile;
}
$rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
openssl_pkey_export($rsa, $privatekey, null, $config);
$publickey = openssl_pkey_get_details($rsa);
$publickey = $publickey['key'];

$privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
$publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));



while (openssl_error_string() !== false) {
}

return array(
'privatekey' => $privatekey,
'publickey' => $publickey,
'partialkey' => false
);
}

static $e;
if (!isset($e)) {
$e = new BigInteger(CRYPT_RSA_EXPONENT);
}

extract($this->_generateMinMax($bits));
$absoluteMin = $min;
$temp = $bits >> 1; 
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
$temp = CRYPT_RSA_SMALLEST_PRIME;
} else {
$num_primes = 2;
}
extract($this->_generateMinMax($temp + $bits % $temp));
$finalMax = $max;
extract($this->_generateMinMax($temp));

$generator = new BigInteger();

$n = $this->one->copy();
if (!empty($partial)) {
extract(unserialize($partial));
} else {
$exponents = $coefficients = $primes = array();
$lcm = array(
'top' => $this->one->copy(),
'bottom' => false
);
}

$start = time();
$i0 = count($primes) + 1;

do {
for ($i = $i0; $i <= $num_primes; $i++) {
if ($timeout !== false) {
$timeout-= time() - $start;
$start = time();
if ($timeout <= 0) {
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
))
);
}
}

if ($i == $num_primes) {
list($min, $temp) = $absoluteMin->divide($n);
if (!$temp->equals($this->zero)) {
$min = $min->add($this->one); 
}
$primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
} else {
$primes[$i] = $generator->randomPrime($min, $max, $timeout);
}

if ($primes[$i] === false) { 
if (count($primes) > 1) {
$partialkey = '';
} else {
array_pop($primes);
$partialkey = serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
));
}

return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => $partialkey
);
}



if ($i > 2) {
$coefficients[$i] = $n->modInverse($primes[$i]);
}

$n = $n->multiply($primes[$i]);

$temp = $primes[$i]->subtract($this->one);



$lcm['top'] = $lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);

$exponents[$i] = $e->modInverse($temp);
}

list($temp) = $lcm['top']->divide($lcm['bottom']);
$gcd = $temp->gcd($e);
$i0 = 1;
} while (!$gcd->equals($this->one));

$d = $e->modInverse($temp);

$coefficients[2] = $primes[2]->modInverse($primes[1]);















return array(
'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
'publickey' => $this->_convertPublicKey($n, $e),
'partialkey' => false
);
}














function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
{
$signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
$num_primes = count($primes);
$raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1), 
'modulus' => $n->toBytes($signed),
'publicExponent' => $e->toBytes($signed),
'privateExponent' => $d->toBytes($signed),
'prime1' => $primes[1]->toBytes($signed),
'prime2' => $primes[2]->toBytes($signed),
'exponent1' => $exponents[1]->toBytes($signed),
'exponent2' => $exponents[2]->toBytes($signed),
'coefficient' => $coefficients[2]->toBytes($signed)
);



switch ($this->privateKeyFormat) {
case self::PRIVATE_FORMAT_XML:
if ($num_primes != 2) {
return false;
}
return "<RSAKeyValue>\r\n" .
'  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
'  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
'  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
'  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
'  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
'  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
'  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
'  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
'</RSAKeyValue>';
break;
case self::PRIVATE_FORMAT_PUTTY:
if ($num_primes != 2) {
return false;
}
$key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
$encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
$key.= $encryption;
$key.= "\r\nComment: " . $this->comment . "\r\n";
$public = pack(
'Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['modulus']),
$raw['modulus']
);
$source = pack(
'Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($encryption),
$encryption,
strlen($this->comment),
$this->comment,
strlen($public),
$public
);
$public = base64_encode($public);
$key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
$key.= chunk_split($public, 64);
$private = pack(
'Na*Na*Na*Na*',
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2'],
strlen($raw['coefficient']),
$raw['coefficient']
);
if (empty($this->password) && !is_string($this->password)) {
$source.= pack('Na*', strlen($private), $private);
$hashkey = 'putty-private-key-file-mac-key';
} else {
$private.= Random::string(16 - (strlen($private) & 15));
$source.= pack('Na*', strlen($private), $private);
$sequence = 0;
$symkey = '';
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++, $this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$crypto = new AES();

$crypto->setKey($symkey);
$crypto->disablePadding();
$private = $crypto->encrypt($private);
$hashkey = 'putty-private-key-file-mac-key' . $this->password;
}

$private = base64_encode($private);
$key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
$key.= chunk_split($private, 64);
$hash = new Hash('sha1');
$hash->setKey(pack('H*', sha1($hashkey)));
$key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";

return $key;
case self::PRIVATE_FORMAT_OPENSSH:
if ($num_primes != 2) {
return false;
}
$publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
$privateKey = pack(
'Na*Na*Na*Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['modulus']),
$raw['modulus'],
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['coefficient']),
$raw['coefficient'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2']
);
$checkint = Random::string(4);
$paddedKey = pack(
'a*Na*',
$checkint . $checkint . $privateKey,
strlen($this->comment),
$this->comment
);
$paddingLength = (7 * strlen($paddedKey)) % 8;
for ($i = 1; $i <= $paddingLength; $i++) {
$paddedKey.= chr($i);
}
$key = pack(
'Na*Na*Na*NNa*Na*',
strlen('none'),
'none',
strlen('none'),
'none',
0,
'',
1,
strlen($publicKey),
$publicKey,
strlen($paddedKey),
$paddedKey
);
$key = "openssh-key-v1\0$key";

return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
chunk_split(base64_encode($key), 70, "\n") .
"-----END OPENSSH PRIVATE KEY-----\n";
default: 
$components = array();
foreach ($raw as $name => $value) {
$components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
}

$RSAPrivateKey = implode('', $components);

if ($num_primes > 2) {
$OtherPrimeInfos = '';
for ($i = 3; $i <= $num_primes; $i++) {







$OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
$OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
}
$RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
}

$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);

if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); 
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_INTEGER,
"\01\00",
$rsaOID,
4,
$this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
if (!empty($this->password) || is_string($this->password)) {
$salt = Random::string(8);
$iterationCount = 2048;

$crypto = new DES();
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
$RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);

$parameters = pack(
'Ca*a*Ca*N',
self::ASN1_OCTETSTRING,
$this->_encodeLength(strlen($salt)),
$salt,
self::ASN1_INTEGER,
$this->_encodeLength(4),
$iterationCount
);
$pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";

$encryptionAlgorithm = pack(
'Ca*a*Ca*a*',
self::ASN1_OBJECT,
$this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
$pbeWithMD5AndDES_CBC,
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($parameters)),
$parameters
);

$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($encryptionAlgorithm)),
$encryptionAlgorithm,
self::ASN1_OCTETSTRING,
$this->_encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);

$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);

$RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END ENCRYPTED PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END PRIVATE KEY-----';
}
return $RSAPrivateKey;
}

if (!empty($this->password) || is_string($this->password)) {
$iv = Random::string(8);
$symkey = pack('H*', md5($this->password . $iv)); 
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
$des = new TripleDES();
$des->setKey($symkey);
$des->setIV($iv);
$iv = strtoupper(bin2hex($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n" .
"DEK-Info: DES-EDE3-CBC,$iv\r\n" .
"\r\n" .
chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
'-----END RSA PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END RSA PRIVATE KEY-----';
}

return $RSAPrivateKey;
}
}










function _convertPublicKey($n, $e)
{
$signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;

$modulus = $n->toBytes($signed);
$publicExponent = $e->toBytes($signed);

switch ($this->publicKeyFormat) {
case self::PUBLIC_FORMAT_RAW:
return array('e' => $e->copy(), 'n' => $n->copy());
case self::PUBLIC_FORMAT_XML:
return "<RSAKeyValue>\r\n" .
'  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
'  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
'</RSAKeyValue>';
break;
case self::PUBLIC_FORMAT_OPENSSH:




$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
$RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;

return $RSAPublicKey;
default: 





$components = array(
'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
);

$RSAPublicKey = pack(
'Ca*a*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);

if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
$RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey), 64) .
'-----END RSA PUBLIC KEY-----';
} else {

$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); 
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;

$RSAPublicKey = pack(
'Ca*a*',
self::ASN1_SEQUENCE,
$this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
$rsaOID . $RSAPublicKey
);

$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey), 64) .
'-----END PUBLIC KEY-----';
}

return $RSAPublicKey;
}
}











function _parseKey($key, $type)
{
if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
return false;
}

switch ($type) {
case self::PUBLIC_FORMAT_RAW:
if (!is_array($key)) {
return false;
}
$components = array();
switch (true) {
case isset($key['e']):
$components['publicExponent'] = $key['e']->copy();
break;
case isset($key['exponent']):
$components['publicExponent'] = $key['exponent']->copy();
break;
case isset($key['publicExponent']):
$components['publicExponent'] = $key['publicExponent']->copy();
break;
case isset($key[0]):
$components['publicExponent'] = $key[0]->copy();
}
switch (true) {
case isset($key['n']):
$components['modulus'] = $key['n']->copy();
break;
case isset($key['modulo']):
$components['modulus'] = $key['modulo']->copy();
break;
case isset($key['modulus']):
$components['modulus'] = $key['modulus']->copy();
break;
case isset($key[1]):
$components['modulus'] = $key[1]->copy();
}
return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
case self::PRIVATE_FORMAT_PKCS1:
case self::PRIVATE_FORMAT_PKCS8:
case self::PUBLIC_FORMAT_PKCS1:















if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
$iv = pack('H*', trim($matches[2]));
$symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); 
$symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));

$key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
$ciphertext = $this->_extractBER($key);
if ($ciphertext === false) {
$ciphertext = $key;
}
switch ($matches[1]) {
case 'AES-256-CBC':
$crypto = new AES();
break;
case 'AES-128-CBC':
$symkey = substr($symkey, 0, 16);
$crypto = new AES();
break;
case 'DES-EDE3-CFB':
$crypto = new TripleDES(Base::MODE_CFB);
break;
case 'DES-EDE3-CBC':
$symkey = substr($symkey, 0, 24);
$crypto = new TripleDES();
break;
case 'DES-CBC':
$crypto = new DES();
break;
default:
return false;
}
$crypto->setKey($symkey);
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
} else {
$decoded = $this->_extractBER($key);
}

if ($decoded !== false) {
$key = $decoded;
}

$components = array();

if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}

$tag = ord($this->_string_shift($key));











if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
$this->_string_shift($key, 3);
$tag = self::ASN1_SEQUENCE;
}

if ($tag == self::ASN1_SEQUENCE) {
$temp = $this->_string_shift($key, $this->_decodeLength($key));
if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
return false;
}
$length = $this->_decodeLength($temp);
switch ($this->_string_shift($temp, $length)) {
case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": 
case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": 
break;
case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": 





if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($temp) != strlen($temp)) {
return false;
}
$this->_string_shift($temp); 
$salt = $this->_string_shift($temp, $this->_decodeLength($temp));
if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
return false;
}
$this->_decodeLength($temp);
list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
$this->_string_shift($key); 
$length = $this->_decodeLength($key);
if (strlen($key) != $length) {
return false;
}

$crypto = new DES();
$crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
$key = $crypto->decrypt($key);
if ($key === false) {
return false;
}
return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
default:
return false;
}







$tag = ord($this->_string_shift($key)); 
$this->_decodeLength($key); 



if ($tag == self::ASN1_BITSTRING) {
$this->_string_shift($key);
}
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
}
if ($tag != self::ASN1_INTEGER) {
return false;
}

$length = $this->_decodeLength($key);
$temp = $this->_string_shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) {
$components['modulus'] = new BigInteger($temp, 256);
$this->_string_shift($key); 
$length = $this->_decodeLength($key);
$components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);

return $components;
}
if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key);
$components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));

if (!empty($key)) {
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
while (!empty($key)) {
if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
$key = substr($key, 1);
$length = $this->_decodeLength($key);
$components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
}
}

return $components;
case self::PUBLIC_FORMAT_OPENSSH:
$parts = explode(' ', $key, 3);

$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
if ($key === false) {
return false;
}

$comment = isset($parts[2]) ? $parts[2] : false;

$cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";

if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$modulus = new BigInteger($this->_string_shift($key, $length), -256);

if ($cleanup && strlen($key)) {
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$realModulus = new BigInteger($this->_string_shift($key, $length), -256);
return strlen($key) ? false : array(
'modulus' => $realModulus,
'publicExponent' => $modulus,
'comment' => $comment
);
} else {
return strlen($key) ? false : array(
'modulus' => $modulus,
'publicExponent' => $publicExponent,
'comment' => $comment
);
}


case self::PRIVATE_FORMAT_XML:
case self::PUBLIC_FORMAT_XML:
if (!extension_loaded('xml')) {
return false;
}

$this->components = array();

$xml = xml_parser_create('UTF-8');
xml_set_object($xml, $this);
xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
xml_set_character_data_handler($xml, '_data_handler');

if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
xml_parser_free($xml);
unset($xml);
return false;
}

xml_parser_free($xml);
unset($xml);

return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;

case self::PRIVATE_FORMAT_PUTTY:
$components = array();
$key = preg_split('#\r\n|\r|\n#', $key);
if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
return false;
}
$version = (int) $this->_string_shift($key[0], 3); 
if ($version != 2 && $version != 3) {
return false;
}
$type = rtrim($key[0]);
if ($type != 'ssh-rsa') {
return false;
}
$encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
$comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));

$publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
$public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
$public = substr($public, 11);
extract(unpack('Nlength', $this->_string_shift($public, 4)));
$components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
extract(unpack('Nlength', $this->_string_shift($public, 4)));
$components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);

$offset = $publicLength + 4;
switch ($encryption) {
case 'aes256-cbc':
$crypto = new AES();
switch ($version) {
case 3:
if (!function_exists('sodium_crypto_pwhash')) {
return false;
}
$flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
switch ($flavour) {
case 'Argon2i':
$flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
break;
case 'Argon2id':
$flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
break;
default:
return false;
}
$memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
$passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
$parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
$salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));

$length = 80; 
$temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour);

$symkey = substr($temp, 0, 32);
$symiv = substr($temp, 32, 16);
break;
case 2:
$symkey = '';
$sequence = 0;
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++, $this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$symiv = str_repeat("\0", 16);
}
}

$privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
$private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));

if ($encryption != 'none') {
$crypto->setKey($symkey);
$crypto->setIV($symiv);
$crypto->disablePadding();
$private = $crypto->decrypt($private);
if ($private === false) {
return false;
}
}

extract(unpack('Nlength', $this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
extract(unpack('Nlength', $this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
extract(unpack('Nlength', $this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);

$temp = $components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
$temp = $components['primes'][2]->subtract($this->one);
$components['exponents'][] = $components['publicExponent']->modInverse($temp);

extract(unpack('Nlength', $this->_string_shift($private, 4)));
if (strlen($private) < $length) {
return false;
}
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));

return $components;
case self::PRIVATE_FORMAT_OPENSSH:
$components = array();
$decoded = $this->_extractBER($key);
$magic = $this->_string_shift($decoded, 15);
if ($magic !== "openssh-key-v1\0") {
return false;
}
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$ciphername = $this->_string_shift($decoded, $length);
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$kdfname = $this->_string_shift($decoded, $length);
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$kdfoptions = $this->_string_shift($decoded, $length);
extract(unpack('Nnumkeys', $this->_string_shift($decoded, 4)));
if ($numkeys != 1 || ($ciphername != 'none' && $kdfname != 'bcrypt')) {
return false;
}
switch ($ciphername) {
case 'none':
break;
case 'aes256-ctr':
extract(unpack('Nlength', $this->_string_shift($kdfoptions, 4)));
if (strlen($kdfoptions) < $length) {
return false;
}
$salt = $this->_string_shift($kdfoptions, $length);
extract(unpack('Nrounds', $this->_string_shift($kdfoptions, 4)));
$crypto = new AES(AES::MODE_CTR);
$crypto->disablePadding();
if (!$crypto->setPassword($this->password, 'bcrypt', $salt, $rounds, 32)) {
return false;
}
break;
default:
return false;
}
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$publicKey = $this->_string_shift($decoded, $length);
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}

if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}

$paddedKey = $this->_string_shift($decoded, $length);
if (isset($crypto)) {
$paddedKey = $crypto->decrypt($paddedKey);
}

$checkint1 = $this->_string_shift($paddedKey, 4);
$checkint2 = $this->_string_shift($paddedKey, 4);
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
return false;
}

if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}

$values = array(
&$components['modulus'],
&$components['publicExponent'],
&$components['privateExponent'],
&$components['coefficients'][2],
&$components['primes'][1],
&$components['primes'][2]
);

foreach ($values as &$value) {
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
}

extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$components['comment'] = $this->_string_shift($decoded, $length);

$temp = $components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
$temp = $components['primes'][2]->subtract($this->one);
$components['exponents'][] = $components['publicExponent']->modInverse($temp);

return $components;
}

return false;
}









function getSize()
{
return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
}











function _start_element_handler($parser, $name, $attribs)
{

switch ($name) {
case 'MODULUS':
$this->current = &$this->components['modulus'];
break;
case 'EXPONENT':
$this->current = &$this->components['publicExponent'];
break;
case 'P':
$this->current = &$this->components['primes'][1];
break;
case 'Q':
$this->current = &$this->components['primes'][2];
break;
case 'DP':
$this->current = &$this->components['exponents'][1];
break;
case 'DQ':
$this->current = &$this->components['exponents'][2];
break;
case 'INVERSEQ':
$this->current = &$this->components['coefficients'][2];
break;
case 'D':
$this->current = &$this->components['privateExponent'];
}
$this->current = '';
}










function _stop_element_handler($parser, $name)
{
if (isset($this->current)) {
$this->current = new BigInteger(base64_decode($this->current), 256);
unset($this->current);
}
}










function _data_handler($parser, $data)
{
if (!isset($this->current) || is_object($this->current)) {
return;
}
$this->current.= trim($data);
}











function loadKey($key, $type = false)
{
if ($key instanceof RSA) {
$this->privateKeyFormat = $key->privateKeyFormat;
$this->publicKeyFormat = $key->publicKeyFormat;
$this->k = $key->k;
$this->hLen = $key->hLen;
$this->sLen = $key->sLen;
$this->mgfHLen = $key->mgfHLen;
$this->encryptionMode = $key->encryptionMode;
$this->signatureMode = $key->signatureMode;
$this->password = $key->password;
$this->configFile = $key->configFile;
$this->comment = $key->comment;

if (is_object($key->hash)) {
$this->hash = new Hash($key->hash->getHash());
}
if (is_object($key->mgfHash)) {
$this->mgfHash = new Hash($key->mgfHash->getHash());
}

if (is_object($key->modulus)) {
$this->modulus = $key->modulus->copy();
}
if (is_object($key->exponent)) {
$this->exponent = $key->exponent->copy();
}
if (is_object($key->publicExponent)) {
$this->publicExponent = $key->publicExponent->copy();
}

$this->primes = array();
$this->exponents = array();
$this->coefficients = array();

foreach ($this->primes as $prime) {
$this->primes[] = $prime->copy();
}
foreach ($this->exponents as $exponent) {
$this->exponents[] = $exponent->copy();
}
foreach ($this->coefficients as $coefficient) {
$this->coefficients[] = $coefficient->copy();
}

return true;
}

if ($type === false) {
$types = array(
self::PUBLIC_FORMAT_RAW,
self::PRIVATE_FORMAT_PKCS1,
self::PRIVATE_FORMAT_XML,
self::PRIVATE_FORMAT_PUTTY,
self::PUBLIC_FORMAT_OPENSSH,
self::PRIVATE_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}

if ($components === false) {
$this->comment = null;
$this->modulus = null;
$this->k = null;
$this->exponent = null;
$this->primes = null;
$this->exponents = null;
$this->coefficients = null;
$this->publicExponent = null;

return false;
}

if (isset($components['comment']) && $components['comment'] !== false) {
$this->comment = $components['comment'];
}
$this->modulus = $components['modulus'];
$this->k = strlen($this->modulus->toBytes());
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
if (isset($components['primes'])) {
$this->primes = $components['primes'];
$this->exponents = $components['exponents'];
$this->coefficients = $components['coefficients'];
$this->publicExponent = $components['publicExponent'];
} else {
$this->primes = array();
$this->exponents = array();
$this->coefficients = array();
$this->publicExponent = false;
}

switch ($type) {
case self::PUBLIC_FORMAT_OPENSSH:
case self::PUBLIC_FORMAT_RAW:
$this->setPublicKey();
break;
case self::PRIVATE_FORMAT_PKCS1:
switch (true) {
case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
$this->setPublicKey();
}
}

return true;
}












function setPassword($password = false)
{
$this->password = $password;
}






















function setPublicKey($key = false, $type = false)
{

if (!empty($this->publicExponent)) {
return false;
}

if ($key === false && !empty($this->modulus)) {
$this->publicExponent = $this->exponent;
return true;
}

if ($type === false) {
$types = array(
self::PUBLIC_FORMAT_RAW,
self::PUBLIC_FORMAT_PKCS1,
self::PUBLIC_FORMAT_XML,
self::PUBLIC_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}

if ($components === false) {
return false;
}

if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
$this->modulus = $components['modulus'];
$this->exponent = $this->publicExponent = $components['publicExponent'];
return true;
}

$this->publicExponent = $components['publicExponent'];

return true;
}

















function setPrivateKey($key = false, $type = false)
{
if ($key === false && !empty($this->publicExponent)) {
$this->publicExponent = false;
return true;
}

$rsa = new RSA();
if (!$rsa->loadKey($key, $type)) {
return false;
}
$rsa->publicExponent = false;


$this->loadKey($rsa);
return true;
}












function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}

$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $type;
$temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}













function getPublicKeyFingerprint($algorithm = 'md5')
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}

$modulus = $this->modulus->toBytes(true);
$publicExponent = $this->publicExponent->toBytes(true);

$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);

switch ($algorithm) {
case 'sha256':
$hash = new Hash('sha256');
$base = base64_encode($hash->hash($RSAPublicKey));
return substr($base, 0, strlen($base) - 1);
case 'md5':
return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
default:
return false;
}
}











function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
{
if (empty($this->primes)) {
return false;
}

$oldFormat = $this->privateKeyFormat;
$this->privateKeyFormat = $type;
$temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
$this->privateKeyFormat = $oldFormat;
return $temp;
}











function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}

$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $mode;
$temp = $this->_convertPublicKey($this->modulus, $this->exponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}







function __toString()
{
$key = $this->getPrivateKey($this->privateKeyFormat);
if ($key !== false) {
return $key;
}
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
return $key !== false ? $key : '';
}







function __clone()
{
$key = new RSA();
$key->loadKey($this);
return $key;
}








function _generateMinMax($bits)
{
$bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}

return array(
'min' => new BigInteger($min, 256),
'max' => new BigInteger($max, 256)
);
}











function _decodeLength(&$string)
{
$length = ord($this->_string_shift($string));
if ($length & 0x80) { 
$length&= 0x7F;
$temp = $this->_string_shift($string, $length);
list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
}
return $length;
}











function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}

$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}








function setPrivateKeyFormat($format)
{
$this->privateKeyFormat = $format;
}








function setPublicKeyFormat($format)
{
$this->publicKeyFormat = $format;
}










function setHash($hash)
{

switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = new Hash($hash);
$this->hashName = $hash;
break;
default:
$this->hash = new Hash('sha1');
$this->hashName = 'sha1';
}
$this->hLen = $this->hash->getLength();
}










function setMGFHash($hash)
{

switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->mgfHash = new Hash($hash);
break;
default:
$this->mgfHash = new Hash('sha1');
}
$this->mgfHLen = $this->mgfHash->getLength();
}












function setSaltLength($sLen)
{
$this->sLen = $sLen;
}











function _i2osp($x, $xLen)
{
$x = $x->toBytes();
if (strlen($x) > $xLen) {
user_error('Integer too large');
return false;
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}










function _os2ip($x)
{
return new BigInteger($x, 256);
}










function _exponentiate($x)
{
switch (true) {
case empty($this->primes):
case $this->primes[1]->equals($this->zero):
case empty($this->coefficients):
case $this->coefficients[2]->equals($this->zero):
case empty($this->exponents):
case $this->exponents[1]->equals($this->zero):
return $x->modPow($this->exponent, $this->modulus);
}

$num_primes = count($this->primes);

if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
$m_i = array(
1 => $x->modPow($this->exponents[1], $this->primes[1]),
2 => $x->modPow($this->exponents[2], $this->primes[2])
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));

$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);

$r = $r->multiply($this->primes[$i - 1]);

$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);

$m = $m->add($r->multiply($h));
}
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}

$one = new BigInteger(1);

$r = $one->random($one, $smallest->subtract($one));

$m_i = array(
1 => $this->_blind($x, $r, 1),
2 => $this->_blind($x, $r, 2)
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));

$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->_blind($x, $r, $i);

$r = $r->multiply($this->primes[$i - 1]);

$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);

$m = $m->add($r->multiply($h));
}
}

return $m;
}













function _blind($x, $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);

$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);

return $x;
}















function _equals($x, $y)
{
if (function_exists('hash_equals')) {
return hash_equals($x, $y);
}

if (strlen($x) != strlen($y)) {
return false;
}

$result = "\0";
$x^= $y;
for ($i = 0; $i < strlen($x); $i++) {
$result|= $x[$i];
}

return $result === "\0";
}










function _rsaep($m)
{
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
user_error('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}










function _rsadp($c)
{
if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
user_error('Ciphertext representative out of range');
return false;
}
return $this->_exponentiate($c);
}










function _rsasp1($m)
{
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
user_error('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}










function _rsavp1($s)
{
if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
user_error('Signature representative out of range');
return false;
}
return $this->_exponentiate($s);
}











function _mgf1($mgfSeed, $maskLen)
{


$t = '';
$count = ceil($maskLen / $this->mgfHLen);
for ($i = 0; $i < $count; $i++) {
$c = pack('N', $i);
$t.= $this->mgfHash->hash($mgfSeed . $c);
}

return substr($t, 0, $maskLen);
}












function _rsaes_oaep_encrypt($m, $l = '')
{
$mLen = strlen($m);






if ($mLen > $this->k - 2 * $this->hLen - 2) {
user_error('Message too long');
return false;
}



$lHash = $this->hash->hash($l);
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
$db = $lHash . $ps . chr(1) . $m;
$seed = Random::string($this->hLen);
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$maskedSeed = $seed ^ $seedMask;
$em = chr(0) . $maskedSeed . $maskedDB;



$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);



return $c;
}



























function _rsaes_oaep_decrypt($c, $l = '')
{





if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
user_error('Decryption error');
return false;
}



$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);



$lHash = $this->hash->hash($l);
$y = ord($em[0]);
$maskedSeed = substr($em, 1, $this->hLen);
$maskedDB = substr($em, $this->hLen + 1);
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$seed = $maskedSeed ^ $seedMask;
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
$hashesMatch = $this->_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
}



if (!$hashesMatch | !$patternMatch) {
user_error('Decryption error');
return false;
}



return substr($m, $offset + 1);
}










function _raw_encrypt($m)
{
$temp = $this->_os2ip($m);
$temp = $this->_rsaep($temp);
return $this->_i2osp($temp, $this->k);
}










function _rsaes_pkcs1_v1_5_encrypt($m)
{
$mLen = strlen($m);



if ($mLen > $this->k - 11) {
user_error('Message too long');
return false;
}



$psLen = $this->k - $mLen - 3;
$ps = '';
while (strlen($ps) != $psLen) {
$temp = Random::string($psLen - strlen($ps));
$temp = str_replace("\x00", '', $temp);
$ps.= $temp;
}
$type = 2;

if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
$type = 1;

$ps = str_repeat("\xFF", $psLen);
}
$em = chr(0) . chr($type) . $ps . chr(0) . $m;


$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);



return $c;
}





















function _rsaes_pkcs1_v1_5_decrypt($c)
{


if (strlen($c) != $this->k) { 
user_error('Decryption error');
return false;
}



$c = $this->_os2ip($c);
$m = $this->_rsadp($c);

if ($m === false) {
user_error('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);



if (ord($em[0]) != 0 || ord($em[1]) > 2) {
user_error('Decryption error');
return false;
}

$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);

if (strlen($ps) < 8) {
user_error('Decryption error');
return false;
}



return $m;
}










function _emsa_pss_encode($m, $emBits)
{



$emLen = ($emBits + 1) >> 3; 
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;

$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
user_error('Encoding error');
return false;
}

$salt = Random::string($sLen);
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h = $this->hash->hash($m2);
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
$db = $ps . chr(1) . $salt;
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);

return $em;
}












function _emsa_pss_verify($m, $em, $emBits)
{



$emLen = ($emBits + 7) >> 3; 
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;

$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}

if ($em[strlen($em) - 1] != chr(0xBC)) {
return false;
}

$maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
return false;
}
$salt = substr($db, $temp + 1); 
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return $this->_equals($h, $h2);
}










function _rsassa_pss_sign($m)
{


$em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);



$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);



return $s;
}











function _rsassa_pss_verify($m, $s)
{


if (strlen($s) != $this->k) {
user_error('Invalid signature');
return false;
}



$modBits = strlen($this->modulus->toBits());

$s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2);
if ($m2 === false) {
user_error('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature');
return false;
}



return $this->_emsa_pss_verify($m, $em, $modBits - 1);
}











function _emsa_pkcs1_v1_5_encode($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}


switch ($this->hashName) {
case 'md2':
$t = pack('H*', '3020300c06082a864886f70d020205000410');
break;
case 'md5':
$t = pack('H*', '3020300c06082a864886f70d020505000410');
break;
case 'sha1':
$t = pack('H*', '3021300906052b0e03021a05000414');
break;
case 'sha256':
$t = pack('H*', '3031300d060960864801650304020105000420');
break;
case 'sha384':
$t = pack('H*', '3041300d060960864801650304020205000430');
break;
case 'sha512':
$t = pack('H*', '3051300d060960864801650304020305000440');
}
$t.= $h;
$tLen = strlen($t);

if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too short');
return false;
}

$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);

$em = "\0\1$ps\0$t";

return $em;
}
















function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}

switch ($this->hashName) {
case 'sha1':
$t = pack('H*', '301f300706052b0e03021a0414');
break;
case 'sha256':
$t = pack('H*', '302f300b06096086480165030402010420');
break;
case 'sha384':
$t = pack('H*', '303f300b06096086480165030402020430');
break;
case 'sha512':
$t = pack('H*', '304f300b06096086480165030402030440');
break;
default:
return false;
}
$t.= $h;
$tLen = strlen($t);

if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too short');
return false;
}

$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);

$em = "\0\1$ps\0$t";

return $em;
}










function _rsassa_pkcs1_v1_5_sign($m)
{


$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em === false) {
user_error('RSA modulus too short');
return false;
}



$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);



return $s;
}











function _rsassa_pkcs1_v1_5_verify($m, $s)
{


if (strlen($s) != $this->k) {
user_error('Invalid signature');
return false;
}



$s = $this->_os2ip($s);
$m2 = $this->_rsavp1($s);
if ($m2 === false) {
user_error('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature');
return false;
}



$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
$em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k);

if ($em2 === false && $em3 === false) {
user_error('RSA modulus too short');
return false;
}



return ($em2 !== false && $this->_equals($em, $em2)) ||
($em3 !== false && $this->_equals($em, $em3));
}









function setEncryptionMode($mode)
{
$this->encryptionMode = $mode;
}









function setSignatureMode($mode)
{
$this->signatureMode = $mode;
}







function setComment($comment)
{
$this->comment = $comment;
}







function getComment()
{
return $this->comment;
}













function encrypt($plaintext)
{
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
$plaintext = str_split($plaintext, $this->k);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_raw_encrypt($m);
}
return $ciphertext;
case self::ENCRYPTION_PKCS1:
$length = $this->k - 11;
if ($length <= 0) {
return false;
}

$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
}
return $ciphertext;

default:
$length = $this->k - 2 * $this->hLen - 2;
if ($length <= 0) {
return false;
}

$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_oaep_encrypt($m);
}
return $ciphertext;
}
}









function decrypt($ciphertext)
{
if ($this->k <= 0) {
return false;
}

$ciphertext = str_split($ciphertext, $this->k);
$ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);

$plaintext = '';

switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
$decrypt = '_raw_encrypt';
break;
case self::ENCRYPTION_PKCS1:
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
break;

default:
$decrypt = '_rsaes_oaep_decrypt';
}

foreach ($ciphertext as $c) {
$temp = $this->$decrypt($c);
if ($temp === false) {
return false;
}
$plaintext.= $temp;
}

return $plaintext;
}









function sign($message)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}

switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_sign($message);

default:
return $this->_rsassa_pss_sign($message);
}
}










function verify($message, $signature)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}

switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);

default:
return $this->_rsassa_pss_verify($message, $signature);
}
}








function _extractBER($str)
{









$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);

$temp = preg_replace('#-+[^-]+-+#', '', $temp);

$temp = str_replace(array("\r", "\n", ' '), '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
return $temp != false ? $temp : $str;
}
}
<?php























namespace phpseclib\Crypt;








class Random
{










static function string($length)
{
if (!$length) {
return '';
}

if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
try {
return \random_bytes($length);
} catch (\Throwable $e) {






}
}

if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {


if (extension_loaded('mcrypt') && function_exists('class_alias')) {
return @mcrypt_create_iv($length);
}













if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
return openssl_random_pseudo_bytes($length);
}
} else {

if (extension_loaded('openssl')) {
return openssl_random_pseudo_bytes($length);
}

static $fp = true;
if ($fp === true) {


$fp = @fopen('/dev/urandom', 'rb');
}
if ($fp !== true && $fp !== false) { 
$temp = fread($fp, $length);
if (strlen($temp) == $length) {
return $temp;
}
}





if (extension_loaded('mcrypt')) {
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
}
}



















static $crypto = false, $v;
if ($crypto === false) {

$old_session_id = session_id();
$old_use_cookies = ini_get('session.use_cookies');
$old_session_cache_limiter = session_cache_limiter();
$_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
if ($old_session_id != '') {
session_write_close();
}

session_id(1);
ini_set('session.use_cookies', 0);
session_cache_limiter('');
session_start();

$v = $seed = $_SESSION['seed'] = pack('H*', sha1(
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
(isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
(isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') .



(version_compare(PHP_VERSION, '8.1.0', '>=') ? serialize($GLOBALS) : phpseclib_safe_serialize($GLOBALS)) .
phpseclib_safe_serialize($_SESSION) .
phpseclib_safe_serialize($_OLD_SESSION)
));
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
$_SESSION['count']++;

session_write_close();


if ($old_session_id != '') {
session_id($old_session_id);
session_start();
ini_set('session.use_cookies', $old_use_cookies);
session_cache_limiter($old_session_cache_limiter);
} else {
if ($_OLD_SESSION !== false) {
$_SESSION = $_OLD_SESSION;
unset($_OLD_SESSION);
} else {
unset($_SESSION);
}
}









$key = pack('H*', sha1($seed . 'A'));
$iv = pack('H*', sha1($seed . 'C'));




switch (true) {
case class_exists('\phpseclib\Crypt\AES'):
$crypto = new AES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\Twofish'):
$crypto = new Twofish(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\Blowfish'):
$crypto = new Blowfish(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\TripleDES'):
$crypto = new TripleDES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\DES'):
$crypto = new DES(Base::MODE_CTR);
break;
case class_exists('\phpseclib\Crypt\RC4'):
$crypto = new RC4();
break;
default:
user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded');
return false;
}

$crypto->setKey($key);
$crypto->setIV($iv);
$crypto->enableContinuousBuffer();
}











$result = '';
while (strlen($result) < $length) {
$i = $crypto->encrypt(microtime()); 
$r = $crypto->encrypt($i ^ $v); 
$v = $crypto->encrypt($r ^ $i); 
$result.= $r;
}
return substr($result, 0, $length);
}
}

if (!function_exists('phpseclib_safe_serialize')) {









function phpseclib_safe_serialize(&$arr)
{
if (is_object($arr)) {
return '';
}
if (!is_array($arr)) {
return serialize($arr);
}

if (isset($arr['__phpseclib_marker'])) {
return '';
}
$safearr = array();
$arr['__phpseclib_marker'] = true;
foreach (array_keys($arr) as $key) {

if ($key !== '__phpseclib_marker') {
$safearr[$key] = phpseclib_safe_serialize($arr[$key]);
}
}
unset($arr['__phpseclib_marker']);
return serialize($safearr);
}
}
<?php


































namespace phpseclib\Crypt;







class RC2 extends Base
{







var $block_size = 8;









var $key;











var $orig_key = '';








var $skip_key_adjustment = true;








var $key_length = 16; 








var $cipher_name_mcrypt = 'rc2';








var $cfb_init_len = 500;











var $default_key_length = 1024;










var $current_key_length;








var $keys;









var $pitable = array(
0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD,
0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD
);








var $invpitable = array(
0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66,
0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4,
0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20,
0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53,
0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68,
0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B,
0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12,
0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB,
0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3,
0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26,
0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67,
0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB,
0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC,
0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60,
0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7,
0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD,
0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24,
0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31,
0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE,
0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99,
0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C,
0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA,
0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35,
0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61,
0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72,
0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3,
0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F,
0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9,
0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77,
0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75,
0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87,
0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6
);











function isValidEngine($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:



if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'rc2-ecb';
$this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode();
}

return parent::isValidEngine($engine);
}











function setKeyLength($length)
{
if ($length < 8) {
$this->default_key_length = 1;
} elseif ($length > 1024) {
$this->default_key_length = 128;
} else {
$this->default_key_length = $length;
}
$this->current_key_length = $this->default_key_length;

parent::setKeyLength($length);
}







function getKeyLength()
{
return $this->current_key_length;
}

















function setKey($key, $t1 = 0)
{
$this->orig_key = $key;

if ($t1 <= 0) {
$t1 = $this->default_key_length;
} elseif ($t1 > 1024) {
$t1 = 1024;
}
$this->current_key_length = $t1;

$key = strlen($key) ? substr($key, 0, 128) : "\x00";
$t = strlen($key);








$l = array_values(unpack('C*', $key));
$t8 = ($t1 + 7) >> 3;
$tm = 0xFF >> (8 * $t8 - $t1);


$pitable = $this->pitable;
for ($i = $t; $i < 128; $i++) {
$l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]];
}
$i = 128 - $t8;
$l[$i] = $pitable[$l[$i] & $tm];
while ($i--) {
$l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]];
}


$l[0] = $this->invpitable[$l[0]];
array_unshift($l, 'C*');

parent::setKey(call_user_func_array('pack', $l));
}











function encrypt($plaintext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
$this->key = $this->orig_key;
$result = parent::encrypt($plaintext);
$this->key = $temp;
return $result;
}

return parent::encrypt($plaintext);
}











function decrypt($ciphertext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
$this->key = $this->orig_key;
$result = parent::decrypt($ciphertext);
$this->key = $temp;
return $result;
}

return parent::decrypt($ciphertext);
}










function _encryptBlock($in)
{
list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
$keys = $this->keys;
$limit = 20;
$actions = array($limit => 44, 44 => 64);
$j = 0;

for (;;) {

$r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
$r0 |= $r0 >> 16;
$r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
$r1 |= $r1 >> 16;
$r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
$r2 |= $r2 >> 16;
$r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
$r3 |= $r3 >> 16;

if ($j === $limit) {
if ($limit === 64) {
break;
}


$r0 += $keys[$r3 & 0x3F];
$r1 += $keys[$r0 & 0x3F];
$r2 += $keys[$r1 & 0x3F];
$r3 += $keys[$r2 & 0x3F];
$limit = $actions[$limit];
}
}

return pack('vvvv', $r0, $r1, $r2, $r3);
}










function _decryptBlock($in)
{
list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
$keys = $this->keys;
$limit = 44;
$actions = array($limit => 20, 20 => 0);
$j = 64;

for (;;) {

$r3 = ($r3 | ($r3 << 16)) >> 5;
$r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
$r2 = ($r2 | ($r2 << 16)) >> 3;
$r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
$r1 = ($r1 | ($r1 << 16)) >> 2;
$r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
$r0 = ($r0 | ($r0 << 16)) >> 1;
$r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;

if ($j === $limit) {
if ($limit === 0) {
break;
}


$r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
$r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
$r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
$r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;
$limit = $actions[$limit];
}
}

return pack('vvvv', $r0, $r1, $r2, $r3);
}







function _setupMcrypt()
{
if (!isset($this->key)) {
$this->setKey('');
}

parent::_setupMcrypt();
}







function _setupKey()
{
if (!isset($this->key)) {
$this->setKey('');
}



$l = unpack('Ca/Cb/v*', $this->key);
array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8));
unset($l['a']);
unset($l['b']);
$this->keys = $l;
}







function _setupInlineCrypt()
{
$lambda_functions =& self::_getLambdaFunctions();





$gen_hi_opt_code = (bool)(count($lambda_functions) < 10);


$code_hash = "Crypt_RC2, {$this->mode}";
if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}



if (!isset($lambda_functions[$code_hash])) {

$init_crypt = '$keys = $self->keys;';

switch (true) {
case $gen_hi_opt_code:
$keys = $this->keys;
default:
$keys = array();
foreach ($this->keys as $k => $v) {
$keys[$k] = '$keys[' . $k . ']';
}
}


$encrypt_block = $decrypt_block = '
                $in = unpack("v4", $in);
                $r0 = $in[1];
                $r1 = $in[2];
                $r2 = $in[3];
                $r3 = $in[4];
            ';


$limit = 20;
$actions = array($limit => 44, 44 => 64);
$j = 0;

for (;;) {

$encrypt_block .= '
                    $r0 = (($r0 + ' . $keys[$j++] . ' +
                           ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
                    $r0 |= $r0 >> 16;
                    $r1 = (($r1 + ' . $keys[$j++] . ' +
                           ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
                    $r1 |= $r1 >> 16;
                    $r2 = (($r2 + ' . $keys[$j++] . ' +
                           ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
                    $r2 |= $r2 >> 16;
                    $r3 = (($r3 + ' . $keys[$j++] . ' +
                           ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
                    $r3 |= $r3 >> 16;';

if ($j === $limit) {
if ($limit === 64) {
break;
}


$encrypt_block .= '
                        $r0 += $keys[$r3 & 0x3F];
                        $r1 += $keys[$r0 & 0x3F];
                        $r2 += $keys[$r1 & 0x3F];
                        $r3 += $keys[$r2 & 0x3F];';
$limit = $actions[$limit];
}
}

$encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';


$limit = 44;
$actions = array($limit => 20, 20 => 0);
$j = 64;

for (;;) {

$decrypt_block .= '
                    $r3 = ($r3 | ($r3 << 16)) >> 5;
                    $r3 = ($r3 - ' . $keys[--$j] . ' -
                           ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
                    $r2 = ($r2 | ($r2 << 16)) >> 3;
                    $r2 = ($r2 - ' . $keys[--$j] . ' -
                           ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
                    $r1 = ($r1 | ($r1 << 16)) >> 2;
                    $r1 = ($r1 - ' . $keys[--$j] . ' -
                           ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
                    $r0 = ($r0 | ($r0 << 16)) >> 1;
                    $r0 = ($r0 - ' . $keys[--$j] . ' -
                           ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';

if ($j === $limit) {
if ($limit === 0) {
break;
}


$decrypt_block .= '
                        $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
                        $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
                        $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
                        $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
$limit = $actions[$limit];
}
}

$decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';


$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
array(
'init_crypt' => $init_crypt,
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
)
);
}


$this->inline_crypt = $lambda_functions[$code_hash];
}
}
<?php






if (extension_loaded('mbstring')) {


if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) {
throw new \UnexpectedValueException(
'Overloading of string functions using mbstring.func_overload ' .
'is not supported by phpseclib.'
);
}
}
<?php














namespace phpseclib\System\SSH\Agent;

use phpseclib\System\SSH\Agent;














class Identity
{







const SSH_AGENT_RSA2_256 = 2;
const SSH_AGENT_RSA2_512 = 4;









var $key;








var $key_blob;








var $fsock;









var $flags = 0;








function __construct($fsock)
{
$this->fsock = $fsock;
}









function setPublicKey($key)
{
$this->key = $key;
$this->key->setPublicKey();
}










function setPublicKeyBlob($key_blob)
{
$this->key_blob = $key_blob;
}










function getPublicKey($format = null)
{
return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format);
}










function setSignatureMode($mode)
{
}









function setHash($hash)
{
$this->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
break;
default:
user_error('The only supported hashes for RSA are sha1, sha256 and sha512');
}
}










function sign($message)
{

$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags);
$packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing');
return false;
}

$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
user_error('Unable to retrieve signature');
return false;
}

$signature_blob = fread($this->fsock, $length - 1);
if (strlen($signature_blob) != $length - 1) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length != strlen($signature_blob)) {
user_error('Malformed signature blob');
}
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length > strlen($signature_blob) + 4) {
user_error('Malformed signature blob');
}
$type = $this->_string_shift($signature_blob, $length);
$this->_string_shift($signature_blob, 4);

return $signature_blob;
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}
<?php
































namespace phpseclib\System\SSH;

use phpseclib\Crypt\RSA;
use phpseclib\System\SSH\Agent\Identity;










class Agent
{






const SSH_AGENTC_REQUEST_IDENTITIES = 11;

const SSH_AGENT_IDENTITIES_ANSWER = 12;

const SSH_AGENTC_SIGN_REQUEST = 13;

const SSH_AGENT_SIGN_RESPONSE = 14;








const FORWARD_NONE = 0;

const FORWARD_REQUEST = 1;

const FORWARD_ACTIVE = 2;





const SSH_AGENT_FAILURE = 5;







var $fsock;






var $forward_status = self::FORWARD_NONE;








var $socket_buffer = '';






var $expected_bytes = 0;







function __construct($address = null)
{
if (!$address) {
switch (true) {
case isset($_SERVER['SSH_AUTH_SOCK']):
$address = $_SERVER['SSH_AUTH_SOCK'];
break;
case isset($_ENV['SSH_AUTH_SOCK']):
$address = $_ENV['SSH_AUTH_SOCK'];
break;
default:
user_error('SSH_AUTH_SOCK not found');
return false;
}
}

if (in_array('unix', stream_get_transports())) {
$this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
if (!$this->fsock) {
user_error("Unable to connect to ssh-agent (Error $errno: $errstr)");
}
} else {
if (substr($address, 0, 9) != '\\\\.\\pipe\\' || strpos(substr($address, 9), '\\') !== false) {
user_error('Address is not formatted as a named pipe should be');
} else {
$this->fsock = fopen($address, 'r+b');
if (!$this->fsock) {
user_error('Unable to open address');
}
}
}
}










function requestIdentities()
{
if (!$this->fsock) {
return array();
}

$packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed while requesting identities');
return array();
}

$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting identities');
return array();
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
user_error('Unable to request identities');
return array();
}

$identities = array();
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting identities');
return array();
}
$keyCount = current(unpack('N', $temp));
for ($i = 0; $i < $keyCount; $i++) {
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting identities');
return array();
}
$length = current(unpack('N', $temp));
$key_blob = fread($this->fsock, $length);
if (strlen($key_blob) != $length) {
user_error('Connection closed while requesting identities');
return array();
}
$key_str = 'ssh-rsa ' . base64_encode($key_blob);
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while requesting identities');
return array();
}
$length = current(unpack('N', $temp));
if ($length) {
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
user_error('Connection closed while requesting identities');
return array();
}
$key_str.= ' ' . $temp;
}
$length = current(unpack('N', substr($key_blob, 0, 4)));
$key_type = substr($key_blob, 4, $length);
switch ($key_type) {
case 'ssh-rsa':
$key = new RSA();
$key->loadKey($key_str);
break;
case 'ssh-dss':

break;
}

if (isset($key)) {
$identity = new Identity($this->fsock);
$identity->setPublicKey($key);
$identity->setPublicKeyBlob($key_blob);
$identities[] = $identity;
unset($key);
}
}

return $identities;
}








function startSSHForwarding()
{
if ($this->forward_status == self::FORWARD_NONE) {
$this->forward_status = self::FORWARD_REQUEST;
}
}








function _request_forwarding($ssh)
{
$request_channel = $ssh->_get_open_channel();
if ($request_channel === false) {
return false;
}

$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
$ssh->server_channels[$request_channel],
strlen('auth-agent-req@openssh.com'),
'auth-agent-req@openssh.com',
1
);

$ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST;

if (!$ssh->_send_binary_packet($packet)) {
return false;
}

$response = $ssh->_get_channel_packet($request_channel);
if ($response === false) {
return false;
}

$ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN;
$this->forward_status = self::FORWARD_ACTIVE;

return true;
}











function _on_channel_open($ssh)
{
if ($this->forward_status == self::FORWARD_REQUEST) {
$this->_request_forwarding($ssh);
}
}








function _forward_data($data)
{
if ($this->expected_bytes > 0) {
$this->socket_buffer.= $data;
$this->expected_bytes -= strlen($data);
} else {
$agent_data_bytes = current(unpack('N', $data));
$current_data_bytes = strlen($data);
$this->socket_buffer = $data;
if ($current_data_bytes != $agent_data_bytes + 4) {
$this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes;
return false;
}
}

if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) {
user_error('Connection closed attempting to forward data to SSH agent');
return false;
}

$this->socket_buffer = '';
$this->expected_bytes = 0;

$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed while reading data response');
return false;
}
$agent_reply_bytes = current(unpack('N', $temp));

$agent_reply_data = fread($this->fsock, $agent_reply_bytes);
if (strlen($agent_reply_data) != $agent_reply_bytes) {
user_error('Connection closed while reading data response');
return false;
}
$agent_reply_data = current(unpack('a*', $agent_reply_data));

return pack('Na*', $agent_reply_bytes, $agent_reply_data);
}
}
<?php






















namespace phpseclib\File;

use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;








class ASN1
{






const CLASS_UNIVERSAL = 0;
const CLASS_APPLICATION = 1;
const CLASS_CONTEXT_SPECIFIC = 2;
const CLASS_PRIVATE = 3;








const TYPE_BOOLEAN = 1;
const TYPE_INTEGER = 2;
const TYPE_BIT_STRING = 3;
const TYPE_OCTET_STRING = 4;
const TYPE_NULL = 5;
const TYPE_OBJECT_IDENTIFIER = 6;


const TYPE_REAL = 9;
const TYPE_ENUMERATED = 10;

const TYPE_UTF8_STRING = 12;

const TYPE_SEQUENCE = 16; 
const TYPE_SET = 17; 







const TYPE_NUMERIC_STRING = 18;
const TYPE_PRINTABLE_STRING = 19;
const TYPE_TELETEX_STRING = 20; 
const TYPE_VIDEOTEX_STRING = 21;
const TYPE_IA5_STRING = 22;
const TYPE_UTC_TIME = 23;
const TYPE_GENERALIZED_TIME = 24;
const TYPE_GRAPHIC_STRING = 25;
const TYPE_VISIBLE_STRING = 26; 
const TYPE_GENERAL_STRING = 27;
const TYPE_UNIVERSAL_STRING = 28;

const TYPE_BMP_STRING = 30;









const TYPE_CHOICE = -1;
const TYPE_ANY = -2;









var $oids = array();








var $format = 'D, d M Y H:i:s O';










var $encoded;










var $filters;









var $location;











var $ANYmap = array(
self::TYPE_BOOLEAN => true,
self::TYPE_INTEGER => true,
self::TYPE_BIT_STRING => 'bitString',
self::TYPE_OCTET_STRING => 'octetString',
self::TYPE_NULL => 'null',
self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
self::TYPE_REAL => true,
self::TYPE_ENUMERATED => 'enumerated',
self::TYPE_UTF8_STRING => 'utf8String',
self::TYPE_NUMERIC_STRING => 'numericString',
self::TYPE_PRINTABLE_STRING => 'printableString',
self::TYPE_TELETEX_STRING => 'teletexString',
self::TYPE_VIDEOTEX_STRING => 'videotexString',
self::TYPE_IA5_STRING => 'ia5String',
self::TYPE_UTC_TIME => 'utcTime',
self::TYPE_GENERALIZED_TIME => 'generalTime',
self::TYPE_GRAPHIC_STRING => 'graphicString',
self::TYPE_VISIBLE_STRING => 'visibleString',
self::TYPE_GENERAL_STRING => 'generalString',
self::TYPE_UNIVERSAL_STRING => 'universalString',

self::TYPE_BMP_STRING => 'bmpString'
);










var $stringTypeSize = array(
self::TYPE_UTF8_STRING => 0,
self::TYPE_BMP_STRING => 2,
self::TYPE_UNIVERSAL_STRING => 4,
self::TYPE_PRINTABLE_STRING => 1,
self::TYPE_TELETEX_STRING => 1,
self::TYPE_IA5_STRING => 1,
self::TYPE_VISIBLE_STRING => 1,
);










function decodeBER($encoded)
{
if ($encoded instanceof Element) {
$encoded = $encoded->element;
}

$this->encoded = $encoded;

return array($this->_decode_ber($encoded));
}














function _decode_ber($encoded, $start = 0, $encoded_pos = 0)
{
$current = array('start' => $start);

if (!isset($encoded[$encoded_pos])) {
return false;
}
$type = ord($encoded[$encoded_pos++]);
$startOffset = 1;

$constructed = ($type >> 5) & 1;

$tag = $type & 0x1F;
if ($tag == 0x1F) {
$tag = 0;

do {
if (!isset($encoded[$encoded_pos])) {
return false;
}
$temp = ord($encoded[$encoded_pos++]);
$startOffset++;
$loop = $temp >> 7;
$tag <<= 7;
$temp &= 0x7F;

if ($startOffset == 2 && $temp == 0) {
return false;
}
$tag |= $temp;
} while ($loop);
}

$start+= $startOffset;


if (!isset($encoded[$encoded_pos])) {
return false;
}
$length = ord($encoded[$encoded_pos++]);
$start++;
if ($length == 0x80) { 


$length = strlen($encoded) - $encoded_pos;
} elseif ($length & 0x80) { 


$length&= 0x7F;
$temp = substr($encoded, $encoded_pos, $length);
$encoded_pos += $length;

$current+= array('headerlength' => $length + 2);
$start+= $length;
extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
} else {
$current+= array('headerlength' => 2);
}

if ($length > (strlen($encoded) - $encoded_pos)) {
return false;
}

$content = substr($encoded, $encoded_pos, $length);
$content_pos = 0;












$class = ($type >> 6) & 3;
switch ($class) {
case self::CLASS_APPLICATION:
case self::CLASS_PRIVATE:
case self::CLASS_CONTEXT_SPECIFIC:
if (!$constructed) {
return array(
'type' => $class,
'constant' => $tag,
'content' => $content,
'length' => $length + $start - $current['start']
);
}

$newcontent = array();
$remainingLength = $length;
while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];

if (substr($content, $content_pos + $length, 2) == "\0\0") {
$length+= 2;
$start+= $length;
$newcontent[] = $temp;
break;
}
$start+= $length;
$remainingLength-= $length;
$newcontent[] = $temp;
$content_pos += $length;
}

return array(
'type' => $class,
'constant' => $tag,

'content' => $newcontent,



'length' => $start - $current['start']
) + $current;
}

$current+= array('type' => $tag);


switch ($tag) {
case self::TYPE_BOOLEAN:

if ($constructed || strlen($content) != 1) {
return false;
}
$current['content'] = (bool) ord($content[$content_pos]);
break;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if ($constructed) {
return false;
}
$current['content'] = new BigInteger(substr($content, $content_pos), -256);
break;
case self::TYPE_REAL: 
return false;
case self::TYPE_BIT_STRING:



if (!$constructed) {
$current['content'] = substr($content, $content_pos);
} else {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {

if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
return false;
}
$current['content'].= substr($temp[$i]['content'], 1);
}

if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
return false;
}
$current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
}
break;
case self::TYPE_OCTET_STRING:
if (!$constructed) {
$current['content'] = substr($content, $content_pos);
} else {
$current['content'] = '';
$length = 0;
while (substr($content, $content_pos, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];

if ($temp['type'] != self::TYPE_OCTET_STRING) {
return false;
}
$current['content'].= $temp['content'];
$length+= $temp['length'];
}
if (substr($content, $content_pos, 2) == "\0\0") {
$length+= 2; 
}
}
break;
case self::TYPE_NULL:

if ($constructed || strlen($content)) {
return false;
}
break;
case self::TYPE_SEQUENCE:
case self::TYPE_SET:
if (!$constructed) {
return false;
}
$offset = 0;
$current['content'] = array();
$content_len = strlen($content);
while ($content_pos < $content_len) {


if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") {
$length = $offset + 2; 
break 2;
}
$temp = $this->_decode_ber($content, $start + $offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
$offset+= $temp['length'];
}
break;
case self::TYPE_OBJECT_IDENTIFIER:
if ($constructed) {
return false;
}
$current['content'] = $this->_decodeOID(substr($content, $content_pos));
if ($current['content'] === false) {
return false;
}
break;







case self::TYPE_NUMERIC_STRING:

case self::TYPE_PRINTABLE_STRING:


case self::TYPE_TELETEX_STRING:


case self::TYPE_VIDEOTEX_STRING:

case self::TYPE_VISIBLE_STRING:

case self::TYPE_IA5_STRING:

case self::TYPE_GRAPHIC_STRING:

case self::TYPE_GENERAL_STRING:

case self::TYPE_UTF8_STRING:

case self::TYPE_BMP_STRING:
if ($constructed) {
return false;
}
$current['content'] = substr($content, $content_pos);
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
if ($constructed) {
return false;
}
$current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag);
break;
default:
return false;
}

$start+= $length;


return $current + array('length' => $start - $current['start']);
}














function asn1map($decoded, $mapping, $special = array())
{
if (!is_array($decoded)) {
return false;
}

if (isset($mapping['explicit']) && is_array($decoded['content'])) {
$decoded = $decoded['content'][0];
}

switch (true) {
case $mapping['type'] == self::TYPE_ANY:
$intype = $decoded['type'];
if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || (ord($this->encoded[$decoded['start']]) & 0x20)) {
return new Element(substr($this->encoded, $decoded['start'], $decoded['length']));
}
$inmap = $this->ANYmap[$intype];
if (is_string($inmap)) {
return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
}
break;
case $mapping['type'] == self::TYPE_CHOICE:
foreach ($mapping['children'] as $key => $option) {
switch (true) {
case isset($option['constant']) && $option['constant'] == $decoded['constant']:
case !isset($option['constant']) && $option['type'] == $decoded['type']:
$value = $this->asn1map($decoded, $option, $special);
break;
case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE:
$v = $this->asn1map($decoded, $option, $special);
if (isset($v)) {
$value = $v;
}
}
if (isset($value)) {
if (isset($special[$key])) {
$value = call_user_func($special[$key], $value);
}
return array($key => $value);
}
}
return null;
case isset($mapping['implicit']):
case isset($mapping['explicit']):
case $decoded['type'] == $mapping['type']:
break;
default:


switch (true) {
case $decoded['type'] < 18: 
case $decoded['type'] > 30: 
case $mapping['type'] < 18:
case $mapping['type'] > 30:
return null;
}
}

if (isset($mapping['implicit'])) {
$decoded['type'] = $mapping['type'];
}

switch ($decoded['type']) {
case self::TYPE_SEQUENCE:
$map = array();


if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child, $special)) === null) {
return null;
}
}

return $map;
}

$n = count($decoded['content']);
$i = 0;

foreach ($mapping['children'] as $key => $child) {
$maymatch = $i < $n; 
if ($maymatch) {
$temp = $decoded['content'][$i];

if ($child['type'] != self::TYPE_CHOICE) {

$childClass = $tempClass = self::CLASS_UNIVERSAL;
$constant = null;
if (isset($temp['constant'])) {
$tempClass = $temp['type'];
}
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
} elseif (isset($child['constant'])) {
$childClass = self::CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}

if (isset($constant) && isset($temp['constant'])) {

$maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
} else {

$maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
}
}
}

if ($maymatch) {

$candidate = $this->asn1map($temp, $child, $special);
$maymatch = $candidate !== null;
}

if ($maymatch) {

if (isset($special[$key])) {
$candidate = call_user_func($special[$key], $candidate);
}
$map[$key] = $candidate;
$i++;
} elseif (isset($child['default'])) {
$map[$key] = $child['default']; 
} elseif (!isset($child['optional'])) {
return null; 
}
}


return $i < $n ? null: $map;


case self::TYPE_SET:
$map = array();


if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
if (($map[] = $this->asn1map($content, $child, $special)) === null) {
return null;
}
}

return $map;
}

for ($i = 0; $i < count($decoded['content']); $i++) {
$temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) {
$tempClass = $temp['type'];
}

foreach ($mapping['children'] as $key => $child) {
if (isset($map[$key])) {
continue;
}
$maymatch = true;
if ($child['type'] != self::TYPE_CHOICE) {
$childClass = self::CLASS_UNIVERSAL;
$constant = null;
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
} elseif (isset($child['constant'])) {
$childClass = self::CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}

if (isset($constant) && isset($temp['constant'])) {

$maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
} else {

$maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
}
}

if ($maymatch) {

$candidate = $this->asn1map($temp, $child, $special);
$maymatch = $candidate !== null;
}

if (!$maymatch) {
break;
}


if (isset($special[$key])) {
$candidate = call_user_func($special[$key], $candidate);
}
$map[$key] = $candidate;
break;
}
}

foreach ($mapping['children'] as $key => $child) {
if (!isset($map[$key])) {
if (isset($child['default'])) {
$map[$key] = $child['default'];
} elseif (!isset($child['optional'])) {
return null;
}
}
}
return $map;
case self::TYPE_OBJECT_IDENTIFIER:
return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:

if (is_array($decoded['content'])) {
$decoded['content'] = $decoded['content'][0]['content'];
}



if (!is_object($decoded['content'])) {
$decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
}
return $decoded['content'] ? $decoded['content']->format($this->format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
$size = (strlen($decoded['content']) - 1) * 8 - $offset;








$bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false);
for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) {
$current = ord($decoded['content'][$i]);
for ($j = $offset; $j < 8; $j++) {
$bits[] = (bool) ($current & (1 << $j));
}
$offset = 0;
}
$values = array();
$map = array_reverse($mapping['mapping']);
foreach ($map as $i => $value) {
if ($bits[$i]) {
$values[] = $value;
}
}
return $values;
}
case self::TYPE_OCTET_STRING:
return base64_encode($decoded['content']);
case self::TYPE_NULL:
return '';
case self::TYPE_BOOLEAN:
return $decoded['content'];
case self::TYPE_NUMERIC_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_TELETEX_STRING:
case self::TYPE_VIDEOTEX_STRING:
case self::TYPE_IA5_STRING:
case self::TYPE_GRAPHIC_STRING:
case self::TYPE_VISIBLE_STRING:
case self::TYPE_GENERAL_STRING:
case self::TYPE_UNIVERSAL_STRING:
case self::TYPE_UTF8_STRING:
case self::TYPE_BMP_STRING:
return $decoded['content'];
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
$temp = $decoded['content'];
if (isset($mapping['implicit'])) {
$temp = new BigInteger($decoded['content'], -256);
}
if (isset($mapping['mapping'])) {
$temp = (int) $temp->toString();
return isset($mapping['mapping'][$temp]) ?
$mapping['mapping'][$temp] :
false;
}
return $temp;
}
}















function encodeDER($source, $mapping, $special = array())
{
$this->location = array();
return $this->_encode_der($source, $mapping, null, $special);
}











function _encode_der($source, $mapping, $idx = null, $special = array())
{
if ($source instanceof Element) {
return $source->element;
}


if (isset($mapping['default']) && $source === $mapping['default']) {
return '';
}

if (isset($idx)) {
if (isset($special[$idx])) {
$source = call_user_func($special[$idx], $source);
}
$this->location[] = $idx;
}

$tag = $mapping['type'];

switch ($tag) {
case self::TYPE_SET: 
case self::TYPE_SEQUENCE:
$tag|= 0x20; 


if (isset($mapping['min']) && isset($mapping['max'])) {
$value = array();
$child = $mapping['children'];

foreach ($source as $content) {
$temp = $this->_encode_der($content, $child, null, $special);
if ($temp === false) {
return false;
}
$value[]= $temp;
}





if ($mapping['type'] == self::TYPE_SET) {
sort($value);
}
$value = implode('', $value);
break;
}

$value = '';
foreach ($mapping['children'] as $key => $child) {
if (!array_key_exists($key, $source)) {
if (!isset($child['optional'])) {
return false;
}
continue;
}

$temp = $this->_encode_der($source[$key], $child, $key, $special);
if ($temp === false) {
return false;
}



if ($temp === '') {
continue;
}


if (isset($child['constant'])) {









if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
$temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
$value.= $temp;
}
break;
case self::TYPE_CHOICE:
$temp = false;

foreach ($mapping['children'] as $key => $child) {
if (!isset($source[$key])) {
continue;
}

$temp = $this->_encode_der($source[$key], $child, $key, $special);
if ($temp === false) {
return false;
}



if ($temp === '') {
continue;
}

$tag = ord($temp[0]);


if (isset($child['constant'])) {
if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
$temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
}

if (isset($idx)) {
array_pop($this->location);
}

if ($temp && isset($mapping['cast'])) {
$temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
}

return $temp;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if (!isset($mapping['mapping'])) {
if (is_numeric($source)) {
$source = new BigInteger($source);
}
$value = $source->toBytes(true);
} else {
$value = array_search($source, $mapping['mapping']);
if ($value === false) {
return false;
}
$value = new BigInteger($value);
$value = $value->toBytes(true);
}
if (!strlen($value)) {
$value = chr(0);
}
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
$format.= 'mdHis';

$date = new DateTime($source, new DateTimeZone('GMT'));

$date->setTimezone(new DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$bits = array_fill(0, count($mapping['mapping']), 0);
$size = 0;
for ($i = 0; $i < count($mapping['mapping']); $i++) {
if (in_array($mapping['mapping'][$i], $source)) {
$bits[$i] = 1;
$size = $i;
}
}

if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
$size = $mapping['min'] - 1;
}

$offset = 8 - (($size + 1) & 7);
$offset = $offset !== 8 ? $offset : 0;

$value = chr($offset);

for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
unset($bits[$i]);
}

$bits = implode('', array_pad($bits, $size + $offset + 1, 0));
$bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
foreach ($bytes as $byte) {
$value.= chr(bindec($byte));
}

break;
}
case self::TYPE_OCTET_STRING:




$value = base64_decode($source);
break;
case self::TYPE_OBJECT_IDENTIFIER:
$value = $this->_encodeOID($source);
break;
case self::TYPE_ANY:
$loc = $this->location;
if (isset($idx)) {
array_pop($this->location);
}

switch (true) {
case !isset($source):
return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special);
case is_int($source):
case $source instanceof BigInteger:
return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special);
case is_float($source):
return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special);
case is_bool($source):
return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special);
case is_array($source) && count($source) == 1:
$typename = implode('', array_keys($source));
$outtype = array_search($typename, $this->ANYmap, true);
if ($outtype !== false) {
return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
}
}

$filters = $this->filters;
foreach ($loc as $part) {
if (!isset($filters[$part])) {
$filters = false;
break;
}
$filters = $filters[$part];
}
if ($filters === false) {
user_error('No filters defined for ' . implode('/', $loc));
return false;
}
return $this->_encode_der($source, $filters + $mapping, null, $special);
case self::TYPE_NULL:
$value = '';
break;
case self::TYPE_NUMERIC_STRING:
case self::TYPE_TELETEX_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_UNIVERSAL_STRING:
case self::TYPE_UTF8_STRING:
case self::TYPE_BMP_STRING:
case self::TYPE_IA5_STRING:
case self::TYPE_VISIBLE_STRING:
case self::TYPE_VIDEOTEX_STRING:
case self::TYPE_GRAPHIC_STRING:
case self::TYPE_GENERAL_STRING:
$value = $source;
break;
case self::TYPE_BOOLEAN:
$value = $source ? "\xFF" : "\x00";
break;
default:
user_error('Mapping provides no type definition for ' . implode('/', $this->location));
return false;
}

if (isset($idx)) {
array_pop($this->location);
}

if (isset($mapping['cast'])) {
if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) {
$value = chr($tag) . $this->_encodeLength(strlen($value)) . $value;
$tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
} else {
$tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
}
}

return chr($tag) . $this->_encodeLength(strlen($value)) . $value;
}











function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}

$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}










function _decodeOID($content)
{
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}

$oid = array();
$pos = 0;
$len = strlen($content);

if ($len > 4096) {

return false;
}

if (ord($content[$len - 1]) & 0x80) {
return false;
}

$n = new BigInteger();
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$oid[] = $n;
$n = new BigInteger();
}
}
$part1 = array_shift($oid);
$first = floor(ord($content[0]) / 40);






if ($first <= 2) { 
array_unshift($oid, ord($content[0]) % 40);
array_unshift($oid, $first);
} else {
array_unshift($oid, $part1->subtract($eighty));
array_unshift($oid, 2);
}

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










function _encodeOID($source)
{
static $mask, $zero, $forty;
if (!$mask) {
$mask = new BigInteger(0x7F);
$zero = new BigInteger();
$forty = new BigInteger(40);
}

$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$parts = explode('.', $oid);
$part1 = array_shift($parts);
$part2 = array_shift($parts);

$first = new BigInteger($part1);
$first = $first->multiply($forty);
$first = $first->add(new BigInteger($part2));

array_unshift($parts, $first->toString());

$value = '';
foreach ($parts as $part) {
if (!$part) {
$temp = "\0";
} else {
$temp = '';
$part = new BigInteger($part);
while (!$part->equals($zero)) {
$submask = $part->bitwise_and($mask);
$submask->setPrecision(8);
$temp = (chr(0x80) | $submask->toBytes()) . $temp;
$part = $part->bitwise_rightShift(7);
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
$value.= $temp;
}

return $value;
}











function _decodeTime($content, $tag)
{








$format = 'YmdHis';

if ($tag == self::TYPE_UTC_TIME) {



if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
$content = $matches[1] . '00' . $matches[2];
}
$prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
$format.= '.u';
}

if ($content[strlen($content) - 1] == 'Z') {
$content = substr($content, 0, -1) . '+0000';
}

if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
$format.= 'O';
}



return @DateTime::createFromFormat($format, $content);
}









function setTimeFormat($format)
{
$this->format = $format;
}









function loadOIDs($oids)
{
$this->oids = $oids;
}









function loadFilters($filters)
{
$this->filters = $filters;
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}













function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING)
{
if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
return false;
}
$insize = $this->stringTypeSize[$from];
$outsize = $this->stringTypeSize[$to];
$inlength = strlen($in);
$out = '';

for ($i = 0; $i < $inlength;) {
if ($inlength - $i < $insize) {
return false;
}


$c = ord($in[$i++]);
switch (true) {
case $insize == 4:
$c = ($c << 8) | ord($in[$i++]);
$c = ($c << 8) | ord($in[$i++]);
case $insize == 2:
$c = ($c << 8) | ord($in[$i++]);
case $insize == 1:
break;
case ($c & 0x80) == 0x00:
break;
case ($c & 0x40) == 0x00:
return false;
default:
$bit = 6;
do {
if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
return false;
}
$c = ($c << 6) | (ord($in[$i++]) & 0x3F);
$bit += 5;
$mask = 1 << $bit;
} while ($c & $bit);
$c &= $mask - 1;
break;
}


$v = '';
switch (true) {
case $outsize == 4:
$v .= chr($c & 0xFF);
$c >>= 8;
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 2:
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 1:
$v .= chr($c & 0xFF);
$c >>= 8;
if ($c) {
return false;
}
break;
case ($c & (PHP_INT_SIZE == 8 ? 0x80000000 : (1 << 31))) != 0:
return false;
case $c >= 0x04000000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x04000000;
case $c >= 0x00200000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00200000;
case $c >= 0x00010000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00010000;
case $c >= 0x00000800:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00000800;
case $c >= 0x00000080:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x000000C0;
default:
$v .= chr($c);
break;
}
$out .= strrev($v);
}
return $out;
}
}
<?php



















namespace phpseclib\File;








class ANSI
{






var $max_x;







var $max_y;







var $max_history;







var $history;







var $history_attrs;







var $x;







var $y;







var $old_x;







var $old_y;







var $base_attr_cell;







var $attr_cell;







var $attr_row;







var $screen;







var $attrs;







var $ansi;







var $tokenization;







function __construct()
{
$attr_cell = new \stdClass();
$attr_cell->bold = false;
$attr_cell->underline = false;
$attr_cell->blink = false;
$attr_cell->background = 'black';
$attr_cell->foreground = 'white';
$attr_cell->reverse = false;
$this->base_attr_cell = clone $attr_cell;
$this->attr_cell = clone $attr_cell;

$this->setHistory(200);
$this->setDimensions(80, 24);
}










function setDimensions($x, $y)
{
$this->max_x = $x - 1;
$this->max_y = $y - 1;
$this->x = $this->y = 0;
$this->history = $this->history_attrs = array();
$this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell);
$this->screen = array_fill(0, $this->max_y + 1, '');
$this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
$this->ansi = '';
}







function setHistory($history)
{
$this->max_history = $history;
}







function loadString($source)
{
$this->setDimensions($this->max_x + 1, $this->max_y + 1);
$this->appendString($source);
}







function appendString($source)
{
$this->tokenization = array('');
for ($i = 0; $i < strlen($source); $i++) {
if (strlen($this->ansi)) {
$this->ansi.= $source[$i];
$chr = ord($source[$i]);


switch (true) {
case $this->ansi == "\x1B=":
$this->ansi = '';
continue 2;
case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['):
case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126:
break;
default:
continue 2;
}
$this->tokenization[] = $this->ansi;
$this->tokenization[] = '';

switch ($this->ansi) {
case "\x1B[H": 
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $this->y = 0;
break;
case "\x1B[J": 
$this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y));
$this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, ''));

$this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y));
$this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row));

if (count($this->history) == $this->max_history) {
array_shift($this->history);
array_shift($this->history_attrs);
}
case "\x1B[K": 
$this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);

array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell));
break;
case "\x1B[2K": 
$this->screen[$this->y] = str_repeat(' ', $this->x);
$this->attrs[$this->y] = $this->attr_row;
break;
case "\x1B[?1h": 
case "\x1B[?25h": 
case "\x1B(B": 
break;
case "\x1BE": 
$this->_newLine();
$this->x = 0;
break;
default:
switch (true) {
case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): 
$this->old_y = $this->y;
$this->y+= $match[1];
break;
case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): 
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $match[2] - 1;
$this->y = $match[1] - 1;
break;
case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): 
$this->old_x = $this->x;
$this->x+= $match[1];
break;
case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): 
$this->old_x = $this->x;
$this->x-= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
break;
case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): 
break;
case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): 
$attr_cell = &$this->attr_cell;
$mods = explode(';', $match[1]);
foreach ($mods as $mod) {
switch ($mod) {
case '':
case '0': 
$attr_cell = clone $this->base_attr_cell;
break;
case '1': 
$attr_cell->bold = true;
break;
case '4': 
$attr_cell->underline = true;
break;
case '5': 
$attr_cell->blink = true;
break;
case '7': 
$attr_cell->reverse = !$attr_cell->reverse;
$temp = $attr_cell->background;
$attr_cell->background = $attr_cell->foreground;
$attr_cell->foreground = $temp;
break;
default: 

$front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' };

$back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
switch ($mod) {

case '30': $front = 'black'; break;
case '31': $front = 'red'; break;
case '32': $front = 'green'; break;
case '33': $front = 'yellow'; break;
case '34': $front = 'blue'; break;
case '35': $front = 'magenta'; break;
case '36': $front = 'cyan'; break;
case '37': $front = 'white'; break;

case '40': $back = 'black'; break;
case '41': $back = 'red'; break;
case '42': $back = 'green'; break;
case '43': $back = 'yellow'; break;
case '44': $back = 'blue'; break;
case '45': $back = 'magenta'; break;
case '46': $back = 'cyan'; break;
case '47': $back = 'white'; break;


default:

$this->ansi = '';
break 2;
}
}
}
break;
default:

}
}
$this->ansi = '';
continue;
}

$this->tokenization[count($this->tokenization) - 1].= $source[$i];
switch ($source[$i]) {
case "\r":
$this->x = 0;
break;
case "\n":
$this->_newLine();
break;
case "\x08": 
if ($this->x) {
$this->x--;
$this->attrs[$this->y][$this->x] = clone $this->base_attr_cell;
$this->screen[$this->y] = substr_replace(
$this->screen[$this->y],
$source[$i],
$this->x,
1
);
}
break;
case "\x0F": 
break;
case "\x1B": 
$this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1);



$this->ansi.= "\x1B";
break;
default:
$this->attrs[$this->y][$this->x] = clone $this->attr_cell;
if ($this->x > strlen($this->screen[$this->y])) {
$this->screen[$this->y] = str_repeat(' ', $this->x);
}
$this->screen[$this->y] = substr_replace(
$this->screen[$this->y],
$source[$i],
$this->x,
1
);

if ($this->x > $this->max_x) {
$this->x = 0;
$this->_newLine();
} else {
$this->x++;
}
}
}
}








function _newLine()
{




while ($this->y >= $this->max_y) {
$this->history = array_merge($this->history, array(array_shift($this->screen)));
$this->screen[] = '';

$this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs)));
$this->attrs[] = $this->attr_row;

if (count($this->history) >= $this->max_history) {
array_shift($this->history);
array_shift($this->history_attrs);
}

$this->y--;
}
$this->y++;
}







function _processCoordinate($last_attr, $cur_attr, $char)
{
$output = '';

if ($last_attr != $cur_attr) {
$close = $open = '';
if ($last_attr->foreground != $cur_attr->foreground) {
if ($cur_attr->foreground != 'white') {
$open.= '<span style="color: ' . $cur_attr->foreground . '">';
}
if ($last_attr->foreground != 'white') {
$close = '</span>' . $close;
}
}
if ($last_attr->background != $cur_attr->background) {
if ($cur_attr->background != 'black') {
$open.= '<span style="background: ' . $cur_attr->background . '">';
}
if ($last_attr->background != 'black') {
$close = '</span>' . $close;
}
}
if ($last_attr->bold != $cur_attr->bold) {
if ($cur_attr->bold) {
$open.= '<b>';
} else {
$close = '</b>' . $close;
}
}
if ($last_attr->underline != $cur_attr->underline) {
if ($cur_attr->underline) {
$open.= '<u>';
} else {
$close = '</u>' . $close;
}
}
if ($last_attr->blink != $cur_attr->blink) {
if ($cur_attr->blink) {
$open.= '<blink>';
} else {
$close = '</blink>' . $close;
}
}
$output.= $close . $open;
}

$output.= htmlspecialchars($char);

return $output;
}







function _getScreen()
{
$output = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i <= $this->max_y; $i++) {
for ($j = 0; $j <= $this->max_x; $j++) {
$cur_attr = $this->attrs[$i][$j];
$output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : '');
$last_attr = $this->attrs[$i][$j];
}
$output.= "\r\n";
}
$output = substr($output, 0, -2);

$output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, '');
return rtrim($output);
}







function getScreen()
{
return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->_getScreen() . '</pre>';
}







function getHistory()
{
$scrollback = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i < count($this->history); $i++) {
for ($j = 0; $j <= $this->max_x + 1; $j++) {
$cur_attr = $this->history_attrs[$i][$j];
$scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : '');
$last_attr = $this->history_attrs[$i][$j];
}
$scrollback.= "\r\n";
}
$base_attr_cell = $this->base_attr_cell;
$this->base_attr_cell = $last_attr;
$scrollback.= $this->_getScreen();
$this->base_attr_cell = $base_attr_cell;

return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>';
}
}
<?php

























namespace phpseclib\File;

use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;








class X509
{







const VALIDATE_SIGNATURE_BY_CA = 1;








const DN_ARRAY = 0;



const DN_STRING = 1;



const DN_ASN1 = 2;



const DN_OPENSSL = 3;



const DN_CANON = 4;



const DN_HASH = 5;













const FORMAT_PEM = 0;



const FORMAT_DER = 1;





const FORMAT_SPKAC = 2;





const FORMAT_AUTO_DETECT = 3;






const ATTR_ALL = -1; 
const ATTR_APPEND = -2; 
const ATTR_REPLACE = -3; 







var $Certificate;






var $DirectoryString;
var $PKCS9String;
var $AttributeValue;
var $Extensions;
var $KeyUsage;
var $ExtKeyUsageSyntax;
var $BasicConstraints;
var $KeyIdentifier;
var $CRLDistributionPoints;
var $AuthorityKeyIdentifier;
var $CertificatePolicies;
var $AuthorityInfoAccessSyntax;
var $SubjectInfoAccessSyntax;
var $SubjectAltName;
var $SubjectDirectoryAttributes;
var $PrivateKeyUsagePeriod;
var $IssuerAltName;
var $PolicyMappings;
var $NameConstraints;

var $CPSuri;
var $UserNotice;

var $netscape_cert_type;
var $netscape_comment;
var $netscape_ca_policy_url;

var $Name;
var $RelativeDistinguishedName;
var $CRLNumber;
var $CRLReason;
var $IssuingDistributionPoint;
var $InvalidityDate;
var $CertificateIssuer;
var $HoldInstructionCode;
var $SignedPublicKeyAndChallenge;







var $PostalAddress;








var $CertificationRequest;







var $CertificateList;







var $dn;







var $publicKey;







var $privateKey;








var $oids;







var $CAs;







var $currentCert;










var $signatureSubject;







var $startDate;







var $endDate;







var $serialNumber;










var $currentKeyIdentifier;







var $caFlag = false;







var $challenge;







static $recur_limit = 5;







static $disable_url_fetch = false;







function __construct()
{



$this->DirectoryString = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'teletexString' => array('type' => ASN1::TYPE_TELETEX_STRING),
'printableString' => array('type' => ASN1::TYPE_PRINTABLE_STRING),
'universalString' => array('type' => ASN1::TYPE_UNIVERSAL_STRING),
'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING),
'bmpString' => array('type' => ASN1::TYPE_BMP_STRING)
)
);

$this->PKCS9String = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'ia5String' => array('type' => ASN1::TYPE_IA5_STRING),
'directoryString' => $this->DirectoryString
)
);

$this->AttributeValue = array('type' => ASN1::TYPE_ANY);

$AttributeType = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);

$AttributeTypeAndValue = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => $AttributeType,
'value'=> $this->AttributeValue
)
);








$this->RelativeDistinguishedName = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $AttributeTypeAndValue
);


$RDNSequence = array(
'type' => ASN1::TYPE_SEQUENCE,

'min' => 0,
'max' => -1,
'children' => $this->RelativeDistinguishedName
);

$this->Name = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'rdnSequence' => $RDNSequence
)
);


$AlgorithmIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
'parameters' => array(
'type' => ASN1::TYPE_ANY,
'optional' => true
)
)
);








$Extension = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'extnId' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
'critical' => array(
'type' => ASN1::TYPE_BOOLEAN,
'optional' => true,
'default' => false
),
'extnValue' => array('type' => ASN1::TYPE_OCTET_STRING)
)
);

$this->Extensions = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,

'max' => -1,

'children' => $Extension
);

$SubjectPublicKeyInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => $AlgorithmIdentifier,
'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING)
)
);

$UniqueIdentifier = array('type' => ASN1::TYPE_BIT_STRING);

$Time = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'utcTime' => array('type' => ASN1::TYPE_UTC_TIME),
'generalTime' => array('type' => ASN1::TYPE_GENERALIZED_TIME)
)
);


$Validity = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'notBefore' => $Time,
'notAfter' => $Time
)
);

$CertificateSerialNumber = array('type' => ASN1::TYPE_INTEGER);

$Version = array(
'type' => ASN1::TYPE_INTEGER,
'mapping' => array('v1', 'v2', 'v3')
);


$TBSCertificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(


'version' => array(
'constant' => 0,
'optional' => true,
'explicit' => true,
'default' => 'v1'
) + $Version,
'serialNumber' => $CertificateSerialNumber,
'signature' => $AlgorithmIdentifier,
'issuer' => $this->Name,
'validity' => $Validity,
'subject' => $this->Name,
'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,

'issuerUniqueID' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $UniqueIdentifier,
'subjectUniqueID' => array(
'constant' => 2,
'optional' => true,
'implicit' => true
) + $UniqueIdentifier,


'extensions' => array(
'constant' => 3,
'optional' => true,
'explicit' => true
) + $this->Extensions
)
);

$this->Certificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'tbsCertificate' => $TBSCertificate,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' => array('type' => ASN1::TYPE_BIT_STRING)
)
);

$this->KeyUsage = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'digitalSignature',
'nonRepudiation',
'keyEncipherment',
'dataEncipherment',
'keyAgreement',
'keyCertSign',
'cRLSign',
'encipherOnly',
'decipherOnly'
)
);

$this->BasicConstraints = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'cA' => array(
'type' => ASN1::TYPE_BOOLEAN,
'optional' => true,
'default' => false
),
'pathLenConstraint' => array(
'type' => ASN1::TYPE_INTEGER,
'optional' => true
)
)
);

$this->KeyIdentifier = array('type' => ASN1::TYPE_OCTET_STRING);

$OrganizationalUnitNames = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, 
'children' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
);

$PersonalName = array(
'type' => ASN1::TYPE_SET,
'children' => array(
'surname' => array(
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 0,
'optional' => true,
'implicit' => true
),
'given-name' => array(
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 1,
'optional' => true,
'implicit' => true
),
'initials' => array(
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 2,
'optional' => true,
'implicit' => true
),
'generation-qualifier' => array(
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 3,
'optional' => true,
'implicit' => true
)
)
);

$NumericUserIdentifier = array('type' => ASN1::TYPE_NUMERIC_STRING);

$OrganizationName = array('type' => ASN1::TYPE_PRINTABLE_STRING);

$PrivateDomainName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING),
'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
)
);

$TerminalIdentifier = array('type' => ASN1::TYPE_PRINTABLE_STRING);

$NetworkAddress = array('type' => ASN1::TYPE_NUMERIC_STRING);

$AdministrationDomainName = array(
'type' => ASN1::TYPE_CHOICE,


'class' => ASN1::CLASS_APPLICATION,
'cast' => 2,
'children' => array(
'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING),
'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
)
);

$CountryName = array(
'type' => ASN1::TYPE_CHOICE,


'class' => ASN1::CLASS_APPLICATION,
'cast' => 1,
'children' => array(
'x121-dcc-code' => array('type' => ASN1::TYPE_NUMERIC_STRING),
'iso-3166-alpha2-code' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
)
);

$AnotherName = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type-id' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
'value' => array(
'type' => ASN1::TYPE_ANY,
'constant' => 0,
'optional' => true,
'explicit' => true
)
)
);

$ExtensionAttribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'extension-attribute-type' => array(
'type' => ASN1::TYPE_PRINTABLE_STRING,
'constant' => 0,
'optional' => true,
'implicit' => true
),
'extension-attribute-value' => array(
'type' => ASN1::TYPE_ANY,
'constant' => 1,
'optional' => true,
'explicit' => true
)
)
);

$ExtensionAttributes = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => 256, 
'children' => $ExtensionAttribute
);

$BuiltInDomainDefinedAttribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => array('type' => ASN1::TYPE_PRINTABLE_STRING),
'value' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
)
);

$BuiltInDomainDefinedAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 4, 
'children' => $BuiltInDomainDefinedAttribute
);

$BuiltInStandardAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'country-name' => array('optional' => true) + $CountryName,
'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
'network-address' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $NetworkAddress,
'terminal-identifier' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $TerminalIdentifier,
'private-domain-name' => array(
'constant' => 2,
'optional' => true,
'explicit' => true
) + $PrivateDomainName,
'organization-name' => array(
'constant' => 3,
'optional' => true,
'implicit' => true
) + $OrganizationName,
'numeric-user-identifier' => array(
'constant' => 4,
'optional' => true,
'implicit' => true
) + $NumericUserIdentifier,
'personal-name' => array(
'constant' => 5,
'optional' => true,
'implicit' => true
) + $PersonalName,
'organizational-unit-names' => array(
'constant' => 6,
'optional' => true,
'implicit' => true
) + $OrganizationalUnitNames
)
);

$ORAddress = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'built-in-standard-attributes' => $BuiltInStandardAttributes,
'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
'extension-attributes' => array('optional' => true) + $ExtensionAttributes
)
);

$EDIPartyName = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'nameAssigner' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $this->DirectoryString,


'partyName' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $this->DirectoryString
)
);

$GeneralName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'otherName' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $AnotherName,
'rfc822Name' => array(
'type' => ASN1::TYPE_IA5_STRING,
'constant' => 1,
'optional' => true,
'implicit' => true
),
'dNSName' => array(
'type' => ASN1::TYPE_IA5_STRING,
'constant' => 2,
'optional' => true,
'implicit' => true
),
'x400Address' => array(
'constant' => 3,
'optional' => true,
'implicit' => true
) + $ORAddress,
'directoryName' => array(
'constant' => 4,
'optional' => true,
'explicit' => true
) + $this->Name,
'ediPartyName' => array(
'constant' => 5,
'optional' => true,
'implicit' => true
) + $EDIPartyName,
'uniformResourceIdentifier' => array(
'type' => ASN1::TYPE_IA5_STRING,
'constant' => 6,
'optional' => true,
'implicit' => true
),
'iPAddress' => array(
'type' => ASN1::TYPE_OCTET_STRING,
'constant' => 7,
'optional' => true,
'implicit' => true
),
'registeredID' => array(
'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
'constant' => 8,
'optional' => true,
'implicit' => true
)
)
);

$GeneralNames = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $GeneralName
);

$this->IssuerAltName = $GeneralNames;

$ReasonFlags = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'unused',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',
'privilegeWithdrawn',
'aACompromise'
)
);

$DistributionPointName = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'fullName' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $GeneralNames,
'nameRelativeToCRLIssuer' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $this->RelativeDistinguishedName
)
);

$DistributionPoint = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'distributionPoint' => array(
'constant' => 0,
'optional' => true,
'explicit' => true
) + $DistributionPointName,
'reasons' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $ReasonFlags,
'cRLIssuer' => array(
'constant' => 2,
'optional' => true,
'implicit' => true
) + $GeneralNames
)
);

$this->CRLDistributionPoints = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $DistributionPoint
);

$this->AuthorityKeyIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'keyIdentifier' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $this->KeyIdentifier,
'authorityCertIssuer' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $GeneralNames,
'authorityCertSerialNumber' => array(
'constant' => 2,
'optional' => true,
'implicit' => true
) + $CertificateSerialNumber
)
);

$PolicyQualifierId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);

$PolicyQualifierInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'policyQualifierId' => $PolicyQualifierId,
'qualifier' => array('type' => ASN1::TYPE_ANY)
)
);

$CertPolicyId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);

$PolicyInformation = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'policyIdentifier' => $CertPolicyId,
'policyQualifiers' => array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 0,
'max' => -1,
'optional' => true,
'children' => $PolicyQualifierInfo
)
)
);

$this->CertificatePolicies = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $PolicyInformation
);

$this->PolicyMappings = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'issuerDomainPolicy' => $CertPolicyId,
'subjectDomainPolicy' => $CertPolicyId
)
)
);

$KeyPurposeId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);

$this->ExtKeyUsageSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $KeyPurposeId
);

$AccessDescription = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'accessMethod' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
'accessLocation' => $GeneralName
)
);

$this->AuthorityInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);

$this->SubjectInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);

$this->SubjectAltName = $GeneralNames;

$this->PrivateKeyUsagePeriod = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'notBefore' => array(
'constant' => 0,
'optional' => true,
'implicit' => true,
'type' => ASN1::TYPE_GENERALIZED_TIME),
'notAfter' => array(
'constant' => 1,
'optional' => true,
'implicit' => true,
'type' => ASN1::TYPE_GENERALIZED_TIME)
)
);

$BaseDistance = array('type' => ASN1::TYPE_INTEGER);

$GeneralSubtree = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'base' => $GeneralName,
'minimum' => array(
'constant' => 0,
'optional' => true,
'implicit' => true,
'default' => new BigInteger(0)
) + $BaseDistance,
'maximum' => array(
'constant' => 1,
'optional' => true,
'implicit' => true,
) + $BaseDistance
)
);

$GeneralSubtrees = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $GeneralSubtree
);

$this->NameConstraints = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'permittedSubtrees' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $GeneralSubtrees,
'excludedSubtrees' => array(
'constant' => 1,
'optional' => true,
'implicit' => true
) + $GeneralSubtrees
)
);

$this->CPSuri = array('type' => ASN1::TYPE_IA5_STRING);

$DisplayText = array(
'type' => ASN1::TYPE_CHOICE,
'children' => array(
'ia5String' => array('type' => ASN1::TYPE_IA5_STRING),
'visibleString' => array('type' => ASN1::TYPE_VISIBLE_STRING),
'bmpString' => array('type' => ASN1::TYPE_BMP_STRING),
'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING)
)
);

$NoticeReference = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'organization' => $DisplayText,
'noticeNumbers' => array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => 200,
'children' => array('type' => ASN1::TYPE_INTEGER)
)
)
);

$this->UserNotice = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'noticeRef' => array(
'optional' => true,
'implicit' => true
) + $NoticeReference,
'explicitText' => array(
'optional' => true,
'implicit' => true
) + $DisplayText
)
);


$this->netscape_cert_type = array(
'type' => ASN1::TYPE_BIT_STRING,
'mapping' => array(
'SSLClient',
'SSLServer',
'Email',
'ObjectSigning',
'Reserved',
'SSLCA',
'EmailCA',
'ObjectSigningCA'
)
);

$this->netscape_comment = array('type' => ASN1::TYPE_IA5_STRING);
$this->netscape_ca_policy_url = array('type' => ASN1::TYPE_IA5_STRING);



$Attribute = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'type' => $AttributeType,
'value'=> array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $this->AttributeValue
)
)
);

$this->SubjectDirectoryAttributes = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $Attribute
);



$Attributes = array(
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => $Attribute
);

$CertificationRequestInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'version' => array(
'type' => ASN1::TYPE_INTEGER,
'mapping' => array('v1')
),
'subject' => $this->Name,
'subjectPKInfo' => $SubjectPublicKeyInfo,
'attributes' => array(
'constant' => 0,
'optional' => true,
'implicit' => true
) + $Attributes,
)
);

$this->CertificationRequest = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'certificationRequestInfo' => $CertificationRequestInfo,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' => array('type' => ASN1::TYPE_BIT_STRING)
)
);

$RevokedCertificate = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'userCertificate' => $CertificateSerialNumber,
'revocationDate' => $Time,
'crlEntryExtensions' => array(
'optional' => true
) + $this->Extensions
)
);

$TBSCertList = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'version' => array(
'optional' => true,
'default' => 'v1'
) + $Version,
'signature' => $AlgorithmIdentifier,
'issuer' => $this->Name,
'thisUpdate' => $Time,
'nextUpdate' => array(
'optional' => true
) + $Time,
'revokedCertificates' => array(
'type' => ASN1::TYPE_SEQUENCE,
'optional' => true,
'min' => 0,
'max' => -1,
'children' => $RevokedCertificate
),
'crlExtensions' => array(
'constant' => 0,
'optional' => true,
'explicit' => true
) + $this->Extensions
)
);

$this->CertificateList = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'tbsCertList' => $TBSCertList,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' => array('type' => ASN1::TYPE_BIT_STRING)
)
);

$this->CRLNumber = array('type' => ASN1::TYPE_INTEGER);

$this->CRLReason = array('type' => ASN1::TYPE_ENUMERATED,
'mapping' => array(
'unspecified',
'keyCompromise',
'cACompromise',
'affiliationChanged',
'superseded',
'cessationOfOperation',
'certificateHold',

8 => 'removeFromCRL',
'privilegeWithdrawn',
'aACompromise'
)
);

$this->IssuingDistributionPoint = array('type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'distributionPoint' => array(
'constant' => 0,
'optional' => true,
'explicit' => true
) + $DistributionPointName,
'onlyContainsUserCerts' => array(
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 1,
'optional' => true,
'default' => false,
'implicit' => true
),
'onlyContainsCACerts' => array(
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 2,
'optional' => true,
'default' => false,
'implicit' => true
),
'onlySomeReasons' => array(
'constant' => 3,
'optional' => true,
'implicit' => true
) + $ReasonFlags,
'indirectCRL' => array(
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 4,
'optional' => true,
'default' => false,
'implicit' => true
),
'onlyContainsAttributeCerts' => array(
'type' => ASN1::TYPE_BOOLEAN,
'constant' => 5,
'optional' => true,
'default' => false,
'implicit' => true
)
)
);

$this->InvalidityDate = array('type' => ASN1::TYPE_GENERALIZED_TIME);

$this->CertificateIssuer = $GeneralNames;

$this->HoldInstructionCode = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);

$PublicKeyAndChallenge = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'spki' => $SubjectPublicKeyInfo,
'challenge' => array('type' => ASN1::TYPE_IA5_STRING)
)
);

$this->SignedPublicKeyAndChallenge = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'publicKeyAndChallenge' => $PublicKeyAndChallenge,
'signatureAlgorithm' => $AlgorithmIdentifier,
'signature' => array('type' => ASN1::TYPE_BIT_STRING)
)
);

$this->PostalAddress = array(
'type' => ASN1::TYPE_SEQUENCE,
'optional' => true,
'min' => 1,
'max' => -1,
'children' => $this->DirectoryString
);


$this->oids = array(
'1.3.6.1.5.5.7' => 'id-pkix',
'1.3.6.1.5.5.7.1' => 'id-pe',
'1.3.6.1.5.5.7.2' => 'id-qt',
'1.3.6.1.5.5.7.3' => 'id-kp',
'1.3.6.1.5.5.7.48' => 'id-ad',
'1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
'1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
'1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
'1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
'1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
'1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
'2.5.4' => 'id-at',
'2.5.4.41' => 'id-at-name',
'2.5.4.4' => 'id-at-surname',
'2.5.4.42' => 'id-at-givenName',
'2.5.4.43' => 'id-at-initials',
'2.5.4.44' => 'id-at-generationQualifier',
'2.5.4.3' => 'id-at-commonName',
'2.5.4.7' => 'id-at-localityName',
'2.5.4.8' => 'id-at-stateOrProvinceName',
'2.5.4.10' => 'id-at-organizationName',
'2.5.4.11' => 'id-at-organizationalUnitName',
'2.5.4.12' => 'id-at-title',
'2.5.4.13' => 'id-at-description',
'2.5.4.46' => 'id-at-dnQualifier',
'2.5.4.6' => 'id-at-countryName',
'2.5.4.5' => 'id-at-serialNumber',
'2.5.4.65' => 'id-at-pseudonym',
'2.5.4.17' => 'id-at-postalCode',
'2.5.4.9' => 'id-at-streetAddress',
'2.5.4.45' => 'id-at-uniqueIdentifier',
'2.5.4.72' => 'id-at-role',
'2.5.4.16' => 'id-at-postalAddress',
'1.3.6.1.4.1.311.60.2.1.3' => 'jurisdictionOfIncorporationCountryName',
'1.3.6.1.4.1.311.60.2.1.2' => 'jurisdictionOfIncorporationStateOrProvinceName',
'1.3.6.1.4.1.311.60.2.1.1' => 'jurisdictionLocalityName',
'2.5.4.15' => 'id-at-businessCategory',

'0.9.2342.19200300.100.1.25' => 'id-domainComponent',
'1.2.840.113549.1.9' => 'pkcs-9',
'1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress',
'2.5.29' => 'id-ce',
'2.5.29.35' => 'id-ce-authorityKeyIdentifier',
'2.5.29.14' => 'id-ce-subjectKeyIdentifier',
'2.5.29.15' => 'id-ce-keyUsage',
'2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
'2.5.29.32' => 'id-ce-certificatePolicies',
'2.5.29.32.0' => 'anyPolicy',

'2.5.29.33' => 'id-ce-policyMappings',
'2.5.29.17' => 'id-ce-subjectAltName',
'2.5.29.18' => 'id-ce-issuerAltName',
'2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
'2.5.29.19' => 'id-ce-basicConstraints',
'2.5.29.30' => 'id-ce-nameConstraints',
'2.5.29.36' => 'id-ce-policyConstraints',
'2.5.29.31' => 'id-ce-cRLDistributionPoints',
'2.5.29.37' => 'id-ce-extKeyUsage',
'2.5.29.37.0' => 'anyExtendedKeyUsage',
'1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
'1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
'1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
'1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
'1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
'1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
'2.5.29.54' => 'id-ce-inhibitAnyPolicy',
'2.5.29.46' => 'id-ce-freshestCRL',
'1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
'1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
'2.5.29.20' => 'id-ce-cRLNumber',
'2.5.29.28' => 'id-ce-issuingDistributionPoint',
'2.5.29.27' => 'id-ce-deltaCRLIndicator',
'2.5.29.21' => 'id-ce-cRLReasons',
'2.5.29.29' => 'id-ce-certificateIssuer',
'2.5.29.23' => 'id-ce-holdInstructionCode',
'1.2.840.10040.2' => 'holdInstruction',
'1.2.840.10040.2.1' => 'id-holdinstruction-none',
'1.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
'1.2.840.10040.2.3' => 'id-holdinstruction-reject',
'2.5.29.24' => 'id-ce-invalidityDate',

'1.2.840.113549.2.2' => 'md2',
'1.2.840.113549.2.5' => 'md5',
'1.3.14.3.2.26' => 'id-sha1',
'1.2.840.10040.4.1' => 'id-dsa',
'1.2.840.10040.4.3' => 'id-dsa-with-sha1',
'1.2.840.113549.1.1' => 'pkcs-1',
'1.2.840.113549.1.1.1' => 'rsaEncryption',
'1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
'1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
'1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
'1.2.840.10046.2.1' => 'dhpublicnumber',
'2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
'1.2.840.10045' => 'ansi-X9-62',
'1.2.840.10045.4' => 'id-ecSigType',
'1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
'1.2.840.10045.1' => 'id-fieldType',
'1.2.840.10045.1.1' => 'prime-field',
'1.2.840.10045.1.2' => 'characteristic-two-field',
'1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
'1.2.840.10045.1.2.3.1' => 'gnBasis',
'1.2.840.10045.1.2.3.2' => 'tpBasis',
'1.2.840.10045.1.2.3.3' => 'ppBasis',
'1.2.840.10045.2' => 'id-publicKeyType',
'1.2.840.10045.2.1' => 'id-ecPublicKey',
'1.2.840.10045.3' => 'ellipticCurve',
'1.2.840.10045.3.0' => 'c-TwoCurve',
'1.2.840.10045.3.0.1' => 'c2pnb163v1',
'1.2.840.10045.3.0.2' => 'c2pnb163v2',
'1.2.840.10045.3.0.3' => 'c2pnb163v3',
'1.2.840.10045.3.0.4' => 'c2pnb176w1',
'1.2.840.10045.3.0.5' => 'c2pnb191v1',
'1.2.840.10045.3.0.6' => 'c2pnb191v2',
'1.2.840.10045.3.0.7' => 'c2pnb191v3',
'1.2.840.10045.3.0.8' => 'c2pnb191v4',
'1.2.840.10045.3.0.9' => 'c2pnb191v5',
'1.2.840.10045.3.0.10' => 'c2pnb208w1',
'1.2.840.10045.3.0.11' => 'c2pnb239v1',
'1.2.840.10045.3.0.12' => 'c2pnb239v2',
'1.2.840.10045.3.0.13' => 'c2pnb239v3',
'1.2.840.10045.3.0.14' => 'c2pnb239v4',
'1.2.840.10045.3.0.15' => 'c2pnb239v5',
'1.2.840.10045.3.0.16' => 'c2pnb272w1',
'1.2.840.10045.3.0.17' => 'c2pnb304w1',
'1.2.840.10045.3.0.18' => 'c2pnb359v1',
'1.2.840.10045.3.0.19' => 'c2pnb368w1',
'1.2.840.10045.3.0.20' => 'c2pnb431r1',
'1.2.840.10045.3.1' => 'primeCurve',
'1.2.840.10045.3.1.1' => 'prime192v1',
'1.2.840.10045.3.1.2' => 'prime192v2',
'1.2.840.10045.3.1.3' => 'prime192v3',
'1.2.840.10045.3.1.4' => 'prime239v1',
'1.2.840.10045.3.1.5' => 'prime239v2',
'1.2.840.10045.3.1.6' => 'prime239v3',
'1.2.840.10045.3.1.7' => 'prime256v1',
'1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
'1.2.840.113549.1.1.9' => 'id-pSpecified',
'1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
'1.2.840.113549.1.1.8' => 'id-mgf1',
'1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
'1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
'1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
'1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
'2.16.840.1.101.3.4.2.4' => 'id-sha224',
'2.16.840.1.101.3.4.2.1' => 'id-sha256',
'2.16.840.1.101.3.4.2.2' => 'id-sha384',
'2.16.840.1.101.3.4.2.3' => 'id-sha512',
'1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
'1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
'1.2.643.2.2.20' => 'id-GostR3410-2001',
'1.2.643.2.2.19' => 'id-GostR3410-94',

'2.16.840.1.113730' => 'netscape',
'2.16.840.1.113730.1' => 'netscape-cert-extension',
'2.16.840.1.113730.1.1' => 'netscape-cert-type',
'2.16.840.1.113730.1.13' => 'netscape-comment',
'2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',

'1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
'1.2.840.113533.7.65.0' => 'entrustVersInfo',
'2.16.840.1.113733.1.6.9' => 'verisignPrivate',


'1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', 
'1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', 
'1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' 
);
}











function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($cert) && isset($cert['tbsCertificate'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
$this->dn = $cert['tbsCertificate']['subject'];
if (!isset($this->dn)) {
return false;
}
$this->currentCert = $cert;

$currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
$this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null;

unset($this->signatureSubject);

return $cert;
}

$asn1 = new ASN1();

if ($mode != self::FORMAT_DER) {
$newcert = $this->_extractBER($cert);
if ($mode == self::FORMAT_PEM && $cert == $newcert) {
return false;
}
$cert = $newcert;
}

if ($cert === false) {
$this->currentCert = false;
return false;
}

$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($cert);

if (!empty($decoded)) {
$x509 = $asn1->asn1map($decoded[0], $this->Certificate);
}
if (!isset($x509) || $x509 === false) {
$this->currentCert = false;
return false;
}

$this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) {
$this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);
}
$this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1);

$key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
$key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);

$this->currentCert = $x509;
$this->dn = $x509['tbsCertificate']['subject'];

$currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
$this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null;

return $x509;
}









function saveX509($cert, $format = self::FORMAT_PEM)
{
if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
return false;
}

switch (true) {

case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
= base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));






$cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null;

$cert['signatureAlgorithm']['parameters'] = null;
$cert['tbsCertificate']['signature']['parameters'] = null;
}
}

$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);

$filters = array();
$type_utf8_string = array('type' => ASN1::TYPE_UTF8_STRING);
$filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string;
$filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string;
$filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string;
$filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string;
$filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string;
$filters['signatureAlgorithm']['parameters'] = $type_utf8_string;
$filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string;

$filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
$filters['directoryName']['rdnSequence']['value'] = $type_utf8_string;





$filters['policyQualifiers']['qualifier']
= array('type' => ASN1::TYPE_IA5_STRING);

$asn1->loadFilters($filters);

$this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1);
$this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1);

$cert = $asn1->encodeDER($cert, $this->Certificate);

switch ($format) {
case self::FORMAT_DER:
return $cert;

default:
return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----';
}
}










function _mapInExtensions(&$root, $path, $asn1)
{
$extensions = &$this->_subArrayUnchecked($root, $path);

if ($extensions) {
for ($i = 0; $i < count($extensions); $i++) {
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
$value = base64_decode($value);


$map = $this->_getMapping($id);
if (!is_bool($map)) {
$decoder = $id == 'id-ce-nameConstraints' ?
array($this, '_decodeNameConstraintIP') :
array($this, '_decodeIP');
$decoded = $asn1->decodeBER($value);
$mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
$value = $mapped === false ? $decoded[0] : $mapped;

if ($id == 'id-ce-certificatePolicies') {
for ($j = 0; $j < count($value); $j++) {
if (!isset($value[$j]['policyQualifiers'])) {
continue;
}
for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
$subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
$map = $this->_getMapping($subid);
$subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
$decoded = $asn1->decodeBER($subvalue);
$mapped = $asn1->asn1map($decoded[0], $map);
$subvalue = $mapped === false ? $decoded[0] : $mapped;
}
}
}
}
} else {
$value = base64_encode($value);
}
}
}
}










function _mapOutExtensions(&$root, $path, $asn1)
{
$extensions = &$this->_subArray($root, $path);

if (is_array($extensions)) {
$size = count($extensions);
for ($i = 0; $i < $size; $i++) {
if ($extensions[$i] instanceof Element) {
continue;
}

$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];

switch ($id) {
case 'id-ce-certificatePolicies':
for ($j = 0; $j < count($value); $j++) {
if (!isset($value[$j]['policyQualifiers'])) {
continue;
}
for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
$subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
$map = $this->_getMapping($subid);
$subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {


$subvalue = new Element($asn1->encodeDER($subvalue, $map));
}
}
}
break;
case 'id-ce-authorityKeyIdentifier': 
if (isset($value['authorityCertSerialNumber'])) {
if ($value['authorityCertSerialNumber']->toBytes() == '') {
$temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0";
$value['authorityCertSerialNumber'] = new Element($temp);
}
}
}



$map = $this->_getMapping($id);
if (is_bool($map)) {
if (!$map) {
user_error($id . ' is not a currently supported extension');
unset($extensions[$i]);
}
} else {
$temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP')));
$value = base64_encode($temp);
}
}
}
}










function _mapInAttributes(&$root, $path, $asn1)
{
$attributes = &$this->_subArray($root, $path);

if (is_array($attributes)) {
for ($i = 0; $i < count($attributes); $i++) {
$id = $attributes[$i]['type'];


$map = $this->_getMapping($id);
if (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
$value = $asn1->encodeDER($values[$j], $this->AttributeValue);
$decoded = $asn1->decodeBER($value);
if (!is_bool($map)) {
$mapped = $asn1->asn1map($decoded[0], $map);
if ($mapped !== false) {
$values[$j] = $mapped;
}
if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) {
$this->_mapInExtensions($values, $j, $asn1);
}
} elseif ($map) {
$values[$j] = base64_encode($value);
}
}
}
}
}
}










function _mapOutAttributes(&$root, $path, $asn1)
{
$attributes = &$this->_subArray($root, $path);

if (is_array($attributes)) {
$size = count($attributes);
for ($i = 0; $i < $size; $i++) {


$id = $attributes[$i]['type'];
$map = $this->_getMapping($id);
if ($map === false) {
user_error($id . ' is not a currently supported attribute', E_USER_NOTICE);
unset($attributes[$i]);
} elseif (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
switch ($id) {
case 'pkcs-9-at-extensionRequest':
$this->_mapOutExtensions($values, $j, $asn1);
break;
}

if (!is_bool($map)) {
$temp = $asn1->encodeDER($values[$j], $map);
$decoded = $asn1->decodeBER($temp);
$values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue);
}
}
}
}
}
}










function _mapInDNs(&$root, $path, $asn1)
{
$dns = &$this->_subArray($root, $path);

if (is_array($dns)) {
for ($i = 0; $i < count($dns); $i++) {
for ($j = 0; $j < count($dns[$i]); $j++) {
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof Element) {
$map = $this->_getMapping($type);
if (!is_bool($map)) {
$decoded = $asn1->decodeBER($value);
$value = $asn1->asn1map($decoded[0], $map);
}
}
}
}
}
}










function _mapOutDNs(&$root, $path, $asn1)
{
$dns = &$this->_subArray($root, $path);

if (is_array($dns)) {
$size = count($dns);
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < count($dns[$i]); $j++) {
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof Element) {
continue;
}

$map = $this->_getMapping($type);
if (!is_bool($map)) {
$value = new Element($asn1->encodeDER($value, $map));
}
}
}
}
}








function _getMapping($extnId)
{
if (!is_string($extnId)) { 
return true;
}

switch ($extnId) {
case 'id-ce-keyUsage':
return $this->KeyUsage;
case 'id-ce-basicConstraints':
return $this->BasicConstraints;
case 'id-ce-subjectKeyIdentifier':
return $this->KeyIdentifier;
case 'id-ce-cRLDistributionPoints':
return $this->CRLDistributionPoints;
case 'id-ce-authorityKeyIdentifier':
return $this->AuthorityKeyIdentifier;
case 'id-ce-certificatePolicies':
return $this->CertificatePolicies;
case 'id-ce-extKeyUsage':
return $this->ExtKeyUsageSyntax;
case 'id-pe-authorityInfoAccess':
return $this->AuthorityInfoAccessSyntax;
case 'id-pe-subjectInfoAccess':
return $this->SubjectInfoAccessSyntax;
case 'id-ce-subjectAltName':
return $this->SubjectAltName;
case 'id-ce-subjectDirectoryAttributes':
return $this->SubjectDirectoryAttributes;
case 'id-ce-privateKeyUsagePeriod':
return $this->PrivateKeyUsagePeriod;
case 'id-ce-issuerAltName':
return $this->IssuerAltName;
case 'id-ce-policyMappings':
return $this->PolicyMappings;
case 'id-ce-nameConstraints':
return $this->NameConstraints;

case 'netscape-cert-type':
return $this->netscape_cert_type;
case 'netscape-comment':
return $this->netscape_comment;
case 'netscape-ca-policy-url':
return $this->netscape_ca_policy_url;





case 'id-qt-unotice':
return $this->UserNotice;


case 'id-pe-logotype': 
case 'entrustVersInfo':

case '1.3.6.1.4.1.311.20.2': 
case '1.3.6.1.4.1.311.21.1': 


case '2.23.42.7.0': 


case '1.3.6.1.4.1.11129.2.4.2':


case '1.3.6.1.5.5.7.1.3':
return true;


case 'pkcs-9-at-unstructuredName':
return $this->PKCS9String;
case 'pkcs-9-at-challengePassword':
return $this->DirectoryString;
case 'pkcs-9-at-extensionRequest':
return $this->Extensions;


case 'id-ce-cRLNumber':
return $this->CRLNumber;
case 'id-ce-deltaCRLIndicator':
return $this->CRLNumber;
case 'id-ce-issuingDistributionPoint':
return $this->IssuingDistributionPoint;
case 'id-ce-freshestCRL':
return $this->CRLDistributionPoints;
case 'id-ce-cRLReasons':
return $this->CRLReason;
case 'id-ce-invalidityDate':
return $this->InvalidityDate;
case 'id-ce-certificateIssuer':
return $this->CertificateIssuer;
case 'id-ce-holdInstructionCode':
return $this->HoldInstructionCode;
case 'id-at-postalAddress':
return $this->PostalAddress;
}

return false;
}








function loadCA($cert)
{
$olddn = $this->dn;
$oldcert = $this->currentCert;
$oldsigsubj = $this->signatureSubject;
$oldkeyid = $this->currentKeyIdentifier;

$cert = $this->loadX509($cert);
if (!$cert) {
$this->dn = $olddn;
$this->currentCert = $oldcert;
$this->signatureSubject = $oldsigsubj;
$this->currentKeyIdentifier = $oldkeyid;

return false;
}

























$this->CAs[] = $cert;

$this->dn = $olddn;
$this->currentCert = $oldcert;
$this->signatureSubject = $oldsigsubj;

return true;
}


















function validateURL($url)
{
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
return false;
}

$components = parse_url($url);
if (!isset($components['host'])) {
return false;
}

if ($names = $this->getExtension('id-ce-subjectAltName')) {
foreach ($names as $name) {
foreach ($name as $key => $value) {
$value = preg_quote($value);
$value = str_replace('\*', '[^.]*', $value);
switch ($key) {
case 'dNSName':







if (preg_match('#^' . $value . '$#', $components['host'])) {
return true;
}
break;
case 'iPAddress':





if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
return true;
}
}
}
}
return false;
}

if ($value = $this->getDNProp('id-at-commonName')) {
$value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
return preg_match('#^' . $value . '$#', $components['host']);
}

return false;
}









function validateDate($date = null)
{
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
return false;
}

if (!isset($date)) {
$date = new DateTime(null, new DateTimeZone(@date_default_timezone_get()));
}

$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
$notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime'];

$notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];

if (is_string($date)) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}

$notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get()));
$notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get()));

switch (true) {
case $date < $notBefore:
case $date > $notAfter:
return false;
}

return true;
}








static function _fetchURL($url)
{
if (self::$disable_url_fetch) {
return false;
}

$parts = parse_url($url);
$data = '';
switch ($parts['scheme']) {
case 'http':
$fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80);
if (!$fsock) {
return false;
}
$path = $parts['path'];
if (isset($parts['query'])) {
$path.= '?' . $parts['query'];
}
fputs($fsock, "GET $path HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
$line = fgets($fsock, 1024);
if (strlen($line) < 3) {
return false;
}
preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
if ($temp[1] != '200') {
return false;
}


while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") {
}

while (!feof($fsock)) {
$temp = fread($fsock, 1024);
if ($temp === false) {
return false;
}
$data.= $temp;
}

break;



}

return $data;
}











function _testForIntermediate($caonly, $count)
{
$opts = $this->getExtension('id-pe-authorityInfoAccess');
if (!is_array($opts)) {
return false;
}
foreach ($opts as $opt) {
if ($opt['accessMethod'] == 'id-ad-caIssuers') {



if (isset($opt['accessLocation']['uniformResourceIdentifier'])) {
$url = $opt['accessLocation']['uniformResourceIdentifier'];
break;
}
}
}

if (!isset($url)) {
return false;
}

$cert = static::_fetchURL($url);
if (!is_string($cert)) {
return false;
}

$parent = new static();
$parent->CAs = $this->CAs;









if (!is_array($parent->loadX509($cert))) {
return false;
}

if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
return false;
}

$this->CAs[] = $parent->currentCert;


return true;
}
















function validateSignature($caonly = true)
{
return $this->_validateSignatureCountable($caonly, 0);
}











function _validateSignatureCountable($caonly, $count)
{
if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
return null;
}

if ($count == self::$recur_limit) {
return false;
}







switch (true) {
case isset($this->currentCert['tbsCertificate']):

switch (true) {
case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING):
$authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
$signingCert = $this->currentCert; 
}
}

if (!empty($this->CAs)) {
for ($i = 0; $i < count($this->CAs); $i++) {


$ca = $this->CAs[$i];
switch (true) {
case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']):
$authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
break 2; 
}
$signingCert = $ca; 
break 3;
}
}
}
if (count($this->CAs) == $i && $caonly) {
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
}
} elseif (!isset($signingCert) || $caonly) {
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case isset($this->currentCert['certificationRequestInfo']):
return $this->_validateSignature(
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case isset($this->currentCert['publicKeyAndChallenge']):
return $this->_validateSignature(
$this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
$this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
case isset($this->currentCert['tbsCertList']):
if (!empty($this->CAs)) {
for ($i = 0; $i < count($this->CAs); $i++) {
$ca = $this->CAs[$i];
switch (true) {
case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']:
case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']):
$authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case !$subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
break 2; 
}
$signingCert = $ca; 
break 3;
}
}
}
}
if (!isset($signingCert)) {
return false;
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1),
$this->signatureSubject
);
default:
return false;
}
}














function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
{
switch ($publicKeyAlgorithm) {
case 'rsaEncryption':
$rsa = new RSA();
$rsa->loadKey($publicKey);

switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
case 'sha224WithRSAEncryption':
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
if (!@$rsa->verify($signatureSubject, $signature)) {
return false;
}
break;
default:
return null;
}
break;
default:
return null;
}

return true;
}











static function setRecurLimit($count)
{
self::$recur_limit = $count;
}






static function disableURLFetch()
{
self::$disable_url_fetch = true;
}






static function enableURLFetch()
{
self::$disable_url_fetch = false;
}











function _reformatKey($algorithm, $key)
{
switch ($algorithm) {
case 'rsaEncryption':
return
"-----BEGIN RSA PUBLIC KEY-----\r\n" .



chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) .
'-----END RSA PUBLIC KEY-----';
default:
return $key;
}
}










function _decodeIP($ip)
{
return inet_ntop(base64_decode($ip));
}










function _decodeNameConstraintIP($ip)
{
$ip = base64_decode($ip);
$size = strlen($ip) >> 1;
$mask = substr($ip, $size);
$ip = substr($ip, 0, $size);
return array(inet_ntop($ip), inet_ntop($mask));
}










function _encodeIP($ip)
{
return is_string($ip) ?
base64_encode(inet_pton($ip)) :
base64_encode(inet_pton($ip[0]) . inet_pton($ip[1]));
}








function _translateDNProp($propName)
{
switch (strtolower($propName)) {
case 'jurisdictionofincorporationcountryname':
case 'jurisdictioncountryname':
case 'jurisdictionc':
return 'jurisdictionOfIncorporationCountryName';
case 'jurisdictionofincorporationstateorprovincename':
case 'jurisdictionstateorprovincename':
case 'jurisdictionst':
return 'jurisdictionOfIncorporationStateOrProvinceName';
case 'jurisdictionlocalityname':
case 'jurisdictionl':
return 'jurisdictionLocalityName';
case 'id-at-businesscategory':
case 'businesscategory':
return 'id-at-businessCategory';
case 'id-at-countryname':
case 'countryname':
case 'c':
return 'id-at-countryName';
case 'id-at-organizationname':
case 'organizationname':
case 'o':
return 'id-at-organizationName';
case 'id-at-dnqualifier':
case 'dnqualifier':
return 'id-at-dnQualifier';
case 'id-at-commonname':
case 'commonname':
case 'cn':
return 'id-at-commonName';
case 'id-at-stateorprovincename':
case 'stateorprovincename':
case 'state':
case 'province':
case 'provincename':
case 'st':
return 'id-at-stateOrProvinceName';
case 'id-at-localityname':
case 'localityname':
case 'l':
return 'id-at-localityName';
case 'id-emailaddress':
case 'emailaddress':
return 'pkcs-9-at-emailAddress';
case 'id-at-serialnumber':
case 'serialnumber':
return 'id-at-serialNumber';
case 'id-at-postalcode':
case 'postalcode':
return 'id-at-postalCode';
case 'id-at-streetaddress':
case 'streetaddress':
return 'id-at-streetAddress';
case 'id-at-name':
case 'name':
return 'id-at-name';
case 'id-at-givenname':
case 'givenname':
return 'id-at-givenName';
case 'id-at-surname':
case 'surname':
case 'sn':
return 'id-at-surname';
case 'id-at-initials':
case 'initials':
return 'id-at-initials';
case 'id-at-generationqualifier':
case 'generationqualifier':
return 'id-at-generationQualifier';
case 'id-at-organizationalunitname':
case 'organizationalunitname':
case 'ou':
return 'id-at-organizationalUnitName';
case 'id-at-pseudonym':
case 'pseudonym':
return 'id-at-pseudonym';
case 'id-at-title':
case 'title':
return 'id-at-title';
case 'id-at-description':
case 'description':
return 'id-at-description';
case 'id-at-role':
case 'role':
return 'id-at-role';
case 'id-at-uniqueidentifier':
case 'uniqueidentifier':
case 'x500uniqueidentifier':
return 'id-at-uniqueIdentifier';
case 'postaladdress':
case 'id-at-postaladdress':
return 'id-at-postalAddress';
default:
return false;
}
}










function setDNProp($propName, $propValue, $type = 'utf8String')
{
if (empty($this->dn)) {
$this->dn = array('rdnSequence' => array());
}

if (($propName = $this->_translateDNProp($propName)) === false) {
return false;
}

foreach ((array) $propValue as $v) {
if (!is_array($v) && isset($type)) {
$v = array($type => $v);
}
$this->dn['rdnSequence'][] = array(
array(
'type' => $propName,
'value'=> $v
)
);
}

return true;
}







function removeDNProp($propName)
{
if (empty($this->dn)) {
return;
}

if (($propName = $this->_translateDNProp($propName)) === false) {
return;
}

$dn = &$this->dn['rdnSequence'];
$size = count($dn);
for ($i = 0; $i < $size; $i++) {
if ($dn[$i][0]['type'] == $propName) {
unset($dn[$i]);
}
}

$dn = array_values($dn);

if (!isset($dn[0])) {
$dn = array_splice($dn, 0, 0);
}
}










function getDNProp($propName, $dn = null, $withType = false)
{
if (!isset($dn)) {
$dn = $this->dn;
}

if (empty($dn)) {
return false;
}

if (($propName = $this->_translateDNProp($propName)) === false) {
return false;
}

$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
$dn = $dn['rdnSequence'];
$result = array();
for ($i = 0; $i < count($dn); $i++) {
if ($dn[$i][0]['type'] == $propName) {
$v = $dn[$i][0]['value'];
if (!$withType) {
if (is_array($v)) {
foreach ($v as $type => $s) {
$type = array_search($type, $asn1->ANYmap, true);
if ($type !== false && isset($asn1->stringTypeSize[$type])) {
$s = $asn1->convert($s, $type);
if ($s !== false) {
$v = $s;
break;
}
}
}
if (is_array($v)) {
$v = array_pop($v); 
}
} elseif (is_object($v) && $v instanceof Element) {
$map = $this->_getMapping($propName);
if (!is_bool($map)) {
$decoded = $asn1->decodeBER($v);
$v = $asn1->asn1map($decoded[0], $map);
}
}
}
$result[] = $v;
}
}

return $result;
}










function setDN($dn, $merge = false, $type = 'utf8String')
{
if (!$merge) {
$this->dn = null;
}

if (is_array($dn)) {
if (isset($dn['rdnSequence'])) {
$this->dn = $dn; 
return true;
}


foreach ($dn as $prop => $value) {
if (!$this->setDNProp($prop, $value, $type)) {
return false;
}
}
return true;
}


$results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 1; $i < count($results); $i+=2) {
$prop = trim($results[$i], ', =/');
$value = $results[$i + 1];
if (!$this->setDNProp($prop, $value, $type)) {
return false;
}
}

return true;
}









function getDN($format = self::DN_ARRAY, $dn = null)
{
if (!isset($dn)) {
$dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
}

switch ((int) $format) {
case self::DN_ARRAY:
return $dn;
case self::DN_ASN1:
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
return $asn1->encodeDER($dn, $this->Name);
case self::DN_CANON:



$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$result = '';
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);
foreach ($dn['rdnSequence'] as $rdn) {
foreach ($rdn as $i => $attr) {
$attr = &$rdn[$i];
if (is_array($attr['value'])) {
foreach ($attr['value'] as $type => $v) {
$type = array_search($type, $asn1->ANYmap, true);
if ($type !== false && isset($asn1->stringTypeSize[$type])) {
$v = $asn1->convert($v, $type);
if ($v !== false) {
$v = preg_replace('/\s+/', ' ', $v);
$attr['value'] = strtolower(trim($v));
break;
}
}
}
}
}
$result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
}
return $result;
case self::DN_HASH:
$dn = $this->getDN(self::DN_CANON, $dn);
$hash = new Hash('sha1');
$hash = $hash->hash($dn);
extract(unpack('Vhash', $hash));
return strtolower(bin2hex(pack('N', $hash)));
}


$start = true;
$output = '';

$result = array();
$asn1 = new ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$this->_mapOutDNs($dn, 'rdnSequence', $asn1);

foreach ($dn['rdnSequence'] as $field) {
$prop = $field[0]['type'];
$value = $field[0]['value'];

$delim = ', ';
switch ($prop) {
case 'id-at-countryName':
$desc = 'C';
break;
case 'id-at-stateOrProvinceName':
$desc = 'ST';
break;
case 'id-at-organizationName':
$desc = 'O';
break;
case 'id-at-organizationalUnitName':
$desc = 'OU';
break;
case 'id-at-commonName':
$desc = 'CN';
break;
case 'id-at-localityName':
$desc = 'L';
break;
case 'id-at-surname':
$desc = 'SN';
break;
case 'id-at-uniqueIdentifier':
$delim = '/';
$desc = 'x500UniqueIdentifier';
break;
case 'id-at-postalAddress':
$delim = '/';
$desc = 'postalAddress';
break;
default:
$delim = '/';
$desc = preg_replace('#.+-([^-]+)$#', '$1', $prop);
}

if (!$start) {
$output.= $delim;
}
if (is_array($value)) {
foreach ($value as $type => $v) {
$type = array_search($type, $asn1->ANYmap, true);
if ($type !== false && isset($asn1->stringTypeSize[$type])) {
$v = $asn1->convert($v, $type);
if ($v !== false) {
$value = $v;
break;
}
}
}
if (is_array($value)) {
$value = array_pop($value); 
}
} elseif (is_object($value) && $value instanceof Element) {
$callback = function ($x) {
return "\x" . bin2hex($x[0]);
};
$value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element));
}
$output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ?
array_merge((array) $result[$desc], array($value)) :
$value;
$start = false;
}

return $format == self::DN_OPENSSL ? $result : $output;
}








function getIssuerDN($format = self::DN_ARRAY)
{
switch (true) {
case !isset($this->currentCert) || !is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']);
case isset($this->currentCert['tbsCertList']):
return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']);
}

return false;
}









function getSubjectDN($format = self::DN_ARRAY)
{
switch (true) {
case !empty($this->dn):
return $this->getDN($format);
case !isset($this->currentCert) || !is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']);
case isset($this->currentCert['certificationRequestInfo']):
return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']);
}

return false;
}









function getIssuerDNProp($propName, $withType = false)
{
switch (true) {
case !isset($this->currentCert) || !is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType);
case isset($this->currentCert['tbsCertList']):
return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType);
}

return false;
}









function getSubjectDNProp($propName, $withType = false)
{
switch (true) {
case !empty($this->dn):
return $this->getDNProp($propName, null, $withType);
case !isset($this->currentCert) || !is_array($this->currentCert):
break;
case isset($this->currentCert['tbsCertificate']):
return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType);
case isset($this->currentCert['certificationRequestInfo']):
return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType);
}

return false;
}







function getChain()
{
$chain = array($this->currentCert);

if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
return false;
}
if (empty($this->CAs)) {
return $chain;
}
while (true) {
$currentCert = $chain[count($chain) - 1];
for ($i = 0; $i < count($this->CAs); $i++) {
$ca = $this->CAs[$i];
if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
$authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert);
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if ($currentCert === $ca) {
break 3;
}
$chain[] = $ca;
break 2;
}
}
}
if ($i == count($this->CAs)) {
break;
}
}
foreach ($chain as $key => $value) {
$chain[$key] = new X509();
$chain[$key]->loadX509($value);
}
return $chain;
}










function setPublicKey($key)
{
$key->setPublicKey();
$this->publicKey = $key;
}









function setPrivateKey($key)
{
$this->privateKey = $key;
}









function setChallenge($challenge)
{
$this->challenge = $challenge;
}









function getPublicKey()
{
if (isset($this->publicKey)) {
return $this->publicKey;
}

if (isset($this->currentCert) && is_array($this->currentCert)) {
foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) {
$keyinfo = $this->_subArray($this->currentCert, $path);
if (!empty($keyinfo)) {
break;
}
}
}
if (empty($keyinfo)) {
return false;
}

$key = $keyinfo['subjectPublicKey'];

switch ($keyinfo['algorithm']['algorithm']) {
case 'rsaEncryption':
$publicKey = new RSA();
$publicKey->loadKey($key);
$publicKey->setPublicKey();
break;
default:
return false;
}

return $publicKey;
}









function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($csr) && isset($csr['certificationRequestInfo'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
unset($this->signatureSubject);
$this->dn = $csr['certificationRequestInfo']['subject'];
if (!isset($this->dn)) {
return false;
}

$this->currentCert = $csr;
return $csr;
}



$asn1 = new ASN1();

if ($mode != self::FORMAT_DER) {
$newcsr = $this->_extractBER($csr);
if ($mode == self::FORMAT_PEM && $csr == $newcsr) {
return false;
}
$csr = $newcsr;
}
$orig = $csr;

if ($csr === false) {
$this->currentCert = false;
return false;
}

$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($csr);

if (empty($decoded)) {
$this->currentCert = false;
return false;
}

$csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
if (!isset($csr) || $csr === false) {
$this->currentCert = false;
return false;
}

$this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
$this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1);

$this->dn = $csr['certificationRequestInfo']['subject'];

$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

$algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
$key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
$key = $this->_reformatKey($algorithm, $key);

switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->loadKey($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}

$this->currentKeyIdentifier = null;
$this->currentCert = $csr;

return $csr;
}









function saveCSR($csr, $format = self::FORMAT_PEM)
{
if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
return false;
}

switch (true) {
case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
= base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null;
$csr['signatureAlgorithm']['parameters'] = null;
$csr['certificationRequestInfo']['signature']['parameters'] = null;
}
}

$asn1 = new ASN1();

$asn1->loadOIDs($this->oids);

$filters = array();
$filters['certificationRequestInfo']['subject']['rdnSequence']['value']
= array('type' => ASN1::TYPE_UTF8_STRING);

$asn1->loadFilters($filters);

$this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1);
$this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
$csr = $asn1->encodeDER($csr, $this->CertificationRequest);

switch ($format) {
case self::FORMAT_DER:
return $csr;

default:
return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
}
}












function loadSPKAC($spkac)
{
if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) {
unset($this->currentCert);
unset($this->currentKeyIdentifier);
unset($this->signatureSubject);
$this->currentCert = $spkac;
return $spkac;
}



$asn1 = new ASN1();


$temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
if ($temp != false) {
$spkac = $temp;
}
$orig = $spkac;

if ($spkac === false) {
$this->currentCert = false;
return false;
}

$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($spkac);

if (empty($decoded)) {
$this->currentCert = false;
return false;
}

$spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge);

if (!isset($spkac) || $spkac === false) {
$this->currentCert = false;
return false;
}

$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

$algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
$key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
$key = $this->_reformatKey($algorithm, $key);

switch ($algorithm) {
case 'rsaEncryption':
$this->publicKey = new RSA();
$this->publicKey->loadKey($key);
$this->publicKey->setPublicKey();
break;
default:
$this->publicKey = null;
}

$this->currentKeyIdentifier = null;
$this->currentCert = $spkac;

return $spkac;
}









function saveSPKAC($spkac, $format = self::FORMAT_PEM)
{
if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) {
return false;
}

$algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm');
switch (true) {
case !$algorithm:
case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
break;
default:
switch ($algorithm) {
case 'rsaEncryption':
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
= base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
}
}

$asn1 = new ASN1();

$asn1->loadOIDs($this->oids);
$spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge);

switch ($format) {
case self::FORMAT_DER:
return $spkac;

default:


return 'SPKAC=' . base64_encode($spkac);
}
}









function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($crl) && isset($crl['tbsCertList'])) {
$this->currentCert = $crl;
unset($this->signatureSubject);
return $crl;
}

$asn1 = new ASN1();

if ($mode != self::FORMAT_DER) {
$newcrl = $this->_extractBER($crl);
if ($mode == self::FORMAT_PEM && $crl == $newcrl) {
return false;
}
$crl = $newcrl;
}
$orig = $crl;

if ($crl === false) {
$this->currentCert = false;
return false;
}

$asn1->loadOIDs($this->oids);
$decoded = $asn1->decodeBER($crl);

if (empty($decoded)) {
$this->currentCert = false;
return false;
}

$crl = $asn1->asn1map($decoded[0], $this->CertificateList);
if (!isset($crl) || $crl === false) {
$this->currentCert = false;
return false;
}

$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

$this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) {
$this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
}
if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) {
$rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates');
if ($rclist_ref) {
$rclist = $crl['tbsCertList']['revokedCertificates'];
foreach ($rclist as $i => $extension) {
if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) {
$this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1);
}
}
}
}

$this->currentKeyIdentifier = null;
$this->currentCert = $crl;

return $crl;
}









function saveCRL($crl, $format = self::FORMAT_PEM)
{
if (!is_array($crl) || !isset($crl['tbsCertList'])) {
return false;
}

$asn1 = new ASN1();

$asn1->loadOIDs($this->oids);

$filters = array();
$filters['tbsCertList']['issuer']['rdnSequence']['value']
= array('type' => ASN1::TYPE_UTF8_STRING);
$filters['tbsCertList']['signature']['parameters']
= array('type' => ASN1::TYPE_UTF8_STRING);
$filters['signatureAlgorithm']['parameters']
= array('type' => ASN1::TYPE_UTF8_STRING);

if (empty($crl['tbsCertList']['signature']['parameters'])) {
$filters['tbsCertList']['signature']['parameters']
= array('type' => ASN1::TYPE_NULL);
}

if (empty($crl['signatureAlgorithm']['parameters'])) {
$filters['signatureAlgorithm']['parameters']
= array('type' => ASN1::TYPE_NULL);
}

$asn1->loadFilters($filters);

$this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
$this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
$rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates');
if (is_array($rclist)) {
foreach ($rclist as $i => $extension) {
$this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1);
}
}

$crl = $asn1->encodeDER($crl, $this->CertificateList);

switch ($format) {
case self::FORMAT_DER:
return $crl;

default:
return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
}
}













function _timeField($date)
{
if ($date instanceof Element) {
return $date;
}
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
$year = $dateObj->format('Y'); 
if ($year < 2050) {
return array('utcTime' => $date);
} else {
return array('generalTime' => $date);
}
}














function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}

if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) {
return false;
}

$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;

if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert;
$this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm;
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;

if (!empty($this->startDate)) {
$this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate);
}
if (!empty($this->endDate)) {
$this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate);
}
if (!empty($this->serialNumber)) {
$this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
}
if (!empty($subject->dn)) {
$this->currentCert['tbsCertificate']['subject'] = $subject->dn;
}
if (!empty($subject->publicKey)) {
$this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
}
$this->removeExtension('id-ce-authorityKeyIdentifier');
if (isset($subject->domains)) {
$this->removeExtension('id-ce-subjectAltName');
}
} elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) {
return false;
} else {
if (!isset($subject->publicKey)) {
return false;
}

$startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O');

$endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');








$serialNumber = !empty($this->serialNumber) ?
$this->serialNumber :
new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256);

$this->currentCert = array(
'tbsCertificate' =>
array(
'version' => 'v3',
'serialNumber' => $serialNumber, 
'signature' => array('algorithm' => $signatureAlgorithm),
'issuer' => false, 
'validity' => array(
'notBefore' => $this->_timeField($startDate), 
'notAfter' => $this->_timeField($endDate) 
),
'subject' => $subject->dn,
'subjectPublicKeyInfo' => $subjectPublicKey
),
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
'signature' => false 
);


$csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0);

if (!empty($csrexts)) {
$this->currentCert['tbsCertificate']['extensions'] = $csrexts;
}
}

$this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;

if (isset($issuer->currentKeyIdentifier)) {
$this->setExtension('id-ce-authorityKeyIdentifier', array(





'keyIdentifier' => $issuer->currentKeyIdentifier
));





}

if (isset($subject->currentKeyIdentifier)) {
$this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier);
}

$altName = array();

if (isset($subject->domains) && count($subject->domains)) {
$altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains);
}

if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {


$ipAddresses = array();
foreach ($subject->ipAddresses as $ipAddress) {
$encoded = $subject->_ipAddress($ipAddress);
if ($encoded !== false) {
$ipAddresses[] = $encoded;
}
}
if (count($ipAddresses)) {
$altName = array_merge($altName, $ipAddresses);
}
}

if (!empty($altName)) {
$this->setExtension('id-ce-subjectAltName', $altName);
}

if ($this->caFlag) {
$keyUsage = $this->getExtension('id-ce-keyUsage');
if (!$keyUsage) {
$keyUsage = array();
}

$this->setExtension(
'id-ce-keyUsage',
array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign'))))
);

$basicConstraints = $this->getExtension('id-ce-basicConstraints');
if (!$basicConstraints) {
$basicConstraints = array();
}

$this->setExtension(
'id-ce-basicConstraints',
array_unique(array_merge(array('cA' => true), $basicConstraints)),
true
);

if (!isset($subject->currentKeyIdentifier)) {
$this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false);
}
}



$tbsCertificate = $this->currentCert['tbsCertificate'];
$this->loadX509($this->saveX509($this->currentCert));

$result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
$result['tbsCertificate'] = $tbsCertificate;

$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;

return $result;
}







function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey) || empty($this->dn)) {
return false;
}

$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
if (!($publicKey = $this->_formatSubjectPublicKey())) {
return false;
}
$this->publicKey = $origPublicKey;

$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;

if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
if (!empty($this->dn)) {
$this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
}
$this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
} else {
$this->currentCert = array(
'certificationRequestInfo' =>
array(
'version' => 'v1',
'subject' => $this->dn,
'subjectPKInfo' => $publicKey
),
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
'signature' => false 
);
}



$certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
$this->loadCSR($this->saveCSR($this->currentCert));

$result = $this->_sign($this->privateKey, $signatureAlgorithm);
$result['certificationRequestInfo'] = $certificationRequestInfo;

$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;

return $result;
}







function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey)) {
return false;
}

$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
$publicKey = $this->_formatSubjectPublicKey();
if (!$publicKey) {
return false;
}
$this->publicKey = $origPublicKey;

$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;


if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) {
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
$this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey;
if (!empty($this->challenge)) {

$this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge));
}
} else {
$this->currentCert = array(
'publicKeyAndChallenge' =>
array(
'spki' => $publicKey,





'challenge' => !empty($this->challenge) ? $this->challenge : ''
),
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
'signature' => false 
);
}



$publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge'];
$this->loadSPKAC($this->saveSPKAC($this->currentCert));

$result = $this->_sign($this->privateKey, $signatureAlgorithm);
$result['publicKeyAndChallenge'] = $publicKeyAndChallenge;

$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;

return $result;
}












function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption')
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}

$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;

$thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');

if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert;
$this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
} else {
$this->currentCert = array(
'tbsCertList' =>
array(
'version' => 'v2',
'signature' => array('algorithm' => $signatureAlgorithm),
'issuer' => false, 
'thisUpdate' => $this->_timeField($thisUpdate) 
),
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
'signature' => false 
);
}

$tbsCertList = &$this->currentCert['tbsCertList'];
$tbsCertList['issuer'] = $issuer->dn;
$tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate);

if (!empty($this->endDate)) {
$tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); 
} else {
unset($tbsCertList['nextUpdate']);
}

if (!empty($this->serialNumber)) {
$crlNumber = $this->serialNumber;
} else {
$crlNumber = $this->getExtension('id-ce-cRLNumber');





$crlNumber = $crlNumber !== false ? $crlNumber->add(new BigInteger(1)) : null;
}

$this->removeExtension('id-ce-authorityKeyIdentifier');
$this->removeExtension('id-ce-issuerAltName');


$version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0;
if (!$version) {
if (!empty($tbsCertList['crlExtensions'])) {
$version = 1; 
} elseif (!empty($tbsCertList['revokedCertificates'])) {
foreach ($tbsCertList['revokedCertificates'] as $cert) {
if (!empty($cert['crlEntryExtensions'])) {
$version = 1; 
}
}
}

if ($version) {
$tbsCertList['version'] = $version;
}
}


if (!empty($tbsCertList['version'])) { 
if (!empty($crlNumber)) {
$this->setExtension('id-ce-cRLNumber', $crlNumber);
}

if (isset($issuer->currentKeyIdentifier)) {
$this->setExtension('id-ce-authorityKeyIdentifier', array(





'keyIdentifier' => $issuer->currentKeyIdentifier
));





}

$issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert);

if ($issuerAltName !== false) {
$this->setExtension('id-ce-issuerAltName', $issuerAltName);
}
}

if (empty($tbsCertList['revokedCertificates'])) {
unset($tbsCertList['revokedCertificates']);
}

unset($tbsCertList);



$tbsCertList = $this->currentCert['tbsCertList'];
$this->loadCRL($this->saveCRL($this->currentCert));

$result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
$result['tbsCertList'] = $tbsCertList;

$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;

return $result;
}









function _sign($key, $signatureAlgorithm)
{
if ($key instanceof RSA) {
switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
case 'sha224WithRSAEncryption':
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
$key->setSignatureMode(RSA::SIGNATURE_PKCS1);

$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
return $this->currentCert;
}
}

return false;
}







function setStartDate($date)
{
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}

$this->startDate = $date->format('D, d M Y H:i:s O');
}







function setEndDate($date)
{







if (strtolower($date) == 'lifetime') {
$temp = '99991231235959Z';
$asn1 = new ASN1();
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}

$this->endDate = $date->format('D, d M Y H:i:s O');
}
}








function setSerialNumber($serial, $base = -256)
{
$this->serialNumber = new BigInteger($serial, $base);
}






function makeCA()
{
$this->caFlag = true;
}













function _isSubArrayValid($root, $path)
{
if (!is_array($root)) {
return false;
}

foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return false;
}

if (!isset($root[$i])) {
return true;
}

$root = $root[$i];
}

return true;
}

















function &_subArrayUnchecked(&$root, $path, $create = false)
{
$false = false;

foreach (explode('/', $path) as $i) {
if (!isset($root[$i])) {
if (!$create) {
return $false;
}

$root[$i] = array();
}

$root = &$root[$i];
}

return $root;
}










function &_subArray(&$root, $path, $create = false)
{
$false = false;

if (!is_array($root)) {
return $false;
}

foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return $false;
}

if (!isset($root[$i])) {
if (!$create) {
return $false;
}

$root[$i] = array();
}

$root = &$root[$i];
}

return $root;
}










function &_extensions(&$root, $path = null, $create = false)
{
if (!isset($root)) {
$root = $this->currentCert;
}

switch (true) {
case !empty($path):
case !is_array($root):
break;
case isset($root['tbsCertificate']):
$path = 'tbsCertificate/extensions';
break;
case isset($root['tbsCertList']):
$path = 'tbsCertList/crlExtensions';
break;
case isset($root['certificationRequestInfo']):
$pth = 'certificationRequestInfo/attributes';
$attributes = &$this->_subArray($root, $pth, $create);

if (is_array($attributes)) {
foreach ($attributes as $key => $value) {
if ($value['type'] == 'pkcs-9-at-extensionRequest') {
$path = "$pth/$key/value/0";
break 2;
}
}
if ($create) {
$key = count($attributes);
$attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array());
$path = "$pth/$key/value/0";
}
}
break;
}

$extensions = &$this->_subArray($root, $path, $create);

if (!is_array($extensions)) {
$false = false;
return $false;
}

return $extensions;
}









function _removeExtension($id, $path = null)
{
$extensions = &$this->_extensions($this->currentCert, $path);

if (!is_array($extensions)) {
return false;
}

$result = false;
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
unset($extensions[$key]);
$result = true;
}
}

$extensions = array_values($extensions);

if (!isset($extensions[0])) {
$extensions = array_splice($extensions, 0, 0);
}
return $result;
}












function _getExtension($id, $cert = null, $path = null)
{
$extensions = $this->_extensions($cert, $path);

if (!is_array($extensions)) {
return false;
}

foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
return $value['extnValue'];
}
}

return false;
}









function _getExtensions($cert = null, $path = null)
{
$exts = $this->_extensions($cert, $path);
$extensions = array();

if (is_array($exts)) {
foreach ($exts as $extension) {
$extensions[] = $extension['extnId'];
}
}

return $extensions;
}












function _setExtension($id, $value, $critical = false, $replace = true, $path = null)
{
$extensions = &$this->_extensions($this->currentCert, $path, true);

if (!is_array($extensions)) {
return false;
}

$newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value);

foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
if (!$replace) {
return false;
}

$extensions[$key] = $newext;
return true;
}
}

$extensions[] = $newext;
return true;
}








function removeExtension($id)
{
return $this->_removeExtension($id);
}











function getExtension($id, $cert = null)
{
return $this->_getExtension($id, $cert);
}








function getExtensions($cert = null)
{
return $this->_getExtensions($cert);
}











function setExtension($id, $value, $critical = false, $replace = true)
{
return $this->_setExtension($id, $value, $critical, $replace);
}









function removeAttribute($id, $disposition = self::ATTR_ALL)
{
$attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes');

if (!is_array($attributes)) {
return false;
}

$result = false;
foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
case $disposition == self::ATTR_REPLACE:
return false;
case $disposition >= $n:
$disposition -= $n;
break;
case $disposition == self::ATTR_ALL:
case $n == 1:
unset($attributes[$key]);
$result = true;
break;
default:
unset($attributes[$key]['value'][$disposition]);
$attributes[$key]['value'] = array_values($attributes[$key]['value']);
$result = true;
break;
}
if ($result && $disposition != self::ATTR_ALL) {
break;
}
}
}

$attributes = array_values($attributes);
return $result;
}












function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}

$attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');

if (!is_array($attributes)) {
return false;
}

foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
case $disposition == self::ATTR_REPLACE:
return false;
case $disposition == self::ATTR_ALL:
return $attribute['value'];
case $disposition >= $n:
$disposition -= $n;
break;
default:
return $attribute['value'][$disposition];
}
}
}

return false;
}








function getAttributes($csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}

$attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');
$attrs = array();

if (is_array($attributes)) {
foreach ($attributes as $attribute) {
$attrs[] = $attribute['type'];
}
}

return $attrs;
}










function setAttribute($id, $value, $disposition = self::ATTR_ALL)
{
$attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true);

if (!is_array($attributes)) {
return false;
}

switch ($disposition) {
case self::ATTR_REPLACE:
$disposition = self::ATTR_APPEND;
case self::ATTR_ALL:
$this->removeAttribute($id);
break;
}

foreach ($attributes as $key => $attribute) {
if ($attribute['type'] == $id) {
$n = count($attribute['value']);
switch (true) {
case $disposition == self::ATTR_APPEND:
$last = $key;
break;
case $disposition >= $n:
$disposition -= $n;
break;
default:
$attributes[$key]['value'][$disposition] = $value;
return true;
}
}
}

switch (true) {
case $disposition >= 0:
return false;
case isset($last):
$attributes[$last]['value'][] = $value;
break;
default:
$attributes[] = array('type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value: array($value));
break;
}

return true;
}









function setKeyIdentifier($value)
{
if (empty($value)) {
unset($this->currentKeyIdentifier);
} else {
$this->currentKeyIdentifier = base64_encode($value);
}
}



















function computeKeyIdentifier($key = null, $method = 1)
{
if (is_null($key)) {
$key = $this;
}

switch (true) {
case is_string($key):
break;
case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
case !is_object($key):
return false;
case $key instanceof Element:

$asn1 = new ASN1();
$decoded = $asn1->decodeBER($key->element);
if (empty($decoded)) {
return false;
}
$raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING));
if (empty($raw)) {
return false;
}
$raw = base64_decode($raw);

$key = new RSA();
if (!$key->loadKey($raw)) {
return false; 
}
if ($key->getPrivateKey() !== false) { 
return $this->computeKeyIdentifier($key, $method);
}
$key = $raw; 
break;
case $key instanceof X509:
if (isset($key->publicKey)) {
return $this->computeKeyIdentifier($key->publicKey, $method);
}
if (isset($key->privateKey)) {
return $this->computeKeyIdentifier($key->privateKey, $method);
}
if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
return $this->computeKeyIdentifier($key->currentCert, $method);
}
return false;
default: 
$key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
break;
}


$key = $this->_extractBER($key);


$hash = new Hash('sha1');
$hash = $hash->hash($key);

if ($method == 2) {
$hash = substr($hash, -8);
$hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
}

return $hash;
}







function _formatSubjectPublicKey()
{
if ($this->publicKey instanceof RSA) {



return array(
'algorithm' => array('algorithm' => 'rsaEncryption'),
'subjectPublicKey' => $this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1)
);
}

return false;
}







function setDomain()
{
$this->domains = func_get_args();
$this->removeDNProp('id-at-commonName');
$this->setDNProp('id-at-commonName', $this->domains[0]);
}






function setIPAddress()
{
$this->ipAddresses = func_get_args();






}








function _dnsName($domain)
{
return array('dNSName' => $domain);
}










function _iPAddress($address)
{
return array('iPAddress' => $address);
}










function _revokedCertificate(&$rclist, $serial, $create = false)
{
$serial = new BigInteger($serial);

foreach ($rclist as $i => $rc) {
if (!($serial->compare($rc['userCertificate']))) {
return $i;
}
}

if (!$create) {
return false;
}

$i = count($rclist);
$revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$rclist[] = array('userCertificate' => $serial,
'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O')));
return $i;
}









function revoke($serial, $date = null)
{
if (isset($this->currentCert['tbsCertList'])) {
if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
if ($this->_revokedCertificate($rclist, $serial) === false) { 
if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
if (!empty($date)) {
$rclist[$i]['revocationDate'] = $this->_timeField($date);
}

return true;
}
}
}
}

return false;
}








function unrevoke($serial)
{
if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
unset($rclist[$i]);
$rclist = array_values($rclist);
return true;
}
}

return false;
}








function getRevoked($serial)
{
if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
return $rclist[$i];
}
}

return false;
}








function listRevoked($crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}

if (!isset($crl['tbsCertList'])) {
return false;
}

$result = array();

if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
foreach ($rclist as $rc) {
$result[] = $rc['userCertificate']->toString();
}
}

return $result;
}









function removeRevokedCertificateExtension($serial, $id)
{
if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}

return false;
}












function getRevokedCertificateExtension($serial, $id, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}

if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}

return false;
}









function getRevokedCertificateExtensions($serial, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}

if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}

return false;
}












function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
{
if (isset($this->currentCert['tbsCertList'])) {
if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
}

return false;
}








function _extractBER($str)
{









if (strlen($str) > ini_get('pcre.backtrack_limit')) {
$temp = $str;
} else {
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
$temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
}

$temp = str_replace(array("\r", "\n", ' '), '', $temp);

$temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
return $temp != false ? $temp : $str;
}



















function getOID($name)
{
static $reverseMap;
if (!isset($reverseMap)) {
$reverseMap = array_flip($this->oids);
}
return isset($reverseMap[$name]) ? $reverseMap[$name] : $name;
}
}
<?php













namespace phpseclib\File\ASN1;










class Element
{






var $element;








function __construct($encoded)
{
$this->element = $encoded;
}
}
<?php































namespace phpseclib\Net;








class SCP
{







const SOURCE_LOCAL_FILE = 1;



const SOURCE_STRING = 2;










const MODE_SSH1 = 1;



const MODE_SSH2 = 2;








var $ssh;







var $packet_size;







var $mode;










function __construct($ssh)
{
if ($ssh instanceof SSH2) {
$this->mode = self::MODE_SSH2;
} elseif ($ssh instanceof SSH1) {
$this->packet_size = 50000;
$this->mode = self::MODE_SSH1;
} else {
return;
}

$this->ssh = $ssh;
}






















function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null)
{
if (!isset($this->ssh)) {
return false;
}

if (empty($remote_file)) {
user_error('remote_file cannot be blank', E_USER_NOTICE);
return false;
}

if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { 
return false;
}

$temp = $this->_receive();
if ($temp !== chr(0)) {
return false;
}

if ($this->mode == self::MODE_SSH2) {
$this->packet_size = $this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4;
}

$remote_file = basename($remote_file);

if ($mode == self::SOURCE_STRING) {
$size = strlen($data);
} else {
if (!is_file($data)) {
user_error("$data is not a valid file", E_USER_NOTICE);
return false;
}

$fp = @fopen($data, 'rb');
if (!$fp) {
return false;
}
$size = filesize($data);
}

$this->_send('C0644 ' . $size . ' ' . $remote_file . "\n");

$temp = $this->_receive();
if ($temp !== chr(0)) {
return false;
}

$sent = 0;
while ($sent < $size) {
$temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size);
$this->_send($temp);
$sent+= strlen($temp);

if (is_callable($callback)) {
call_user_func($callback, $sent);
}
}
$this->_close();

if ($mode != self::SOURCE_STRING) {
fclose($fp);
}

return true;
}













function get($remote_file, $local_file = false)
{
if (!isset($this->ssh)) {
return false;
}

if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { 
return false;
}

$this->_send("\0");

if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+) (?<name>.+)#', rtrim($this->_receive()), $info)) {
return false;
}

$this->_send("\0");

$size = 0;

if ($local_file !== false) {
$fp = @fopen($local_file, 'wb');
if (!$fp) {
return false;
}
}

$content = '';
while ($size < $info['size']) {
$data = $this->_receive();


if ($data === false) {
user_error('No data received from server', E_USER_NOTICE);
return false;
}


$size+= strlen($data);

if ($local_file === false) {
$content.= $data;
} else {
fputs($fp, $data);
}
}

$this->_close();

if ($local_file !== false) {
fclose($fp);
return true;
}

return $content;
}







function _send($data)
{
switch ($this->mode) {
case self::MODE_SSH2:
$this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC, $data);
break;
case self::MODE_SSH1:
$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data);
$this->ssh->_send_binary_packet($data);
}
}







function _receive()
{
switch ($this->mode) {
case self::MODE_SSH2:
return $this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true);
case self::MODE_SSH1:
if (!$this->ssh->bitmap) {
return false;
}
while (true) {
$response = $this->ssh->_get_binary_packet();
switch ($response[SSH1::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_STDOUT_DATA:
if (strlen($response[SSH1::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA]));
return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length);
case NET_SSH1_SMSG_STDERR_DATA:
break;
case NET_SSH1_SMSG_EXITSTATUS:
$this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION));
fclose($this->ssh->fsock);
$this->ssh->bitmap = 0;
return false;
default:
user_error('Unknown packet received', E_USER_NOTICE);
return false;
}
}
}
}






function _close()
{
switch ($this->mode) {
case self::MODE_SSH2:
$this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true);
break;
case self::MODE_SSH1:
$this->ssh->disconnect();
}
}
}
<?php
















namespace phpseclib\Net\SFTP;

use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP;








class Stream
{







static $instances;







var $sftp;







var $path;







var $mode;







var $pos;







var $size;







var $entries;







var $eof;









var $context;







var $notification;








static function register($protocol = 'sftp')
{
if (in_array($protocol, stream_get_wrappers(), true)) {
return false;
}
return stream_wrapper_register($protocol, get_called_class());
}






function __construct()
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo "__construct()\r\n";
}
}













function _parse_path($path)
{
$orig = $path;
extract(parse_url($path) + array('port' => 22));
if (isset($query)) {
$path.= '?' . $query;
} elseif (preg_match('/(\?|\?#)$/', $orig)) {
$path.= '?';
}
if (isset($fragment)) {
$path.= '#' . $fragment;
} elseif ($orig[strlen($orig) - 1] == '#') {
$path.= '#';
}

if (!isset($host)) {
return false;
}

if (isset($this->context)) {
$context = stream_context_get_params($this->context);
if (isset($context['notification'])) {
$this->notification = $context['notification'];
}
}

if ($host[0] == '$') {
$host = substr($host, 1);
global ${$host};
if (($$host instanceof SFTP) === false) {
return false;
}
$this->sftp = $$host;
} else {
if (isset($this->context)) {
$context = stream_context_get_options($this->context);
}
if (isset($context[$scheme]['session'])) {
$sftp = $context[$scheme]['session'];
}
if (isset($context[$scheme]['sftp'])) {
$sftp = $context[$scheme]['sftp'];
}
if (isset($sftp) && $sftp instanceof SFTP) {
$this->sftp = $sftp;
return $path;
}
if (isset($context[$scheme]['username'])) {
$user = $context[$scheme]['username'];
}
if (isset($context[$scheme]['password'])) {
$pass = $context[$scheme]['password'];
}
if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof RSA) {
$pass = $context[$scheme]['privkey'];
}

if (!isset($user) || !isset($pass)) {
return false;
}


if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
$this->sftp = self::$instances[$host][$port][$user][(string) $pass];
} else {
$this->sftp = new SFTP($host, $port);
$this->sftp->disableStatCache();
if (isset($this->notification) && is_callable($this->notification)) {









call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
if (!$this->sftp->login($user, $pass)) {
call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
return false;
}
call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
} else {
if (!$this->sftp->login($user, $pass)) {
return false;
}
}
self::$instances[$host][$port][$user][(string) $pass] = $this->sftp;
}
}

return $path;
}











function _stream_open($path, $mode, $options, &$opened_path)
{
$path = $this->_parse_path($path);

if ($path === false) {
return false;
}
$this->path = $path;

$this->size = $this->sftp->size($path);
$this->mode = preg_replace('#[bt]$#', '', $mode);
$this->eof = false;

if ($this->size === false) {
if ($this->mode[0] == 'r') {
return false;
} else {
$this->sftp->touch($path);
$this->size = 0;
}
} else {
switch ($this->mode[0]) {
case 'x':
return false;
case 'w':
$this->sftp->truncate($path, 0);
$this->size = 0;
}
}

$this->pos = $this->mode[0] != 'a' ? 0 : $this->size;

return true;
}








function _stream_read($count)
{
switch ($this->mode) {
case 'w':
case 'a':
case 'x':
case 'c':
return false;
}







$result = $this->sftp->get($this->path, false, $this->pos, $count);
if (isset($this->notification) && is_callable($this->notification)) {
if ($result === false) {
call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
return 0;
}

call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size);
}

if (empty($result)) { 
$this->eof = true;
return false;
}
$this->pos+= strlen($result);

return $result;
}








function _stream_write($data)
{
switch ($this->mode) {
case 'r':
return false;
}

$result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos);
if (isset($this->notification) && is_callable($this->notification)) {
if (!$result) {
call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
return 0;
}

call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data));
}

if ($result === false) {
return false;
}
$this->pos+= strlen($data);
if ($this->pos > $this->size) {
$this->size = $this->pos;
}
$this->eof = false;
return strlen($data);
}







function _stream_tell()
{
return $this->pos;
}














function _stream_eof()
{
return $this->eof;
}









function _stream_seek($offset, $whence)
{
switch ($whence) {
case SEEK_SET:
if ($offset < 0) {
return false;
}
break;
case SEEK_CUR:
$offset+= $this->pos;
break;
case SEEK_END:
$offset+= $this->size;
}

$this->pos = $offset;
$this->eof = false;
return true;
}










function _stream_metadata($path, $option, $var)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}




switch ($option) {
case 1: 
$time = isset($var[0]) ? $var[0] : null;
$atime = isset($var[1]) ? $var[1] : null;
return $this->sftp->touch($path, $time, $atime);
case 2: 
case 3: 
return false;
case 4: 
return $this->sftp->chown($path, $var);
case 5: 
return $this->sftp->chgrp($path, $var);
case 6: 
return $this->sftp->chmod($path, $var) !== false;
}
}








function _stream_cast($cast_as)
{
return $this->sftp->fsock;
}








function _stream_lock($operation)
{
return false;
}













function _rename($path_from, $path_to)
{
$path1 = parse_url($path_from);
$path2 = parse_url($path_to);
unset($path1['path'], $path2['path']);
if ($path1 != $path2) {
return false;
}

$path_from = $this->_parse_path($path_from);
$path_to = parse_url($path_to);
if ($path_from === false) {
return false;
}

$path_to = $path_to['path']; 


if (!$this->sftp->rename($path_from, $path_to)) {
if ($this->sftp->stat($path_to)) {
return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to);
}
return false;
}

return true;
}

























function _dir_opendir($path, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}
$this->pos = 0;
$this->entries = $this->sftp->nlist($path);
return $this->entries !== false;
}







function _dir_readdir()
{
if (isset($this->entries[$this->pos])) {
return $this->entries[$this->pos++];
}
return false;
}







function _dir_rewinddir()
{
$this->pos = 0;
return true;
}







function _dir_closedir()
{
return true;
}












function _mkdir($path, $mode, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}

return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE);
}














function _rmdir($path, $options)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}

return $this->sftp->rmdir($path);
}









function _stream_flush()
{
return true;
}







function _stream_stat()
{
$results = $this->sftp->stat($this->path);
if ($results === false) {
return false;
}
return $results;
}








function _unlink($path)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}

return $this->sftp->delete($path, false);
}













function _url_stat($path, $flags)
{
$path = $this->_parse_path($path);
if ($path === false) {
return false;
}

$results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path);
if ($results === false) {
return false;
}

return $results;
}








function _stream_truncate($new_size)
{
if (!$this->sftp->truncate($this->path, $new_size)) {
return false;
}

$this->eof = false;
$this->size = $new_size;

return true;
}













function _stream_set_option($option, $arg1, $arg2)
{
return false;
}






function _stream_close()
{
}
















function __call($name, $arguments)
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo $name . '(';
$last = count($arguments) - 1;
foreach ($arguments as $i => $argument) {
var_export($argument);
if ($i != $last) {
echo ',';
}
}
echo ")\r\n";
}
$name = '_' . $name;
if (!method_exists($this, $name)) {
return false;
}
return call_user_func_array(array($this, $name), $arguments);
}
}
<?php
















































namespace phpseclib\Net;

use phpseclib\Crypt\Base;
use phpseclib\Crypt\Blowfish;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\RC4;
use phpseclib\Crypt\Rijndael;
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\TripleDES;
use phpseclib\Crypt\Twofish;
use phpseclib\Math\BigInteger; 
use phpseclib\System\SSH\Agent;








class SSH2
{








const NET_SSH2_COMPRESSION_NONE = 1;



const NET_SSH2_COMPRESSION_ZLIB = 2;



const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3;








const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN_REQ = 0x00000004;
const MASK_LOGIN = 0x00000008;
const MASK_SHELL = 0x00000010;
const MASK_WINDOW_ADJUST = 0x00000020;


















const CHANNEL_EXEC = 1; 
const CHANNEL_SHELL = 2;
const CHANNEL_SUBSYSTEM = 3;
const CHANNEL_AGENT_FORWARD = 4;
const CHANNEL_KEEP_ALIVE = 5;









const LOG_SIMPLE = 1;



const LOG_COMPLEX = 2;



const LOG_REALTIME = 3;



const LOG_REALTIME_FILE = 4;



const LOG_MAX_SIZE = 1048576; 









const READ_SIMPLE = 1;



const READ_REGEX = 2;






const READ_NEXT = 3;








var $identifier;







var $fsock;










var $bitmap = 0;









var $errors = array();








var $server_identifier = false;








var $kex_algorithms = false;








var $kex_algorithm = false;








var $kex_dh_group_size_min = 1536;








var $kex_dh_group_size_preferred = 2048;








var $kex_dh_group_size_max = 4096;








var $server_host_key_algorithms = false;











var $supported_private_key_algorithms = false;








var $encryption_algorithms_client_to_server = false;








var $encryption_algorithms_server_to_client = false;








var $mac_algorithms_client_to_server = false;








var $mac_algorithms_server_to_client = false;








var $compression_algorithms_client_to_server = false;








var $compression_algorithms_server_to_client = false;








var $languages_server_to_client = false;








var $languages_client_to_server = false;








var $preferred = array();
















var $encrypt_block_size = 8;









var $decrypt_block_size = 8;








var $decrypt = false;







var $decryptName;








var $encrypt = false;







var $encryptName;








var $hmac_create = false;






private $hmac_create_name;








var $hmac_check = false;






var $hmac_check_name;












var $hmac_size = false;








var $server_public_host_key;














var $session_id = false;










var $exchange_hash = false;








var $message_numbers = array();








var $disconnect_reasons = array();








var $channel_open_failure_reasons = array();









var $terminal_modes = array();









var $channel_extended_data_type_codes = array();










var $send_seq_no = 0;










var $get_seq_no = 0;











var $server_channels = array();












var $channel_buffers = array();










var $channel_status = array();










var $packet_size_client_to_server = array();








var $message_number_log = array();








var $message_log = array();











var $window_size = 0x7FFFFFFF;













var $window_resize = 0x40000000;










var $window_size_server_to_client = array();










var $window_size_client_to_server = array();










var $signature = '';










var $signature_format = '';








var $interactiveBuffer = '';











var $log_size;







var $timeout;







var $curTimeout;







var $keepAlive;








var $realtime_log_file;








var $realtime_log_size;








var $signature_validated = false;







var $realtime_log_wrap;







var $quiet_mode = false;







var $last_packet;







var $exit_status;








var $request_pty = false;







var $in_request_pty_exec = false;







var $in_subsystem;







var $stdErrorLog;








var $last_interactive_response = '';








var $keyboard_requests_responses = array();












var $banner_message = '';








var $is_timeout = false;








var $log_boundary = ':';








var $log_long_width = 65;








var $log_short_width = 16;









var $host;









var $port;










var $windowColumns = 80;










var $windowRows = 24;









var $crypto_engine = false;







var $agent;







var $send_id_string_first = true;







var $send_kex_first = true;







var $bad_key_size_fix = false;







var $retry_connect = false;







var $binary_packet_buffer = false;







var $preferred_signature_format = false;







var $auth = array();








var $auth_methods_to_continue = null;







var $compress = self::NET_SSH2_COMPRESSION_NONE;







var $decompress = self::NET_SSH2_COMPRESSION_NONE;







var $compress_context;







var $decompress_context;







var $regenerate_compression_context = false;







var $regenerate_decompression_context = false;







var $smartMFA = true;







var $extra_packets;















function __construct($host, $port = 22, $timeout = 10)
{
$this->message_numbers = array(
1 => 'NET_SSH2_MSG_DISCONNECT',
2 => 'NET_SSH2_MSG_IGNORE',
3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
4 => 'NET_SSH2_MSG_DEBUG',
5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
7 => 'NET_SSH2_MSG_EXT_INFO', 
20 => 'NET_SSH2_MSG_KEXINIT',
21 => 'NET_SSH2_MSG_NEWKEYS',
30 => 'NET_SSH2_MSG_KEXDH_INIT',
31 => 'NET_SSH2_MSG_KEXDH_REPLY',
50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
53 => 'NET_SSH2_MSG_USERAUTH_BANNER',

80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
94 => 'NET_SSH2_MSG_CHANNEL_DATA',
95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
96 => 'NET_SSH2_MSG_CHANNEL_EOF',
97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
);
$this->disconnect_reasons = array(
1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
4 => 'NET_SSH2_DISCONNECT_RESERVED',
5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
);
$this->channel_open_failure_reasons = array(
1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
);
$this->terminal_modes = array(
0 => 'NET_SSH2_TTY_OP_END'
);
$this->channel_extended_data_type_codes = array(
1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
);

$this->_define_array(
$this->message_numbers,
$this->disconnect_reasons,
$this->channel_open_failure_reasons,
$this->terminal_modes,
$this->channel_extended_data_type_codes,
array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),

array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),

array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
);

$this->timeout = $timeout;

if (is_resource($host)) {
$this->fsock = $host;
return;
}

if (is_string($host)) {
$this->host = $host;
$this->port = $port;
}
}










function setCryptoEngine($engine)
{
$this->crypto_engine = $engine;
}










function sendIdentificationStringFirst()
{
$this->send_id_string_first = true;
}










function sendIdentificationStringLast()
{
$this->send_id_string_first = false;
}










function sendKEXINITFirst()
{
$this->send_kex_first = true;
}










function sendKEXINITLast()
{
$this->send_kex_first = false;
}







function _connect()
{
if ($this->bitmap & self::MASK_CONSTRUCTOR) {
return false;
}

$this->bitmap |= self::MASK_CONSTRUCTOR;

$this->curTimeout = $this->timeout;

$this->last_packet = microtime(true);

if (!is_resource($this->fsock)) {
$start = microtime(true);



$this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
if (!$this->fsock) {
$host = $this->host . ':' . $this->port;
user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
return false;
}
$elapsed = microtime(true) - $start;

if ($this->curTimeout) {
$this->curTimeout-= $elapsed;
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
}
}

$this->identifier = $this->_generate_identifier();

if ($this->send_id_string_first) {
fputs($this->fsock, $this->identifier . "\r\n");
}








$data = '';
while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
$line = '';
while (true) {
if ($this->curTimeout) {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
$sec = (int) floor($this->curTimeout);
$usec = (int) (1000000 * ($this->curTimeout - $sec));


if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
$this->is_timeout = true;
return false;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}

$temp = stream_get_line($this->fsock, 255, "\n");
if (strlen($temp) == 255) {
continue;
}

if ($temp === false) {
return false;
}

$line.= "$temp\n";











break;
}

$data.= $line;
}

if (feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

$extra = $matches[1];

if (defined('NET_SSH2_LOGGING')) {
$this->_append_log('<-', $matches[0]);
$this->_append_log('->', $this->identifier . "\r\n");
}

$this->server_identifier = trim($temp, "\r\n");
if (strlen($extra)) {
$this->errors[] = $data;
}

if (version_compare($matches[3], '1.99', '<')) {
user_error("Cannot connect to SSH $matches[3] servers");
return false;
}

if (!$this->send_id_string_first) {
fputs($this->fsock, $this->identifier . "\r\n");
}

if (!$this->send_kex_first) {
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT');
return false;
}

if (!$this->_key_exchange($response)) {
return false;
}
}

if ($this->send_kex_first && !$this->_key_exchange()) {
return false;
}

$this->bitmap|= self::MASK_CONNECTED;

return true;
}









function _generate_identifier()
{
$identifier = 'SSH-2.0-phpseclib_2.0';

$ext = array();
if (function_exists('sodium_crypto_box_publickey_from_secretkey')) {
$ext[] = 'libsodium';
}

if (extension_loaded('openssl')) {
$ext[] = 'openssl';
} elseif (extension_loaded('mcrypt')) {
$ext[] = 'mcrypt';
}

if (extension_loaded('gmp')) {
$ext[] = 'gmp';
} elseif (extension_loaded('bcmath')) {
$ext[] = 'bcmath';
}

if (!empty($ext)) {
$identifier .= ' (' . implode(', ', $ext) . ')';
}

return $identifier;
}







function _key_exchange($kexinit_payload_server = false)
{
$preferred = $this->preferred;
$send_kex = true;

$kex_algorithms = isset($preferred['kex']) ?
$preferred['kex'] :
$this->getSupportedKEXAlgorithms();
$server_host_key_algorithms = isset($preferred['hostkey']) ?
$preferred['hostkey'] :
$this->getSupportedHostKeyAlgorithms();
$s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
$preferred['server_to_client']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
$preferred['client_to_server']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
$preferred['server_to_client']['mac'] :
$this->getSupportedMACAlgorithms();
$c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
$preferred['client_to_server']['mac'] :
$this->getSupportedMACAlgorithms();
$s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
$preferred['server_to_client']['comp'] :
$this->getSupportedCompressionAlgorithms();
$c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
$preferred['client_to_server']['comp'] :
$this->getSupportedCompressionAlgorithms();

$kex_algorithms = array_merge($kex_algorithms, array('ext-info-c', 'kex-strict-c-v00@openssh.com'));


switch (true) {
case $this->server_identifier == 'SSH-2.0-SSHD':
case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
if (!isset($preferred['server_to_client']['mac'])) {
$s2c_mac_algorithms = array_values(array_diff(
$s2c_mac_algorithms,
array('hmac-sha1-96', 'hmac-md5-96')
));
}
if (!isset($preferred['client_to_server']['mac'])) {
$c2s_mac_algorithms = array_values(array_diff(
$c2s_mac_algorithms,
array('hmac-sha1-96', 'hmac-md5-96')
));
}
}

$str_kex_algorithms = implode(',', $kex_algorithms);
$str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
$encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
$encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
$mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
$mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
$compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
$compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);

$client_cookie = Random::string(16);

$kexinit_payload_client = pack(
'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
NET_SSH2_MSG_KEXINIT,
$client_cookie,
strlen($str_kex_algorithms),
$str_kex_algorithms,
strlen($str_server_host_key_algorithms),
$str_server_host_key_algorithms,
strlen($encryption_algorithms_client_to_server),
$encryption_algorithms_client_to_server,
strlen($encryption_algorithms_server_to_client),
$encryption_algorithms_server_to_client,
strlen($mac_algorithms_client_to_server),
$mac_algorithms_client_to_server,
strlen($mac_algorithms_server_to_client),
$mac_algorithms_server_to_client,
strlen($compression_algorithms_client_to_server),
$compression_algorithms_client_to_server,
strlen($compression_algorithms_server_to_client),
$compression_algorithms_server_to_client,
0,
'',
0,
'',
0,
0
);

if ($kexinit_payload_server === false) {
if (!$this->_send_binary_packet($kexinit_payload_client)) {
return false;
}

$this->extra_packets = 0;
$kexinit_payload_server = $this->_get_binary_packet();
if ($kexinit_payload_server === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT');
return false;
}

$send_kex = false;
}

$response = $kexinit_payload_server;
$this->_string_shift($response, 1); 
$server_cookie = $this->_string_shift($response, 16);

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) {
if ($this->session_id === false && $this->extra_packets) {
user_error('Possible Terrapin Attack detected');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
}

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));

$this->supported_private_key_algorithms = $this->server_host_key_algorithms;

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

if (!strlen($response)) {
return false;
}
extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
$first_kex_packet_follows = $first_kex_packet_follows != 0;

if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) {
return false;
}




$decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
$decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
if ($decryptKeyLength === null) {
user_error('No compatible server to client encryption algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
$encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
if ($encryptKeyLength === null) {
user_error('No compatible client to server encryption algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}


$this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
if ($kex_algorithm === false) {
user_error('No compatible key exchange algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
if ($server_host_key_algorithm === false) {
user_error('No compatible server host key algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
if ($mac_algorithm_in === false) {
user_error('No compatible server to client message authentication algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$compression_map = array(
'none' => self::NET_SSH2_COMPRESSION_NONE,
'zlib' => self::NET_SSH2_COMPRESSION_ZLIB,
'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
);

$compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
if ($compression_algorithm_out === false) {
user_error('No compatible client to server compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$this->compress = $compression_map[$compression_algorithm_out];

$compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
if ($compression_algorithm_in === false) {
user_error('No compatible server to client compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$this->decompress = $compression_map[$compression_algorithm_in];


$exchange_hash_rfc4419 = '';

if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
$x = Random::string(32);
$eBytes = sodium_crypto_box_publickey_from_secretkey($x);
$clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
$serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY';
$kexHash = new Hash('sha256');
} else {
if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
$dh_group_sizes_packed = pack(
'NNN',
$this->kex_dh_group_size_min,
$this->kex_dh_group_size_preferred,
$this->kex_dh_group_size_max
);
$packet = pack(
'Ca*',
NET_SSH2_MSG_KEXDH_GEX_REQUEST,
$dh_group_sizes_packed
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
return false;
}
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');

if (strlen($response) < 4) {
return false;
}
extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
$primeBytes = $this->_string_shift($response, $primeLength);
$prime = new BigInteger($primeBytes, -256);

if (strlen($response) < 4) {
return false;
}
extract(unpack('NgLength', $this->_string_shift($response, 4)));
$gBytes = $this->_string_shift($response, $gLength);
$g = new BigInteger($gBytes, -256);

$exchange_hash_rfc4419 = pack(
'a*Na*Na*',
$dh_group_sizes_packed,
$primeLength,
$primeBytes,
$gLength,
$gBytes
);

$clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT';
$serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY';
} else {
switch ($kex_algorithm) {


case 'diffie-hellman-group1-sha1':
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
break;

case 'diffie-hellman-group14-sha1':
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
break;
}


$g = new BigInteger(2);
$prime = new BigInteger($prime, 16);
$clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT';
$serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY';
}

switch ($kex_algorithm) {
case 'diffie-hellman-group-exchange-sha256':
$kexHash = new Hash('sha256');
break;
default:
$kexHash = new Hash('sha1');
}








$one = new BigInteger(1);
$keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
$max = $one->bitwise_leftShift(16 * $keyLength); 
$max = $max->subtract($one);

$x = $one->random($one, $max);
$e = $g->modPow($x, $prime);

$eBytes = $e->toBytes(true);
}
$data = pack('CNa*', constant($clientKexInitMessage), strlen($eBytes), $eBytes);

if (!$this->_send_binary_packet($data)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
switch ($clientKexInitMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_INIT':
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
$this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
}

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

if ($type != constant($serverKexReplyMessage)) {
user_error("Expected $serverKexReplyMessage");
return false;
}
switch ($serverKexReplyMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
$this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
$this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
}

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);

if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$fBytes = $this->_string_shift($response, $temp['length']);

if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->signature = $this->_string_shift($response, $temp['length']);

if (strlen($this->signature) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
$this->signature_format = $this->_string_shift($this->signature, $temp['length']);

if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
if (strlen($fBytes) !== 32) {
user_error('Received curve25519 public key of invalid length.');
return false;
}
$key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256);





if (extension_loaded('sodium') || extension_loaded('libsodium')) {
sodium_memzero($x);
}
} else {
$f = new BigInteger($fBytes, -256);
$key = $f->modPow($x, $prime);
}
$keyBytes = $key->toBytes(true);

$this->exchange_hash = pack(
'Na*Na*Na*Na*Na*a*Na*Na*Na*',
strlen($this->identifier),
$this->identifier,
strlen($this->server_identifier),
$this->server_identifier,
strlen($kexinit_payload_client),
$kexinit_payload_client,
strlen($kexinit_payload_server),
$kexinit_payload_server,
strlen($this->server_public_host_key),
$this->server_public_host_key,
$exchange_hash_rfc4419,
strlen($eBytes),
$eBytes,
strlen($fBytes),
$fBytes,
strlen($keyBytes),
$keyBytes
);

$this->exchange_hash = $kexHash->hash($this->exchange_hash);

if ($this->session_id === false) {
$this->session_id = $this->exchange_hash;
}

switch ($server_host_key_algorithm) {
case 'ssh-dss':
$expected_key_format = 'ssh-dss';
break;



default:
$expected_key_format = 'ssh-rsa';
}

if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
switch (true) {
case $this->signature_format == $server_host_key_algorithm:
case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
case $this->signature_format != 'ssh-rsa':
user_error('Server Host Key Algorithm Mismatch');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
}

$packet = pack(
'C',
NET_SSH2_MSG_NEWKEYS
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$response = $this->_get_binary_packet();

if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

if ($type != NET_SSH2_MSG_NEWKEYS) {
user_error('Expected SSH_MSG_NEWKEYS');
return false;
}

if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) {
$this->get_seq_no = $this->send_seq_no = 0;
}

$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);

$this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
if ($this->encrypt) {
if ($this->crypto_engine) {
$this->encrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->encrypt->block_size) {
$this->encrypt_block_size = $this->encrypt->block_size;
}
$this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding();

if ($this->encrypt->getBlockLength()) {
$this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3;
}

$iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
while ($this->encrypt_block_size > strlen($iv)) {
$iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
}
$this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));

$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
while ($encryptKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
}
$this->encrypt->setKey(substr($key, 0, $encryptKeyLength));

$this->encryptName = $encrypt;
}

$this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
if ($this->decrypt) {
if ($this->crypto_engine) {
$this->decrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->decrypt->block_size) {
$this->decrypt_block_size = $this->decrypt->block_size;
}
$this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding();

if ($this->decrypt->getBlockLength()) {
$this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3;
}

$iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
while ($this->decrypt_block_size > strlen($iv)) {
$iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
}
$this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));

$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
while ($decryptKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
}
$this->decrypt->setKey(substr($key, 0, $decryptKeyLength));

$this->decryptName = $decrypt;
}








if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
$this->encrypt->encrypt(str_repeat("\0", 1536));
}
if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
$this->decrypt->decrypt(str_repeat("\0", 1536));
}

$mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
if ($mac_algorithm_out === false) {
user_error('No compatible client to server message authentication algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$createKeyLength = 0; 
switch ($mac_algorithm_out) {
case 'hmac-sha2-256':
$this->hmac_create = new Hash('sha256');
$createKeyLength = 32;
break;
case 'hmac-sha1':
$this->hmac_create = new Hash('sha1');
$createKeyLength = 20;
break;
case 'hmac-sha1-96':
$this->hmac_create = new Hash('sha1-96');
$createKeyLength = 20;
break;
case 'hmac-md5':
$this->hmac_create = new Hash('md5');
$createKeyLength = 16;
break;
case 'hmac-md5-96':
$this->hmac_create = new Hash('md5-96');
$createKeyLength = 16;
}
$this->hmac_create_name = $mac_algorithm_out;

$checkKeyLength = 0;
$this->hmac_size = 0;
switch ($mac_algorithm_in) {
case 'hmac-sha2-256':
$this->hmac_check = new Hash('sha256');
$checkKeyLength = 32;
$this->hmac_size = 32;
break;
case 'hmac-sha1':
$this->hmac_check = new Hash('sha1');
$checkKeyLength = 20;
$this->hmac_size = 20;
break;
case 'hmac-sha1-96':
$this->hmac_check = new Hash('sha1-96');
$checkKeyLength = 20;
$this->hmac_size = 12;
break;
case 'hmac-md5':
$this->hmac_check = new Hash('md5');
$checkKeyLength = 16;
$this->hmac_size = 16;
break;
case 'hmac-md5-96':
$this->hmac_check = new Hash('md5-96');
$checkKeyLength = 16;
$this->hmac_size = 12;
}
$this->hmac_check_name = $mac_algorithm_in;

$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
while ($createKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
}
$this->hmac_create->setKey(substr($key, 0, $createKeyLength));

$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
while ($checkKeyLength > strlen($key)) {
$key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
}
$this->hmac_check->setKey(substr($key, 0, $checkKeyLength));

$this->regenerate_compression_context = $this->regenerate_decompression_context = true;

return true;
}








function _encryption_algorithm_to_key_size($algorithm)
{
if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
return 16;
}

switch ($algorithm) {
case 'none':
return 0;
case 'aes128-cbc':
case 'aes128-ctr':
case 'arcfour':
case 'arcfour128':
case 'blowfish-cbc':
case 'blowfish-ctr':
case 'twofish128-cbc':
case 'twofish128-ctr':
return 16;
case '3des-cbc':
case '3des-ctr':
case 'aes192-cbc':
case 'aes192-ctr':
case 'twofish192-cbc':
case 'twofish192-ctr':
return 24;
case 'aes256-cbc':
case 'aes256-ctr':
case 'arcfour256':
case 'twofish-cbc':
case 'twofish256-cbc':
case 'twofish256-ctr':
return 32;
}
return null;
}









function _encryption_algorithm_to_crypt_instance($algorithm)
{
switch ($algorithm) {
case '3des-cbc':
return new TripleDES();
case '3des-ctr':
return new TripleDES(Base::MODE_CTR);
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
return new Rijndael();
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
return new Rijndael(Base::MODE_CTR);
case 'blowfish-cbc':
return new Blowfish();
case 'blowfish-ctr':
return new Blowfish(Base::MODE_CTR);
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
return new Twofish();
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
return new Twofish(Base::MODE_CTR);
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
return new RC4();
}
return null;
}










function _bad_algorithm_candidate($algorithm)
{
switch ($algorithm) {
case 'arcfour256':
case 'aes192-ctr':
case 'aes256-ctr':
return true;
}

return false;
}











function login($username)
{
$args = func_get_args();
if (!$this->retry_connect) {
$this->auth[] = $args;
}



if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
if ($this->_login($username)) {
return true;
}
if (count($args) == 1) {
return false;
}
}
return call_user_func_array(array(&$this, '_login'), $args);
}









function _login($username)
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
if (!$this->_connect()) {
return false;
}
}

$args = array_slice(func_get_args(), 1);
if (empty($args)) {
return $this->_login_helper($username);
}

while (count($args)) {
if (!$this->auth_methods_to_continue || !$this->smartMFA) {
$newargs = $args;
$args = array();
} else {
$newargs = array();
foreach ($this->auth_methods_to_continue as $method) {
switch ($method) {
case 'publickey':
foreach ($args as $key => $arg) {
if (is_object($arg)) {
$newargs[] = $arg;
unset($args[$key]);
break;
}
}
break;
case 'keyboard-interactive':
$hasArray = $hasString = false;
foreach ($args as $arg) {
if ($hasArray || is_array($arg)) {
$hasArray = true;
break;
}
if ($hasString || is_string($arg)) {
$hasString = true;
break;
}
}
if ($hasArray && $hasString) {
foreach ($args as $key => $arg) {
if (is_array($arg)) {
$newargs[] = $arg;
break 2;
}
}
}
case 'password':
foreach ($args as $key => $arg) {
$newargs[] = $arg;
unset($args[$key]);
break;
}
}
}
}

if (!count($newargs)) {
return false;
}

foreach ($newargs as $arg) {
if ($this->_login_helper($username, $arg)) {
return true;
}
}
}
return false;
}











function _login_helper($username, $password = null)
{
if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}

if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
$packet = pack(
'CNa*',
NET_SSH2_MSG_SERVICE_REQUEST,
strlen('ssh-userauth'),
'ssh-userauth'
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$response = $this->_get_binary_packet();
if ($response === false) {
if ($this->retry_connect) {
$this->retry_connect = false;
if (!$this->_connect()) {
return false;
}
return $this->_login_helper($username, $password);
}
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

if ($type == NET_SSH2_MSG_EXT_INFO) {
if (strlen($response) < 4) {
return false;
}
$nr_extensions = unpack('Nlength', $this->_string_shift($response, 4));
for ($i = 0; $i < $nr_extensions['length']; $i++) {
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$extension_name = $this->_string_shift($response, $temp['length']);
if ($extension_name == 'server-sig-algs') {
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->supported_private_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
}
}

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
}

if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
user_error('Expected SSH_MSG_SERVICE_ACCEPT');
return false;
}
$this->bitmap |= self::MASK_LOGIN_REQ;
}

if (strlen($this->last_interactive_response)) {
return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
}

if ($password instanceof RSA) {
return $this->_privatekey_login($username, $password);
} elseif ($password instanceof Agent) {
return $this->_ssh_agent_login($username, $password);
}

if (is_array($password)) {
if ($this->_keyboard_interactive_login($username, $password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}

if (!isset($password)) {
$packet = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('none'),
'none'
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

switch ($type) {
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
default:
return false;
}
}

$packet = pack(
'CNa*Na*Na*CNa*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('password'),
'password',
0,
strlen($password),
$password
);


if (!defined('NET_SSH2_LOGGING')) {
$logged = null;
} else {
$logged = pack(
'CNa*Na*Na*CNa*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen('username'),
'username',
strlen('ssh-connection'),
'ssh-connection',
strlen('password'),
'password',
0,
strlen('password'),
'password'
);
}

if (!$this->_send_binary_packet($packet, $logged)) {
return false;
}

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

switch ($type) {
case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: 
$this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE:


if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$auth_methods = explode(',', $this->_string_shift($response, $length));
$this->auth_methods_to_continue = $auth_methods;
if (!strlen($response)) {
return false;
}
extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
$partial_success = $partial_success != 0;

if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
if ($this->_keyboard_interactive_login($username, $password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
return false;
}
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}

return false;
}











function _keyboard_interactive_login($username, $password)
{
$packet = pack(
'CNa*Na*Na*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('keyboard-interactive'),
'keyboard-interactive',
0,
'',
0,
''
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

return $this->_keyboard_interactive_process($password);
}







function _keyboard_interactive_process()
{
$responses = func_get_args();

if (strlen($this->last_interactive_response)) {
$response = $this->last_interactive_response;
} else {
$orig = $response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
}

if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

switch ($type) {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); 
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); 
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); 
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));

for ($i = 0; $i < count($responses); $i++) {
if (is_array($responses[$i])) {
foreach ($responses[$i] as $key => $value) {
$this->keyboard_requests_responses[$key] = $value;
}
unset($responses[$i]);
}
}
$responses = array_values($responses);

if (isset($this->keyboard_requests_responses)) {
for ($i = 0; $i < $num_prompts; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));

$prompt = $this->_string_shift($response, $length);

foreach ($this->keyboard_requests_responses as $key => $value) {
if (substr($prompt, 0, strlen($key)) == $key) {
$responses[] = $value;
break;
}
}
}
}


if (strlen($this->last_interactive_response)) {
$this->last_interactive_response = '';
} else {
$this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
}

if (!count($responses) && $num_prompts) {
$this->last_interactive_response = $orig;
return false;
}






$packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
for ($i = 0; $i < count($responses); $i++) {
$packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
$logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
}

if (!$this->_send_binary_packet($packet, $logged)) {
return false;
}

$this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');








return $this->_keyboard_interactive_process();
case NET_SSH2_MSG_USERAUTH_SUCCESS:
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
return false;
}

return false;
}









function _ssh_agent_login($username, $agent)
{
$this->agent = $agent;
$keys = $agent->requestIdentities();
foreach ($keys as $key) {
if ($this->_privatekey_login($username, $key)) {
return true;
}
}

return false;
}











function _privatekey_login($username, $privatekey)
{

$publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
if ($publickey === false) {
return false;
}

$publickey = array(
'e' => $publickey['e']->toBytes(true),
'n' => $publickey['n']->toBytes(true)
);
$publickey = pack(
'Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($publickey['e']),
$publickey['e'],
strlen($publickey['n']),
$publickey['n']
);

$algos = array('rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa');
if (isset($this->preferred['hostkey'])) {
$algos = array_intersect($algos, $this->preferred['hostkey']);
}
$algo = $this->_array_intersect_first($algos, $this->supported_private_key_algorithms);

switch ($algo) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
$signatureType = 'rsa-sha2-256';
break;

default:
$hash = 'sha1';
$signatureType = 'ssh-rsa';
}

$part1 = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
strlen($username),
$username,
strlen('ssh-connection'),
'ssh-connection',
strlen('publickey'),
'publickey'
);
$part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);

$packet = $part1 . chr(0) . $part2;
if (!$this->_send_binary_packet($packet)) {
return false;
}

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
$auth_methods = explode(',', $this->_string_shift($response, $methodlistlen));
if (in_array('publickey', $auth_methods) && substr($signatureType, 0, 9) == 'rsa-sha2-') {
$this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, array('rsa-sha2-256', 'rsa-sha2-512'));
return $this->_privatekey_login($username, $privatekey);
}
$this->auth_methods_to_continue = $auth_methods;
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
return false;
case NET_SSH2_MSG_USERAUTH_PK_OK:


$this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
break;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
default:
user_error('Unexpected response to publickey authentication pt 1');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}

$packet = $part1 . chr(1) . $part2;
$privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
$privatekey->setHash($hash);
$signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
$signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
$packet.= pack('Na*', strlen($signature), $signature);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}

if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:

extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
$this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}

user_error('Unexpected response to publickey authentication pt 2');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}






function getTimeout()
{
return $this->timeout;
}










function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}









function setKeepAlive($interval)
{
$this->keepAlive = $interval;
}






function getStdError()
{
return $this->stdErrorLog;
}












function exec($command, $callback = null)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
$this->stdErrorLog = '';

if (!$this->isAuthenticated()) {
return false;
}

if ($this->in_request_pty_exec) {
user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
return false;
}





$this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;


$packet_size = 0x4000;

$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_EXEC,
$this->window_size_server_to_client[self::CHANNEL_EXEC],
$packet_size
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;

$response = $this->_get_channel_packet(self::CHANNEL_EXEC);
if ($response === false) {
return false;
}

if ($this->request_pty === true) {
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack(
'CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
strlen('pty-req'),
'pty-req',
1,
strlen('vt100'),
'vt100',
$this->windowColumns,
$this->windowRows,
0,
0,
strlen($terminal_modes),
$terminal_modes
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) {
user_error('Unable to request pseudo-terminal');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}

$this->in_request_pty_exec = true;
}










$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
strlen('exec'),
'exec',
1,
strlen($command),
$command
);
if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;

$response = $this->_get_channel_packet(self::CHANNEL_EXEC);
if ($response === false) {
return false;
}

$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;

if ($callback === false || $this->in_request_pty_exec) {
return true;
}

$output = '';
while (true) {
$temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
switch (true) {
case $temp === true:
return is_callable($callback) ? true : $output;
case $temp === false:
return false;
default:
if (is_callable($callback)) {
if (call_user_func($callback, $temp) === true) {
$this->_close_channel(self::CHANNEL_EXEC);
return true;
}
} else {
$output.= $temp;
}
}
}
}









function _initShell()
{
if ($this->in_request_pty_exec === true) {
return true;
}

$this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
$packet_size = 0x4000;

$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_SHELL,
$this->window_size_server_to_client[self::CHANNEL_SHELL],
$packet_size
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;

$response = $this->_get_channel_packet(self::CHANNEL_SHELL);
if ($response === false) {
return false;
}

$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack(
'CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
strlen('pty-req'),
'pty-req',
1,
strlen('vt100'),
'vt100',
$this->windowColumns,
$this->windowRows,
0,
0,
strlen($terminal_modes),
$terminal_modes
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;

if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) {
user_error('Unable to request pseudo-terminal');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}

$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
strlen('shell'),
'shell',
1
);
if (!$this->_send_binary_packet($packet)) {
return false;
}

$response = $this->_get_channel_packet(self::CHANNEL_SHELL);
if ($response === false) {
return false;
}

$this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;

$this->bitmap |= self::MASK_SHELL;

return true;
}









function _get_interactive_channel()
{
switch (true) {
case $this->in_subsystem:
return self::CHANNEL_SUBSYSTEM;
case $this->in_request_pty_exec:
return self::CHANNEL_EXEC;
default:
return self::CHANNEL_SHELL;
}
}







function _get_open_channel()
{
$channel = self::CHANNEL_EXEC;
do {
if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
return $channel;
}
} while ($channel++ < self::CHANNEL_SUBSYSTEM);

return false;
}













function read($expect = '', $mode = self::READ_SIMPLE)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;

if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}

if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session');
return false;
}

$channel = $this->_get_interactive_channel();

if ($mode == self::READ_NEXT) {
return $this->_get_channel_packet($channel);
}

$match = $expect;
while (true) {
if ($mode == self::READ_REGEX) {
preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
$match = isset($matches[0]) ? $matches[0] : '';
}
$pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
}
$response = $this->_get_channel_packet($channel);
if (is_bool($response)) {
$this->in_request_pty_exec = false;
return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
}

$this->interactiveBuffer.= $response;
}
}









function write($cmd)
{
if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}

if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session');
return false;
}

return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
}















function startSubsystem($subsystem)
{
$this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;

$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_SUBSYSTEM,
$this->window_size,
0x4000
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;

$response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
if ($response === false) {
return false;
}

$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SUBSYSTEM],
strlen('subsystem'),
'subsystem',
1,
strlen($subsystem),
$subsystem
);
if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;

$response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);

if ($response === false) {
return false;
}

$this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;

$this->bitmap |= self::MASK_SHELL;
$this->in_subsystem = true;

return true;
}








function stopSubsystem()
{
$this->in_subsystem = false;
$this->_close_channel(self::CHANNEL_SUBSYSTEM);
return true;
}








function reset()
{
$this->_close_channel($this->_get_interactive_channel());
}








function isTimeout()
{
return $this->is_timeout;
}






function disconnect()
{
$this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
fclose($this->realtime_log_file);
}
}









function __destruct()
{
$this->disconnect();
}







function isConnected()
{
return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock);
}







function isAuthenticated()
{
return (bool) ($this->bitmap & self::MASK_LOGIN);
}









function ping()
{
if (!$this->isAuthenticated()) {
if (!empty($this->auth)) {
return $this->_reconnect();
}
return false;
}

$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_KEEP_ALIVE,
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
$packet_size
);

if (!@$this->_send_binary_packet($packet)) {
return $this->_reconnect();
}

$this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;

$response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
if ($response !== false) {
$this->_close_channel(self::CHANNEL_KEEP_ALIVE);
return true;
}

return $this->_reconnect();
}







function _reconnect()
{
$this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
$this->retry_connect = true;
if (!$this->_connect()) {
return false;
}
foreach ($this->auth as $auth) {
$result = call_user_func_array(array(&$this, 'login'), $auth);
}
return $result;
}







function _reset_connection($reason)
{
$this->_disconnect($reason);
$this->decrypt = $this->encrypt = false;
$this->decrypt_block_size = $this->encrypt_block_size = 8;
$this->hmac_check = $this->hmac_create = false;
$this->hmac_size = false;
$this->session_id = false;
$this->retry_connect = true;
$this->get_seq_no = $this->send_seq_no = 0;
}










function _get_binary_packet($skip_channel_filter = false)
{
if ($skip_channel_filter) {
$read = array($this->fsock);
$write = $except = null;

if (!$this->curTimeout) {
if ($this->keepAlive <= 0) {
@stream_select($read, $write, $except, null);
} else {
if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
return $this->_get_binary_packet(true);
}
}
} else {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return true;
}

$read = array($this->fsock);
$write = $except = null;

$start = microtime(true);

if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
return $this->_get_binary_packet(true);
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}

$sec = (int)floor($this->curTimeout);
$usec = (int)(1000000 * ($this->curTimeout - $sec));


if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
$this->is_timeout = true;
return true;
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
}
}

if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
$str = 'Connection closed (by server) prematurely';
if (isset($elapsed)) {
$str.= ' ' . $elapsed . 's';
}
user_error($str);
return false;
}

$start = microtime(true);
$sec = (int) floor($this->curTimeout);
$usec = (int) (1000000 * ($this->curTimeout - $sec));
stream_set_timeout($this->fsock, $sec, $usec);
$raw = stream_get_contents($this->fsock, $this->decrypt_block_size);

if (!strlen($raw)) {
user_error('No data received from server');
return false;
}

if ($this->decrypt !== false) {
$raw = $this->decrypt->decrypt($raw);
}
if ($raw === false) {
user_error('Unable to decrypt content');
return false;
}

if (strlen($raw) < 5) {
return false;
}
extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));

$remaining_length = $packet_length + 4 - $this->decrypt_block_size;




if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decryptName) && !($this->bitmap & SSH2::MASK_LOGIN)) {
$this->bad_key_size_fix = true;
$this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return false;
}
user_error('Invalid size');
return false;
}

$buffer = '';
while ($remaining_length > 0) {
$temp = stream_get_contents($this->fsock, $remaining_length);
if ($temp === false || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Error reading from socket');
return false;
}
$buffer.= $temp;
$remaining_length-= strlen($temp);
}

$stop = microtime(true);
if (strlen($buffer)) {
$raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
}

$payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
$padding = $this->_string_shift($raw, $padding_length); 

if ($this->hmac_check !== false) {
$hmac = stream_get_contents($this->fsock, $this->hmac_size);
if ($hmac === false || strlen($hmac) != $this->hmac_size) {
$this->bitmap = 0;
user_error('Error reading socket');
return false;
} elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
user_error('Invalid HMAC');
return false;
}
}

switch ($this->decompress) {
case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
if (!$this->isAuthenticated()) {
break;
}
case self::NET_SSH2_COMPRESSION_ZLIB:
if ($this->regenerate_decompression_context) {
$this->regenerate_decompression_context = false;

$cmf = ord($payload[0]);
$cm = $cmf & 0x0F;
if ($cm != 8) { 
user_error("Only CM = 8 ('deflate') is supported ($cm)");
}
$cinfo = ($cmf & 0xF0) >> 4;
if ($cinfo > 7) {
user_error("CINFO above 7 is not allowed ($cinfo)");
}
$windowSize = 1 << ($cinfo + 8);

$flg = ord($payload[1]);

if ((($cmf << 8) | $flg) % 31) {
user_error('fcheck failed');
}
$fdict = boolval($flg & 0x20);
$flevel = ($flg & 0xC0) >> 6;

$this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8));
$payload = substr($payload, 2);
}
if ($this->decompress_context) {
$payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
}
}

$this->get_seq_no++;

if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
$message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
$message_number = '<- ' . $message_number .
' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
$this->_append_log($message_number, $payload);
$this->last_packet = $current;
}

return $this->_filter($payload, $skip_channel_filter);
}










function _filter($payload, $skip_channel_filter)
{
switch (ord($payload[0])) {
case NET_SSH2_MSG_DISCONNECT:
$this->_string_shift($payload, 1);
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
$this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
$this->bitmap = 0;
return false;
case NET_SSH2_MSG_IGNORE:
$this->extra_packets++;
$payload = $this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_DEBUG:
$this->extra_packets++;
$this->_string_shift($payload, 2);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
$payload = $this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_UNIMPLEMENTED:
return false;
case NET_SSH2_MSG_KEXINIT:

if ($this->session_id !== false) {
$this->send_kex_first = false;
if (!$this->_key_exchange($payload)) {
$this->bitmap = 0;
return false;
}
$payload = $this->_get_binary_packet($skip_channel_filter);
}
}


if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
$this->_string_shift($payload, 1);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->banner_message = $this->_string_shift($payload, $length);
$payload = $this->_get_binary_packet();
}


if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
if (is_bool($payload)) {
return $payload;
}

switch (ord($payload[0])) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
if (strlen($payload) == 31) {
extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
if (ord(substr($payload, 9 + $length))) { 
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
}
$payload = $this->_get_binary_packet($skip_channel_filter);
}
}
break;
case NET_SSH2_MSG_CHANNEL_DATA:
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
case NET_SSH2_MSG_CHANNEL_CLOSE:
case NET_SSH2_MSG_CHANNEL_EOF:
if (!$skip_channel_filter && !empty($this->server_channels)) {
$this->binary_packet_buffer = $payload;
$this->_get_channel_packet(true);
$payload = $this->_get_binary_packet();
}
break;
case NET_SSH2_MSG_GLOBAL_REQUEST: 
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);

if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}

$payload = $this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_CHANNEL_OPEN: 
$this->_string_shift($payload, 1);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$data = $this->_string_shift($payload, $length);
if (strlen($payload) < 4) {
return false;
}
extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
switch ($data) {
case 'auth-agent':
case 'auth-agent@openssh.com':
if (isset($this->agent)) {
$new_channel = self::CHANNEL_AGENT_FORWARD;

if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));

$this->packet_size_client_to_server[$new_channel] = $remote_window_size;
$this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
$this->window_size_client_to_server[$new_channel] = $this->window_size;

$packet_size = 0x4000;

$packet = pack(
'CN4',
NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
$server_channel,
$new_channel,
$packet_size,
$packet_size
);

$this->server_channels[$new_channel] = $server_channel;
$this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
if (!$this->_send_binary_packet($packet)) {
return false;
}
}
break;
default:
$packet = pack(
'CN3a*Na*',
NET_SSH2_MSG_REQUEST_FAILURE,
$server_channel,
NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
0,
'',
0,
''
);

if (!$this->_send_binary_packet($packet)) {
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
$payload = $this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
$this->_string_shift($payload, 1);
if (strlen($payload) < 8) {
return false;
}
extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
$this->window_size_client_to_server[$channel]+= $window_size;

$payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
}
}

return $payload;
}








function enableQuietMode()
{
$this->quiet_mode = true;
}








function disableQuietMode()
{
$this->quiet_mode = false;
}









function isQuietModeEnabled()
{
return $this->quiet_mode;
}






function enablePTY()
{
$this->request_pty = true;
}






function disablePTY()
{
if ($this->in_request_pty_exec) {
$this->_close_channel(self::CHANNEL_EXEC);
$this->in_request_pty_exec = false;
}
$this->request_pty = false;
}









function isPTYEnabled()
{
return $this->request_pty;
}











function _get_channel_packet($client_channel, $skip_extended = false)
{
if (!empty($this->channel_buffers[$client_channel])) {
switch ($this->channel_status[$client_channel]) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
switch (ord($packet[0])) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
case NET_SSH2_MSG_CHANNEL_FAILURE:
unset($this->channel_buffers[$client_channel][$i]);
return substr($packet, 1);
}
}
break;
default:
return substr(array_shift($this->channel_buffers[$client_channel]), 1);
}
}

while (true) {
if ($this->binary_packet_buffer !== false) {
$response = $this->binary_packet_buffer;
$this->binary_packet_buffer = false;
} else {
$response = $this->_get_binary_packet(true);
if ($response === true && $this->is_timeout) {
if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) {
$this->_close_channel($client_channel);
}
return true;
}
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
}

if ($client_channel == -1 && $response === true) {
return true;
}
if (!strlen($response)) {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));

if (strlen($response) < 4) {
return false;
}
if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
} else {
extract(unpack('Nchannel', $this->_string_shift($response, 4)));
}


if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
$this->window_size_server_to_client[$channel]-= strlen($response);


if ($this->window_size_server_to_client[$channel] < 0) {


$packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->window_size_server_to_client[$channel]+= $this->window_resize;
}

switch ($type) {
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:






if (strlen($response) < 8) {
return false;
}
extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
$data = $this->_string_shift($response, $length);
$this->stdErrorLog.= $data;
if ($skip_extended || $this->quiet_mode) {
continue 2;
}
if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
return $data;
}
$this->channel_buffers[$channel][] = chr($type) . $data;

continue 2;
case NET_SSH2_MSG_CHANNEL_REQUEST:
if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
continue 2;
}
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$value = $this->_string_shift($response, $length);
switch ($value) {
case 'exit-signal':
$this->_string_shift($response, 1);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
$this->_string_shift($response, 1);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
if ($length) {
$this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
}

$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));

$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;

continue 3;
case 'exit-status':
if (strlen($response) < 5) {
return false;
}
extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
$this->exit_status = $exit_status;




continue 3;
default:


continue 3;
}
}

switch ($this->channel_status[$channel]) {
case NET_SSH2_MSG_CHANNEL_OPEN:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
$this->server_channels[$channel] = $server_channel;
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
if ($window_size < 0) {
$window_size&= 0x7FFFFFFF;
$window_size+= 0x80000000;
}
$this->window_size_client_to_server[$channel] = $window_size;
if (strlen($response) < 4) {
return false;
}
$temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
$this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
$result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
$this->_on_channel_open();
return $result;
case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
user_error('Unable to open channel');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
default:
if ($client_channel == $channel) {
user_error('Unexpected response to open request');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
return $this->_get_channel_packet($client_channel, $skip_extended);
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
return true;
case NET_SSH2_MSG_CHANNEL_FAILURE:
return false;
case NET_SSH2_MSG_CHANNEL_DATA:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$data = $this->_string_shift($response, $length);
$this->channel_buffers[$channel][] = chr($type) . $data;
return $this->_get_channel_packet($client_channel, $skip_extended);
default:
user_error('Unable to fulfill channel request');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
case NET_SSH2_MSG_CHANNEL_CLOSE:
return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
}
}



switch ($type) {
case NET_SSH2_MSG_CHANNEL_DATA:









if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$data = $this->_string_shift($response, $length);

if ($channel == self::CHANNEL_AGENT_FORWARD) {
$agent_response = $this->agent->_forward_data($data);
if (!is_bool($agent_response)) {
$this->_send_channel_packet($channel, $agent_response);
}
break;
}

if ($client_channel == $channel) {
return $data;
}
$this->channel_buffers[$channel][] = chr($type) . $data;
break;
case NET_SSH2_MSG_CHANNEL_CLOSE:
$this->curTimeout = 5;

if ($this->bitmap & self::MASK_SHELL) {
$this->bitmap&= ~self::MASK_SHELL;
}
if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
}

$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
if ($client_channel == $channel) {
return true;
}
case NET_SSH2_MSG_CHANNEL_EOF:
break;
default:
user_error("Error reading channel data ($type)");
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
}












function _send_binary_packet($data, $logged = null)
{
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed prematurely');
return false;
}

if (!isset($logged)) {
$logged = $data;
}

switch ($this->compress) {
case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
if (!$this->isAuthenticated()) {
break;
}
case self::NET_SSH2_COMPRESSION_ZLIB:
if (!$this->regenerate_compression_context) {
$header = '';
} else {
$this->regenerate_compression_context = false;
$this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15));
$header = "\x78\x9C";
}
if ($this->compress_context) {
$data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
}
}


$packet_length = strlen($data) + 9;

$packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;

$padding_length = $packet_length - strlen($data) - 5;
$padding = Random::string($padding_length);


$packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);

$hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
$this->send_seq_no++;

if ($this->encrypt !== false) {
$packet = $this->encrypt->encrypt($packet);
}

$packet.= $hmac;

$start = microtime(true);
$result = strlen($packet) == @fputs($this->fsock, $packet);
$stop = microtime(true);

if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
$message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
$message_number = '-> ' . $message_number .
' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
$this->_append_log($message_number, $logged);
$this->last_packet = $current;
}

return $result;
}










function _append_log($message_number, $message)
{

if (strlen($message_number) > 2) {
$this->_string_shift($message);
}

switch (NET_SSH2_LOGGING) {

case self::LOG_SIMPLE:
$this->message_number_log[] = $message_number;
break;

case self::LOG_COMPLEX:
$this->message_number_log[] = $message_number;
$this->log_size+= strlen($message);
$this->message_log[] = $message;
while ($this->log_size > self::LOG_MAX_SIZE) {
$this->log_size-= strlen(array_shift($this->message_log));
array_shift($this->message_number_log);
}
break;



case self::LOG_REALTIME:
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
@flush();
@ob_flush();
break;




case self::LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) {

$filename = self::LOG_REALTIME_FILENAME;
$fp = fopen($filename, 'w');
$this->realtime_log_file = $fp;
}
if (!is_resource($this->realtime_log_file)) {
break;
}
$entry = $this->_format_log(array($message), array($message_number));
if ($this->realtime_log_wrap) {
$temp = "<<< START >>>\r\n";
$entry.= $temp;
fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
}
$this->realtime_log_size+= strlen($entry);
if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
fseek($this->realtime_log_file, 0);
$this->realtime_log_size = strlen($entry);
$this->realtime_log_wrap = true;
}
fputs($this->realtime_log_file, $entry);
}
}











function _send_channel_packet($client_channel, $data)
{
while (strlen($data)) {
if (!$this->window_size_client_to_server[$client_channel]) {
$this->bitmap^= self::MASK_WINDOW_ADJUST;

$this->_get_channel_packet(-1);
$this->bitmap^= self::MASK_WINDOW_ADJUST;
}





$max_size = min(
$this->packet_size_client_to_server[$client_channel],
$this->window_size_client_to_server[$client_channel]
);

$temp = $this->_string_shift($data, $max_size);
$packet = pack(
'CN2a*',
NET_SSH2_MSG_CHANNEL_DATA,
$this->server_channels[$client_channel],
strlen($temp),
$temp
);
$this->window_size_client_to_server[$client_channel]-= strlen($temp);
if (!$this->_send_binary_packet($packet)) {
return false;
}
}

return true;
}













function _close_channel($client_channel, $want_reply = false)
{


$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));

if (!$want_reply) {
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}

$this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;

$this->curTimeout = 5;

while (!is_bool($this->_get_channel_packet($client_channel))) {
}

if ($this->is_timeout) {
$this->disconnect();
}

if ($want_reply) {
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}

if ($this->bitmap & self::MASK_SHELL) {
$this->bitmap&= ~self::MASK_SHELL;
}
}








function _disconnect($reason)
{
if ($this->bitmap & self::MASK_CONNECTED) {
$data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
$this->_send_binary_packet($data);
}

$this->bitmap = 0;
if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
fclose($this->fsock);
}

return false;
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}










function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}









function getLog()
{
if (!defined('NET_SSH2_LOGGING')) {
return false;
}

switch (NET_SSH2_LOGGING) {
case self::LOG_SIMPLE:
return $this->message_number_log;
case self::LOG_COMPLEX:
$log = $this->_format_log($this->message_log, $this->message_number_log);
return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
default:
return false;
}
}









function _format_log($message_log, $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
}
$fragment = $this->_string_shift($current_log, $this->log_short_width);
$hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));



$raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
$output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
$output.= "\r\n";
}

return $output;
}










function _format_log_helper($matches)
{
return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
}










function _on_channel_open()
{
if (isset($this->agent)) {
$this->agent->_on_channel_open($this);
}
}










function _array_intersect_first($array1, $array2)
{
foreach ($array1 as $value) {
if (in_array($value, $array2)) {
return $value;
}
}
return false;
}









function getErrors()
{
return $this->errors;
}









function getLastError()
{
$count = count($this->errors);

if ($count > 0) {
return $this->errors[$count - 1];
}
}







function getServerIdentification()
{
$this->_connect();

return $this->server_identifier;
}







function getKexAlgorithms()
{
$this->_connect();

return $this->kex_algorithms;
}







function getServerHostKeyAlgorithms()
{
$this->_connect();

return $this->server_host_key_algorithms;
}







function getEncryptionAlgorithmsClient2Server()
{
$this->_connect();

return $this->encryption_algorithms_client_to_server;
}







function getEncryptionAlgorithmsServer2Client()
{
$this->_connect();

return $this->encryption_algorithms_server_to_client;
}







function getMACAlgorithmsClient2Server()
{
$this->_connect();

return $this->mac_algorithms_client_to_server;
}







function getMACAlgorithmsServer2Client()
{
$this->_connect();

return $this->mac_algorithms_server_to_client;
}







function getCompressionAlgorithmsClient2Server()
{
$this->_connect();

return $this->compression_algorithms_client_to_server;
}







function getCompressionAlgorithmsServer2Client()
{
$this->_connect();

return $this->compression_algorithms_server_to_client;
}







function getLanguagesServer2Client()
{
$this->_connect();

return $this->languages_server_to_client;
}







function getLanguagesClient2Server()
{
$this->_connect();

return $this->languages_client_to_server;
}







function getServerAlgorithms()
{
$this->_connect();

return array(
'kex' => $this->kex_algorithms,
'hostkey' => $this->server_host_key_algorithms,
'client_to_server' => array(
'crypt' => $this->encryption_algorithms_client_to_server,
'mac' => $this->mac_algorithms_client_to_server,
'comp' => $this->compression_algorithms_client_to_server,
'lang' => $this->languages_client_to_server
),
'server_to_client' => array(
'crypt' => $this->encryption_algorithms_server_to_client,
'mac' => $this->mac_algorithms_server_to_client,
'comp' => $this->compression_algorithms_server_to_client,
'lang' => $this->languages_server_to_client
)
);
}







function getSupportedKEXAlgorithms()
{
$kex_algorithms = array(



'curve25519-sha256@libssh.org',

'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group-exchange-sha1', 



'diffie-hellman-group14-sha1', 
'diffie-hellman-group1-sha1', 
);

if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) {
$kex_algorithms = array_diff(
$kex_algorithms,
array('curve25519-sha256@libssh.org')
);
}

return $kex_algorithms;
}







function getSupportedHostKeyAlgorithms()
{
return array(
'rsa-sha2-256', 
'rsa-sha2-512', 
'ssh-rsa', 
'ssh-dss' 
);
}







function getSupportedEncryptionAlgorithms()
{
$algos = array(

'arcfour256',
'arcfour128',




'aes128-ctr', 
'aes192-ctr', 
'aes256-ctr', 

'twofish128-ctr', 
'twofish192-ctr', 
'twofish256-ctr', 

'aes128-cbc', 
'aes192-cbc', 
'aes256-cbc', 

'twofish128-cbc', 
'twofish192-cbc', 
'twofish256-cbc',
'twofish-cbc', 


'blowfish-ctr', 

'blowfish-cbc', 

'3des-ctr', 

'3des-cbc', 


);

if ($this->crypto_engine) {
$engines = array($this->crypto_engine);
} else {
$engines = array(
Base::ENGINE_OPENSSL,
Base::ENGINE_MCRYPT,
Base::ENGINE_INTERNAL
);
}

$ciphers = array();
foreach ($engines as $engine) {
foreach ($algos as $algo) {
$obj = $this->_encryption_algorithm_to_crypt_instance($algo);
if ($obj instanceof Rijndael) {
$obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
}
switch ($algo) {
case 'arcfour128':
case 'arcfour256':
if ($engine != Base::ENGINE_INTERNAL) {
continue 2;
}
}
if ($obj->isValidEngine($engine)) {
$algos = array_diff($algos, array($algo));
$ciphers[] = $algo;
}
}
}

return $ciphers;
}







function getSupportedMACAlgorithms()
{
return array(

'hmac-sha2-256',

'hmac-sha1-96', 
'hmac-sha1', 
'hmac-md5-96', 
'hmac-md5', 

);
}







function getSupportedCompressionAlgorithms()
{
$algos = array('none'); 
if (function_exists('deflate_init')) {
$algos[] = 'zlib@openssh.com'; 
$algos[] = 'zlib';
}
return $algos;
}









function getAlgorithmsNegotiated()
{
$this->_connect();

$compression_map = array(
self::NET_SSH2_COMPRESSION_NONE => 'none',
self::NET_SSH2_COMPRESSION_ZLIB => 'zlib',
self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com'
);

return array(
'kex' => $this->kex_algorithm,
'hostkey' => $this->signature_format,
'client_to_server' => array(
'crypt' => $this->encryptName,
'mac' => $this->hmac_create_name,
'comp' => $compression_map[$this->compress],
),
'server_to_client' => array(
'crypt' => $this->decryptName,
'mac' => $this->hmac_check_name,
'comp' => $compression_map[$this->decompress],
)
);
}








function setPreferredAlgorithms($methods)
{
$preferred = $methods;

if (isset($preferred['kex'])) {
$preferred['kex'] = array_intersect(
$preferred['kex'],
$this->getSupportedKEXAlgorithms()
);
}

if (isset($preferred['hostkey'])) {
$preferred['hostkey'] = array_intersect(
$preferred['hostkey'],
$this->getSupportedHostKeyAlgorithms()
);
}

$keys = array('client_to_server', 'server_to_client');
foreach ($keys as $key) {
if (isset($preferred[$key])) {
$a = &$preferred[$key];
if (isset($a['crypt'])) {
$a['crypt'] = array_intersect(
$a['crypt'],
$this->getSupportedEncryptionAlgorithms()
);
}
if (isset($a['comp'])) {
$a['comp'] = array_intersect(
$a['comp'],
$this->getSupportedCompressionAlgorithms()
);
}
if (isset($a['mac'])) {
$a['mac'] = array_intersect(
$a['mac'],
$this->getSupportedMACAlgorithms()
);
}
}
}

$keys = array(
'kex',
'hostkey',
'client_to_server/crypt',
'client_to_server/comp',
'client_to_server/mac',
'server_to_client/crypt',
'server_to_client/comp',
'server_to_client/mac',
);
foreach ($keys as $key) {
$p = $preferred;
$m = $methods;

$subkeys = explode('/', $key);
foreach ($subkeys as $subkey) {
if (!isset($p[$subkey])) {
continue 2;
}
$p = $p[$subkey];
$m = $m[$subkey];
}

if (count($p) != count($m)) {
$diff = array_diff($m, $p);
$msg = count($diff) == 1 ?
' is not a supported algorithm' :
' are not supported algorithms';
user_error(implode(', ', $diff) . $msg);
return false;
}
}

$this->preferred = $preferred;
}










function getBannerMessage()
{
return $this->banner_message;
}










function getServerPublicHostKey()
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
if (!$this->_connect()) {
return false;
}
}

$signature = $this->signature;
$server_public_host_key = $this->server_public_host_key;

if (strlen($server_public_host_key) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
$this->_string_shift($server_public_host_key, $length);

if ($this->signature_validated) {
return $this->bitmap ?
$this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
false;
}

$this->signature_validated = true;

switch ($this->signature_format) {
case 'ssh-dss':
$zero = new BigInteger();

if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);




$temp = unpack('Nlength', $this->_string_shift($signature, 4));
if ($temp['length'] != 40) {
user_error('Invalid signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$r = new BigInteger($this->_string_shift($signature, 20), 256);
$s = new BigInteger($this->_string_shift($signature, 20), 256);

switch (true) {
case $r->equals($zero):
case $r->compare($q) >= 0:
case $s->equals($zero):
case $s->compare($q) >= 0:
user_error('Invalid signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$w = $s->modInverse($q);

$u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16));
list(, $u1) = $u1->divide($q);

$u2 = $w->multiply($r);
list(, $u2) = $u2->divide($q);

$g = $g->modPow($u1, $p);
$y = $y->modPow($u2, $p);

$v = $g->multiply($y);
list(, $v) = $v->divide($p);
list(, $v) = $v->divide($q);

if (!$v->equals($r)) {
user_error('Bad server signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}

break;
case 'ssh-rsa':
case 'rsa-sha2-256':
case 'rsa-sha2-512':
if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

if (strlen($server_public_host_key) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
$rawN = $this->_string_shift($server_public_host_key, $temp['length']);
$n = new BigInteger($rawN, -256);
$nLength = strlen(ltrim($rawN, "\0"));






























if (strlen($signature) < 4) {
return false;
}
$temp = unpack('Nlength', $this->_string_shift($signature, 4));
$s = new BigInteger($this->_string_shift($signature, $temp['length']), 256);







if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) {
user_error('Invalid signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}

$s = $s->modPow($e, $n);
$s = $s->toBytes();

switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;

default:
$hash = 'sha1';
}
$hashObj = new Hash($hash);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash));
break;
case 'rsa-sha2-256':
$h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash));
break;

default:
$hash = 'sha1';
$h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
}
$h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;

if ($s != $h) {
user_error('Bad server signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}
break;
default:
user_error('Unsupported signature format');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}

return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
}







function getExitStatus()
{
if (is_null($this->exit_status)) {
return false;
}
return $this->exit_status;
}







function getWindowColumns()
{
return $this->windowColumns;
}







function getWindowRows()
{
return $this->windowRows;
}







function setWindowColumns($value)
{
$this->windowColumns = $value;
}







function setWindowRows($value)
{
$this->windowRows = $value;
}








function setWindowSize($columns = 80, $rows = 24)
{
$this->windowColumns = $columns;
$this->windowRows = $rows;
}








function _updateLogHistory($old, $new)
{
if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
$old,
$new,
$this->message_number_log[count($this->message_number_log) - 1]
);
}
}







function getAuthMethodsToContinue()
{
return $this->auth_methods_to_continue;
}




function enableSmartMFA()
{
$this->smartMFA = true;
}




function disableSmartMFA()
{
$this->smartMFA = false;
}
}
<?php















































namespace phpseclib\Net;

use phpseclib\Crypt\DES;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\TripleDES;
use phpseclib\Math\BigInteger;








class SSH1
{











const CIPHER_NONE = 0;





const CIPHER_IDEA = 1;



const CIPHER_DES = 2;





const CIPHER_3DES = 3;






const CIPHER_BROKEN_TSS = 4;















const CIPHER_RC4 = 5;






const CIPHER_BLOWFISH = 6;











const AUTH_RHOSTS = 1;



const AUTH_RSA = 2;





const AUTH_PASSWORD = 3;



const AUTH_RHOSTS_RSA = 4;








const TTY_OP_END = 0;








const RESPONSE_TYPE = 1;







const RESPONSE_DATA = 2;







const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN = 0x00000004;
const MASK_SHELL = 0x00000008;









const LOG_SIMPLE = 1;



const LOG_COMPLEX = 2;



const LOG_REALTIME = 3;



const LOG_REALTIME_FILE = 4;



const LOG_MAX_SIZE = 1048576; 









const READ_SIMPLE = 1;



const READ_REGEX = 2;








var $identifier = 'SSH-1.5-phpseclib';







var $fsock;







var $crypto = false;










var $bitmap = 0;










var $server_key_public_exponent;










var $server_key_public_modulus;










var $host_key_public_exponent;










var $host_key_public_modulus;










var $supported_ciphers = array(
self::CIPHER_NONE => 'No encryption',
self::CIPHER_IDEA => 'IDEA in CFB mode',
self::CIPHER_DES => 'DES in CBC mode',
self::CIPHER_3DES => 'Triple-DES in CBC mode',
self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
self::CIPHER_RC4 => 'RC4',
self::CIPHER_BLOWFISH => 'Blowfish'
);










var $supported_authentications = array(
self::AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv',
self::AUTH_RSA => 'pure RSA authentication',
self::AUTH_PASSWORD => 'password authentication',
self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
);








var $server_identification = '';








var $protocol_flags = array();








var $protocol_flags_log = array();








var $message_log = array();








var $realtime_log_file;








var $realtime_log_size;








var $realtime_log_wrap;








var $interactiveBuffer = '';











var $log_size;







var $timeout;







var $curTimeout;







var $log_boundary = ':';







var $log_long_width = 65;







var $log_short_width = 16;









var $host;









var $port;














var $connectionTimeout;









var $cipher;













function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES)
{
$this->protocol_flags = array(
1 => 'NET_SSH1_MSG_DISCONNECT',
2 => 'NET_SSH1_SMSG_PUBLIC_KEY',
3 => 'NET_SSH1_CMSG_SESSION_KEY',
4 => 'NET_SSH1_CMSG_USER',
9 => 'NET_SSH1_CMSG_AUTH_PASSWORD',
10 => 'NET_SSH1_CMSG_REQUEST_PTY',
12 => 'NET_SSH1_CMSG_EXEC_SHELL',
13 => 'NET_SSH1_CMSG_EXEC_CMD',
14 => 'NET_SSH1_SMSG_SUCCESS',
15 => 'NET_SSH1_SMSG_FAILURE',
16 => 'NET_SSH1_CMSG_STDIN_DATA',
17 => 'NET_SSH1_SMSG_STDOUT_DATA',
18 => 'NET_SSH1_SMSG_STDERR_DATA',
19 => 'NET_SSH1_CMSG_EOF',
20 => 'NET_SSH1_SMSG_EXITSTATUS',
33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
);

$this->_define_array($this->protocol_flags);

$this->host = $host;
$this->port = $port;
$this->connectionTimeout = $timeout;
$this->cipher = $cipher;
}







function _connect()
{
$this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
if (!$this->fsock) {
user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr"));
return false;
}

$this->server_identification = $init_line = fgets($this->fsock, 255);

if (defined('NET_SSH1_LOGGING')) {
$this->_append_log('<-', $this->server_identification);
$this->_append_log('->', $this->identifier . "\r\n");
}

if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
user_error('Can only connect to SSH servers');
return false;
}
if ($parts[1][0] != 1) {
user_error("Cannot connect to SSH $parts[1] servers");
return false;
}

fputs($this->fsock, $this->identifier."\r\n");

$response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
user_error('Expected SSH_SMSG_PUBLIC_KEY');
return false;
}

$anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8);

$this->_string_shift($response[self::RESPONSE_DATA], 4);

if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
$server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
$this->server_key_public_exponent = $server_key_public_exponent;

if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
$server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);

$this->server_key_public_modulus = $server_key_public_modulus;

$this->_string_shift($response[self::RESPONSE_DATA], 4);

if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
$host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
$this->host_key_public_exponent = $host_key_public_exponent;

if (strlen($response[self::RESPONSE_DATA]) < 2) {
return false;
}
$temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
$host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);

$this->host_key_public_modulus = $host_key_public_modulus;

$this->_string_shift($response[self::RESPONSE_DATA], 4);


if (strlen($response[self::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));

foreach ($this->supported_ciphers as $mask => $name) {
if (($supported_ciphers_mask & (1 << $mask)) == 0) {
unset($this->supported_ciphers[$mask]);
}
}


if (strlen($response[self::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_authentications as $mask => $name) {
if (($supported_authentications_mask & (1 << $mask)) == 0) {
unset($this->supported_authentications[$mask]);
}
}

$session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));

$session_key = Random::string(32);
$double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));

if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$server_key_public_exponent,
$server_key_public_modulus
)
);
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$host_key_public_exponent,
$host_key_public_modulus
)
);
} else {
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$host_key_public_exponent,
$host_key_public_modulus
)
);
$double_encrypted_session_key = $this->_rsa_crypt(
$double_encrypted_session_key,
array(
$server_key_public_exponent,
$server_key_public_modulus
)
);
}

$cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES;
$data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_SESSION_KEY');
return false;
}

switch ($cipher) {



case self::CIPHER_DES:
$this->crypto = new DES();
$this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 8));
break;
case self::CIPHER_3DES:
$this->crypto = new TripleDES(TripleDES::MODE_3CBC);
$this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 24));
break;





}

$response = $this->_get_binary_packet();

if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS');
return false;
}

$this->bitmap = self::MASK_CONNECTED;

return true;
}









function login($username, $password = '')
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
$this->bitmap |= self::MASK_CONSTRUCTOR;
if (!$this->_connect()) {
return false;
}
}

if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}

$data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_USER');
return false;
}

$response = $this->_get_binary_packet();

if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
$this->bitmap |= self::MASK_LOGIN;
return true;
} elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
return false;
}

$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
return false;
}


if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) {
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
$this->message_log[count($this->message_log) - 1] = $data;
}

$response = $this->_get_binary_packet();

if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
$this->bitmap |= self::MASK_LOGIN;
return true;
} elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
return false;
} else {
user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
return false;
}
}









function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}






















function exec($cmd, $block = true)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}

$data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_CMD');
return false;
}

if (!$block) {
return true;
}

$output = '';
$response = $this->_get_binary_packet();

if ($response !== false) {
do {
$output.= substr($response[self::RESPONSE_DATA], 4);
$response = $this->_get_binary_packet();
} while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
}

$data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);


$this->_send_binary_packet($data);

fclose($this->fsock);


$this->bitmap = 0;

return $output;
}









function _initShell()
{



$data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_REQUEST_PTY');
return false;
}

$response = $this->_get_binary_packet();

if ($response === true) {
return false;
}
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS');
return false;
}

$data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_SHELL');
return false;
}

$this->bitmap |= self::MASK_SHELL;



return true;
}









function write($cmd)
{
return $this->interactiveWrite($cmd);
}













function read($expect, $mode = self::READ_SIMPLE)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}

if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session');
return false;
}

$match = $expect;
while (true) {
if ($mode == self::READ_REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches);
$match = isset($matches[0]) ? $matches[0] : '';
}
$pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
}
$response = $this->_get_binary_packet();

if ($response === true) {
return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
}
$this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4);
}
}









function interactiveWrite($cmd)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}

if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session');
return false;
}

$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);

if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_STDIN');
return false;
}

return true;
}














function interactiveRead()
{
if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()');
return false;
}

if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session');
return false;
}

$read = array($this->fsock);
$write = $except = null;
if (stream_select($read, $write, $except, 0)) {
$response = $this->_get_binary_packet();
return substr($response[self::RESPONSE_DATA], 4);
} else {
return '';
}
}






function disconnect()
{
$this->_disconnect();
}









function __destruct()
{
$this->_disconnect();
}







function _disconnect($msg = 'Client Quit')
{
if ($this->bitmap) {
$data = pack('C', NET_SSH1_CMSG_EOF);
$this->_send_binary_packet($data);













$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);

$this->_send_binary_packet($data);
fclose($this->fsock);
$this->bitmap = 0;
}
}













function _get_binary_packet()
{
if (feof($this->fsock)) {

return false;
}

if ($this->curTimeout) {
$read = array($this->fsock);
$write = $except = null;

$start = strtok(microtime(), ' ') + strtok(''); 
$sec = floor($this->curTimeout);
$usec = 1000000 * ($this->curTimeout - $sec);

if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {

return true;
}
$elapsed = strtok(microtime(), ' ') + strtok('') - $start;
$this->curTimeout-= $elapsed;
}

$start = strtok(microtime(), ' ') + strtok(''); 
$data = fread($this->fsock, 4);
if (strlen($data) < 4) {
return false;
}
$temp = unpack('Nlength', $data);

$padding_length = 8 - ($temp['length'] & 7);
$length = $temp['length'] + $padding_length;
$raw = '';

while ($length > 0) {
$temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
return false;
}
$raw.= $temp;
$length-= strlen($temp);
}
$stop = strtok(microtime(), ' ') + strtok('');

if (strlen($raw) && $this->crypto !== false) {
$raw = $this->crypto->decrypt($raw);
}

$padding = substr($raw, 0, $padding_length);
$type = $raw[$padding_length];
$data = substr($raw, $padding_length + 1, -4);

if (strlen($raw) < 4) {
return false;
}
$temp = unpack('Ncrc', substr($raw, -4));






$type = ord($type);

if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
$temp = '<- ' . $temp .
' (' . round($stop - $start, 4) . 's)';
$this->_append_log($temp, $data);
}

return array(
self::RESPONSE_TYPE => $type,
self::RESPONSE_DATA => $data
);
}











function _send_binary_packet($data)
{
if (feof($this->fsock)) {

return false;
}

$length = strlen($data) + 4;

$padding = Random::string(8 - ($length & 7));

$orig = $data;
$data = $padding . $data;
$data.= pack('N', $this->_crc($data));

if ($this->crypto !== false) {
$data = $this->crypto->encrypt($data);
}

$packet = pack('Na*', $length, $data);

$start = strtok(microtime(), ' ') + strtok(''); 
$result = strlen($packet) == fputs($this->fsock, $packet);
$stop = strtok(microtime(), ' ') + strtok('');

if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
$temp = '-> ' . $temp .
' (' . round($stop - $start, 4) . 's)';
$this->_append_log($temp, $orig);
}

return $result;
}














function _crc($data)
{
static $crc_lookup_table = array(
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
);



$crc = 0x00000000;
$length = strlen($data);

for ($i=0; $i<$length; $i++) {




$crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
}



return $crc;
}











function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}














function _rsa_crypt($m, $key)
{




















$modulus = $key[1]->toBytes();
$length = strlen($modulus) - strlen($m) - 3;
$random = '';
while (strlen($random) != $length) {
$block = Random::string($length - strlen($random));
$block = str_replace("\x00", '', $block);
$random.= $block;
}
$temp = chr(0) . chr(2) . $random . chr(0) . $m;

$m = new BigInteger($temp, 256);
$m = $m->modPow($key[0], $key[1]);

return $m->toBytes();
}










function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}









function getLog()
{
if (!defined('NET_SSH1_LOGGING')) {
return false;
}

switch (NET_SSH1_LOGGING) {
case self::LOG_SIMPLE:
return $this->protocol_flags_log;
break;
case self::LOG_COMPLEX:
return $this->_format_log($this->message_log, $this->protocol_flags_log);
break;
default:
return false;
}
}









function _format_log($message_log, $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (strlen($current_log)) {
$output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
}
$fragment = $this->_string_shift($current_log, $this->log_short_width);
$hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));



$raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
$output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
$output.= "\r\n";
}

return $output;
}










function _format_log_helper($matches)
{
return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
}











function getServerKeyPublicExponent($raw_output = false)
{
return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
}











function getServerKeyPublicModulus($raw_output = false)
{
return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
}











function getHostKeyPublicExponent($raw_output = false)
{
return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
}











function getHostKeyPublicModulus($raw_output = false)
{
return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
}












function getSupportedCiphers($raw_output = false)
{
return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
}












function getSupportedAuthentications($raw_output = false)
{
return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
}







function getServerIdentification()
{
return rtrim($this->server_identification);
}










function _append_log($protocol_flags, $message)
{
switch (NET_SSH1_LOGGING) {

case self::LOG_SIMPLE:
$this->protocol_flags_log[] = $protocol_flags;
break;

case self::LOG_COMPLEX:
$this->protocol_flags_log[] = $protocol_flags;
$this->_string_shift($message);
$this->log_size+= strlen($message);
$this->message_log[] = $message;
while ($this->log_size > self::LOG_MAX_SIZE) {
$this->log_size-= strlen(array_shift($this->message_log));
array_shift($this->protocol_flags_log);
}
break;



case self::LOG_REALTIME:
echo "<pre>\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n</pre>\r\n";
@flush();
@ob_flush();
break;




case self::LOG_REALTIME_FILE:
if (!isset($this->realtime_log_file)) {

$filename = self::LOG_REALTIME_FILE;
$fp = fopen($filename, 'w');
$this->realtime_log_file = $fp;
}
if (!is_resource($this->realtime_log_file)) {
break;
}
$entry = $this->_format_log(array($message), array($protocol_flags));
if ($this->realtime_log_wrap) {
$temp = "<<< START >>>\r\n";
$entry.= $temp;
fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
}
$this->realtime_log_size+= strlen($entry);
if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
fseek($this->realtime_log_file, 0);
$this->realtime_log_size = strlen($entry);
$this->realtime_log_wrap = true;
}
fputs($this->realtime_log_file, $entry);
}
}
}
<?php


































namespace phpseclib\Net;








class SFTP extends SSH2
{









const CHANNEL = 0x100;








const SOURCE_LOCAL_FILE = 1;




const SOURCE_STRING = 2;




const SOURCE_CALLBACK = 16;



const RESUME = 4;



const RESUME_START = 8;









var $packet_types = array();








var $status_codes = array();











var $use_request_id = false;











var $packet_type = -1;








var $packet_buffer = '';








var $extensions = array();








var $version;








var $defaultVersion;








var $preferredVersion = 3;









var $pwd = false;








var $packet_type_log = array();








var $packet_log = array();









var $sftp_errors = array();













var $stat_cache = array();









var $max_sftp_packet;









var $use_stat_cache = true;









var $sortOptions = array();













var $canonicalize_paths = true;








var $requestBuffer = array();









var $preserveTime = false;














var $allow_arbitrary_length_packets = false;









var $channel_close = false;







var $partial_init = false;








var $attributes = array();





var $open_flags = array();








var $open_flags5 = array();







var $file_types = array();












function __construct($host, $port = 22, $timeout = 10)
{
parent::__construct($host, $port, $timeout);

$this->max_sftp_packet = 1 << 15;

$this->packet_types = array(
1 => 'NET_SFTP_INIT',
2 => 'NET_SFTP_VERSION',
3 => 'NET_SFTP_OPEN',
4 => 'NET_SFTP_CLOSE',
5 => 'NET_SFTP_READ',
6 => 'NET_SFTP_WRITE',
7 => 'NET_SFTP_LSTAT',
9 => 'NET_SFTP_SETSTAT',
10 => 'NET_SFTP_FSETSTAT',
11 => 'NET_SFTP_OPENDIR',
12 => 'NET_SFTP_READDIR',
13 => 'NET_SFTP_REMOVE',
14 => 'NET_SFTP_MKDIR',
15 => 'NET_SFTP_RMDIR',
16 => 'NET_SFTP_REALPATH',
17 => 'NET_SFTP_STAT',
18 => 'NET_SFTP_RENAME',
19 => 'NET_SFTP_READLINK',
20 => 'NET_SFTP_SYMLINK',
21 => 'NET_SFTP_LINK',

101=> 'NET_SFTP_STATUS',
102=> 'NET_SFTP_HANDLE',
103=> 'NET_SFTP_DATA',
104=> 'NET_SFTP_NAME',
105=> 'NET_SFTP_ATTRS',

200=> 'NET_SFTP_EXTENDED'
);
$this->status_codes = array(
0 => 'NET_SFTP_STATUS_OK',
1 => 'NET_SFTP_STATUS_EOF',
2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
4 => 'NET_SFTP_STATUS_FAILURE',
5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
6 => 'NET_SFTP_STATUS_NO_CONNECTION',
7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
13 => 'NET_SFTP_STATUS_NO_MEDIA',
14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
21 => 'NET_SFTP_STATUS_LINK_LOOP',
22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
27 => 'NET_SFTP_STATUS_DELETE_PENDING',
28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
29 => 'NET_SFTP_STATUS_OWNER_INVALID',
30 => 'NET_SFTP_STATUS_GROUP_INVALID',
31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
);


$this->attributes = array(
0x00000001 => 'NET_SFTP_ATTR_SIZE',
0x00000002 => 'NET_SFTP_ATTR_UIDGID', 
0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', 
0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
0x00000010 => 'NET_SFTP_ATTR_CREATETIME', 
0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
0x00000040 => 'NET_SFTP_ATTR_ACL',
0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
0x00000200 => 'NET_SFTP_ATTR_BITS', 
0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', 
0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
0x00008000 => 'NET_SFTP_ATTR_CTIME',




(PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000) => 'NET_SFTP_ATTR_EXTENDED'
);
$this->open_flags = array(
0x00000001 => 'NET_SFTP_OPEN_READ',
0x00000002 => 'NET_SFTP_OPEN_WRITE',
0x00000004 => 'NET_SFTP_OPEN_APPEND',
0x00000008 => 'NET_SFTP_OPEN_CREATE',
0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
0x00000020 => 'NET_SFTP_OPEN_EXCL',
0x00000040 => 'NET_SFTP_OPEN_TEXT' 
);


$this->open_flags5 = array(

0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',

0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', 
0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
);


$this->file_types = array(
1 => 'NET_SFTP_TYPE_REGULAR',
2 => 'NET_SFTP_TYPE_DIRECTORY',
3 => 'NET_SFTP_TYPE_SYMLINK',
4 => 'NET_SFTP_TYPE_SPECIAL',
5 => 'NET_SFTP_TYPE_UNKNOWN',


6 => 'NET_SFTP_TYPE_SOCKET',
7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
9 => 'NET_SFTP_TYPE_FIFO'
);
$this->_define_array(
$this->packet_types,
$this->status_codes,
$this->attributes,
$this->open_flags,
$this->open_flags5,
$this->file_types
);

if (!defined('NET_SFTP_QUEUE_SIZE')) {
define('NET_SFTP_QUEUE_SIZE', 32);
}
if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024);
}
}







function _precheck()
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}

if ($this->pwd === false) {
return $this->_init_sftp_connection();
}

return true;
}







function _partial_init_sftp_connection()
{
$this->window_size_server_to_client[self::CHANNEL] = $this->window_size;

$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL,
$this->window_size,
0x4000
);

if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;

$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
} elseif ($response === true && $this->isTimeout()) {
return false;
}

$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
strlen('subsystem'),
'subsystem',
1,
strlen('sftp'),
'sftp'
);
if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;

$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {

$command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
"test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" .
"exec sftp-server";


$packet = pack(
'CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
strlen('exec'),
'exec',
1,
strlen($command),
$command
);
if (!$this->_send_binary_packet($packet)) {
return false;
}

$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;

$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
}
} elseif ($response === true && $this->isTimeout()) {
return false;
}

$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;

if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
return false;
}

$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_VERSION) {
user_error('Expected SSH_FXP_VERSION');
return false;
}

$this->use_request_id = true;

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nversion', $this->_string_shift($response, 4)));
$this->defaultVersion = $version;
while (!empty($response)) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$value = $this->_string_shift($response, $length);
$this->extensions[$key] = $value;
}

$this->partial_init = true;

return true;
}







function _init_sftp_connection()
{
if (!$this->partial_init && !$this->_partial_init_sftp_connection()) {
return false;
}
























$this->version = $this->defaultVersion;
if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) {
$versions = explode(',', $this->extensions['versions']);
$supported = array(6, 5, 4);
if ($this->preferredVersion) {
$supported = array_diff($supported, array($this->preferredVersion));
array_unshift($supported, $this->preferredVersion);
}
foreach ($supported as $ver) {
if (in_array($ver, $versions)) {
if ($ver === $this->version) {
break;
}
$this->version = (int) $ver;
$packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver);
if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}

break;
}
}
}















if ($this->version < 2 || $this->version > 6) {
return false;
}

$this->pwd = true;
$this->pwd = $this->_realpath('.');
if ($this->pwd === false) {
if (!$this->canonicalize_paths) {
user_error('Unable to canonicalize current working directory');
return false;
}
$this->canonicalize_paths = false;
$this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
}

$this->_update_stat_cache($this->pwd, array());

return true;
}






function disableStatCache()
{
$this->use_stat_cache = false;
}






function enableStatCache()
{
$this->use_stat_cache = true;
}






function clearStatCache()
{
$this->stat_cache = array();
}






function enablePathCanonicalization()
{
$this->canonicalize_paths = true;
}








function disablePathCanonicalization()
{
$this->canonicalize_paths = false;
}






function enableArbitraryLengthPackets()
{
$this->allow_arbitrary_length_packets = true;
}






function disableArbitraryLengthPackets()
{
$this->allow_arbitrary_length_packets = false;
}







function pwd()
{
if (!$this->_precheck()) {
return false;
}

return $this->pwd;
}








function _logError($response, $status = -1)
{
if ($status == -1) {
if (strlen($response) < 4) {
return;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
}

$error = $this->status_codes[$status];

if ($this->version > 2) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
} else {
$this->sftp_errors[] = $error;
}
}











function realpath($path)
{
if (!$this->_precheck()) {
return false;
}

return $this->_realpath($path);
}















function _realpath($path)
{
if (!$this->canonicalize_paths) {
if ($this->pwd === true) {
return '.';
}
if (!strlen($path) || $path[0] != '/') {
$path = $this->pwd . '/' . $path;
}

$parts = explode('/', $path);
$afterPWD = $beforePWD = array();
foreach ($parts as $part) {
switch ($part) {

case '.':
break;
case '..':
if (!empty($afterPWD)) {
array_pop($afterPWD);
} else {
$beforePWD[] = '..';
}
break;
default:
$afterPWD[] = $part;
}
}

$beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.';
return $beforePWD . '/' . implode('/', $afterPWD);
}

if ($this->pwd === true) {

if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:



$this->_string_shift($response, 4); 
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
return false;
}
}

if (!strlen($path) || $path[0] != '/') {
$path = $this->pwd . '/' . $path;
}

$path = explode('/', $path);
$new = array();
foreach ($path as $dir) {
if (!strlen($dir)) {
continue;
}
switch ($dir) {
case '..':
array_pop($new);
case '.':
break;
default:
$new[] = $dir;
}
}

return '/' . implode('/', $new);
}








function chdir($dir)
{
if (!$this->_precheck()) {
return false;
}


if ($dir === '') {
$dir = './';

} elseif ($dir[strlen($dir) - 1] != '/') {
$dir.= '/';
}

$dir = $this->_realpath($dir);


if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) {
$this->pwd = $dir;
return true;
}






if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
return false;
}


$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}

if (!$this->_close_handle($handle)) {
return false;
}

$this->_update_stat_cache($dir, array());

$this->pwd = $dir;
return true;
}









function nlist($dir = '.', $recursive = false)
{
return $this->_nlist_helper($dir, $recursive, '');
}










function _nlist_helper($dir, $recursive, $relativeDir)
{
$files = $this->_list($dir, false);



if (is_int($files)) {
return false;
}

if (!$recursive || $files === false) {
return $files;
}

$result = array();
foreach ($files as $value) {
if ($value == '.' || $value == '..') {
if ($relativeDir == '') {
$result[] = $value;
}
continue;
}
if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) {
$temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
$temp = is_array($temp) ? $temp : array();
$result = array_merge($result, $temp);
} else {
$result[] = $relativeDir . $value;
}
}

return $result;
}









function rawlist($dir = '.', $recursive = false)
{
$files = $this->_list($dir, true);



if (is_int($files)) {
return false;
}

if (!$recursive || $files === false) {
return $files;
}

static $depth = 0;

foreach ($files as $key => $value) {
if ($depth != 0 && $key == '..') {
unset($files[$key]);
continue;
}
$is_directory = false;
if ($key != '.' && $key != '..') {
if ($this->use_stat_cache) {
$is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)));
} else {
$stat = $this->lstat($dir . '/' . $key);
$is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
}
}

if ($is_directory) {
$depth++;
$files[$key] = $this->rawlist($dir . '/' . $key, true);
$depth--;
} else {
$files[$key] = (object) $value;
}
}

return $files;
}









function _list($dir, $raw = true)
{
if (!$this->_precheck()) {
return false;
}

$dir = $this->_realpath($dir . '/');
if ($dir === false) {
return false;
}


if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:



$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
$this->_logError($response, $status);
return $status;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}

$this->_update_stat_cache($dir, array());

$contents = array();
while (true) {



if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount', $this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$shortname = $this->_string_shift($response, $length);


if ($this->version < 4) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$longname = $this->_string_shift($response, $length);
}
$attributes = $this->_parseAttributes($response);
if (!isset($attributes['type']) && $this->version < 4) {
$fileType = $this->_parseLongname($longname);
if ($fileType) {
$attributes['type'] = $fileType;
}
}
$contents[$shortname] = $attributes + array('filename' => $shortname);

if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
$this->_update_stat_cache($dir . '/' . $shortname, array());
} else {
if ($shortname == '..') {
$temp = $this->_realpath($dir . '/..') . '/.';
} else {
$temp = $dir . '/' . $shortname;
}
$this->_update_stat_cache($temp, (object) array('lstat' => $attributes));
}


}
break;
case NET_SFTP_STATUS:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) {
$this->_logError($response, $status);
return $status;
}
break 2;
default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
return false;
}
}

if (!$this->_close_handle($handle)) {
return false;
}

if (count($this->sortOptions)) {
uasort($contents, array(&$this, '_comparator'));
}

return $raw ? $contents : array_map('strval', array_keys($contents));
}











function _comparator($a, $b)
{
switch (true) {
case $a['filename'] === '.' || $b['filename'] === '.':
if ($a['filename'] === $b['filename']) {
return 0;
}
return $a['filename'] === '.' ? -1 : 1;
case $a['filename'] === '..' || $b['filename'] === '..':
if ($a['filename'] === $b['filename']) {
return 0;
}
return $a['filename'] === '..' ? -1 : 1;
case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY:
if (!isset($b['type'])) {
return 1;
}
if ($b['type'] !== $a['type']) {
return -1;
}
break;
case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY:
return 1;
}
foreach ($this->sortOptions as $sort => $order) {
if (!isset($a[$sort]) || !isset($b[$sort])) {
if (isset($a[$sort])) {
return -1;
}
if (isset($b[$sort])) {
return 1;
}
return 0;
}
switch ($sort) {
case 'filename':
$result = strcasecmp($a['filename'], $b['filename']);
if ($result) {
return $order === SORT_DESC ? -$result : $result;
}
break;
case 'permissions':
case 'mode':
$a[$sort]&= 07777;
$b[$sort]&= 07777;
default:
if ($a[$sort] === $b[$sort]) {
break;
}
return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort];
}
}
}





















function setListOrder()
{
$this->sortOptions = array();
$args = func_get_args();
if (empty($args)) {
return;
}
$len = count($args) & 0x7FFFFFFE;
for ($i = 0; $i < $len; $i+=2) {
$this->sortOptions[$args[$i]] = $args[$i + 1];
}
if (!count($this->sortOptions)) {
$this->sortOptions = array('bogus' => true);
}
}










function size($filename)
{
$result = $this->stat($filename);
if ($result === false) {
return false;
}
return isset($result['size']) ? $result['size'] : -1;
}








function _update_stat_cache($path, $value)
{
if ($this->use_stat_cache === false) {
return;
}


$dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

$temp = &$this->stat_cache;
$max = count($dirs) - 1;
foreach ($dirs as $i => $dir) {



if (is_object($temp)) {
$temp = array();
}
if (!isset($temp[$dir])) {
$temp[$dir] = array();
}
if ($i === $max) {
if (is_object($temp[$dir]) && is_object($value)) {
if (!isset($value->stat) && isset($temp[$dir]->stat)) {
$value->stat = $temp[$dir]->stat;
}
if (!isset($value->lstat) && isset($temp[$dir]->lstat)) {
$value->lstat = $temp[$dir]->lstat;
}
}
$temp[$dir] = $value;
break;
}
$temp = &$temp[$dir];
}
}








function _remove_from_stat_cache($path)
{
$dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

$temp = &$this->stat_cache;
$max = count($dirs) - 1;
foreach ($dirs as $i => $dir) {
if (!is_array($temp)) {
return false;
}
if ($i === $max) {
unset($temp[$dir]);
return true;
}
if (!isset($temp[$dir])) {
return false;
}
$temp = &$temp[$dir];
}
}










function _query_stat_cache($path)
{
$dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

$temp = &$this->stat_cache;
foreach ($dirs as $dir) {
if (!is_array($temp)) {
return null;
}
if (!isset($temp[$dir])) {
return null;
}
$temp = &$temp[$dir];
}
return $temp;
}










function stat($filename)
{
if (!$this->_precheck()) {
return false;
}

$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}

if ($this->use_stat_cache) {
$result = $this->_query_stat_cache($filename);
if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) {
return $result['.']->stat;
}
if (is_object($result) && isset($result->stat)) {
return $result->stat;
}
}

$stat = $this->_stat($filename, NET_SFTP_STAT);
if ($stat === false) {
$this->_remove_from_stat_cache($filename);
return false;
}
if (isset($stat['type'])) {
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object) array('stat' => $stat));
return $stat;
}

$pwd = $this->pwd;
$stat['type'] = $this->chdir($filename) ?
NET_SFTP_TYPE_DIRECTORY :
NET_SFTP_TYPE_REGULAR;
$this->pwd = $pwd;

if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object) array('stat' => $stat));

return $stat;
}










function lstat($filename)
{
if (!$this->_precheck()) {
return false;
}

$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}

if ($this->use_stat_cache) {
$result = $this->_query_stat_cache($filename);
if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) {
return $result['.']->lstat;
}
if (is_object($result) && isset($result->lstat)) {
return $result->lstat;
}
}

$lstat = $this->_stat($filename, NET_SFTP_LSTAT);
if ($lstat === false) {
$this->_remove_from_stat_cache($filename);
return false;
}
if (isset($lstat['type'])) {
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
return $lstat;
}

$stat = $this->_stat($filename, NET_SFTP_STAT);

if ($lstat != $stat) {
$lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK));
$this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
return $stat;
}

$pwd = $this->pwd;
$lstat['type'] = $this->chdir($filename) ?
NET_SFTP_TYPE_DIRECTORY :
NET_SFTP_TYPE_REGULAR;
$this->pwd = $pwd;

if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
$filename.= '/.';
}
$this->_update_stat_cache($filename, (object) array('lstat' => $lstat));

return $lstat;
}












function _stat($filename, $type)
{

$packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet($type, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
return $this->_parseAttributes($response);
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
}

user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
}









function truncate($filename, $new_size)
{
$attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); 

return $this->_setstat($filename, $attr, false);
}












function touch($filename, $time = null, $atime = null)
{
if (!$this->_precheck()) {
return false;
}

$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}

if (!isset($time)) {
$time = time();
}
if (!isset($atime)) {
$atime = $time;
}

if ($this->version < 4) {
$attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time);
} else {
$attr = pack(
'N5',
NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
$atime / 4294967296,
$atime,
$time / 4294967296,
$time
);
}

$packet = pack('Na*', strlen($filename), $filename);
$packet.= $this->version >= 5 ?
pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) :
pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL);
$packet.= $attr;

if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return $this->_close_handle(substr($response, 4));
case NET_SFTP_STATUS:
$this->_logError($response);
break;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}

return $this->_setstat($filename, $attr, false);
}

















function chown($filename, $uid, $recursive = false)
{

















$attr = $this->version < 4 ?


pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) :




pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, '');

return $this->_setstat($filename, $attr, $recursive);
}

















function chgrp($filename, $gid, $recursive = false)
{
$attr = $this->version < 4 ?
pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) :
pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid);

return $this->_setstat($filename, $attr, $recursive);
}













function chmod($mode, $filename, $recursive = false)
{
if (is_string($mode) && is_int($filename)) {
$temp = $mode;
$mode = $filename;
$filename = $temp;
}

$attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
if (!$this->_setstat($filename, $attr, $recursive)) {
return false;
}
if ($recursive) {
return true;
}

$filename = $this->realpath($filename);



$packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
$attrs = $this->_parseAttributes($response);
return $attrs['permissions'];
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
}

user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
}










function _setstat($filename, $attr, $recursive)
{
if (!$this->_precheck()) {
return false;
}

$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}

$this->_remove_from_stat_cache($filename);

if ($recursive) {
$i = 0;
$result = $this->_setstat_recursive($filename, $attr, $i);
$this->_read_put_responses($i);
return $result;
}

$packet = $this->version >= 4 ?
pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
pack('Na*a*', strlen($filename), $filename, $attr);
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
return false;
}








$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}

return true;
}












function _setstat_recursive($path, $attr, &$i)
{
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
$entries = $this->_list($path, true);

if ($entries === false || is_int($entries)) {
return $this->_setstat($path, $attr, false);
}



if (empty($entries)) {
return false;
}

unset($entries['.'], $entries['..']);
foreach ($entries as $filename => $props) {
if (!isset($props['type'])) {
return false;
}

$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
if (!$this->_setstat_recursive($temp, $attr, $i)) {
return false;
}
} else {
$packet = $this->version >= 4 ?
pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
pack('Na*a*', strlen($temp), $temp, $attr);
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
return false;
}

$i++;

if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
}
}

$packet = $this->version >= 4 ?
pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
pack('Na*a*', strlen($temp), $temp, $attr);
if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
return false;
}

$i++;

if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}

return true;
}








function readlink($link)
{
if (!$this->_precheck()) {
return false;
}

$link = $this->_realpath($link);

if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount', $this->_string_shift($response, 4)));

if (!$count) {
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
}











function symlink($target, $link)
{
if (!$this->_precheck()) {
return false;
}


$link = $this->_realpath($link);








if ($this->version == 6) {
$type = NET_SFTP_LINK;
$packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1);
} else {
$type = NET_SFTP_SYMLINK;














$packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ?
pack('Na*Na*', strlen($target), $target, strlen($link), $link) :
pack('Na*Na*', strlen($link), $link, strlen($target), $target);
}
if (!$this->_send_sftp_packet($type, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}

return true;
}










function mkdir($dir, $mode = -1, $recursive = false)
{
if (!$this->_precheck()) {
return false;
}

$dir = $this->_realpath($dir);

if ($recursive) {
$dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir));
if (empty($dirs[0])) {
array_shift($dirs);
$dirs[0] = '/' . $dirs[0];
}
for ($i = 0; $i < count($dirs); $i++) {
$temp = array_slice($dirs, 0, $i + 1);
$temp = implode('/', $temp);
$result = $this->_mkdir_helper($temp, $mode);
}
return $result;
}

return $this->_mkdir_helper($dir, $mode);
}









function _mkdir_helper($dir, $mode)
{

if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) {
return false;
}

$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}

if ($mode !== -1) {
$this->chmod($mode, $dir);
}

return true;
}








function rmdir($dir)
{
if (!$this->_precheck()) {
return false;
}

$dir = $this->_realpath($dir);
if ($dir === false) {
return false;
}

if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
return false;
}

$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {

$this->_logError($response, $status);
return false;
}

$this->_remove_from_stat_cache($dir);





return true;
}















































function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
{
if (!$this->_precheck()) {
return false;
}

$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}

$this->_remove_from_stat_cache($remote_file);

if ($this->version >= 5) {
$flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
} else {
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;



}

if ($start >= 0) {
$offset = $start;
} elseif ($mode & (self::RESUME | self::RESUME_START)) {

$size = $this->size($remote_file);
$offset = $size !== false ? $size : 0;
} else {
$offset = 0;
if ($this->version >= 5) {
$flags = NET_SFTP_OPEN_CREATE_TRUNCATE;
} else {
$flags|= NET_SFTP_OPEN_TRUNCATE;
}
}

$packet = pack('Na*', strlen($remote_file), $remote_file);
$packet.= $this->version >= 5 ?
pack('N3', 0, $flags, 0) :
pack('N2', $flags, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}


$dataCallback = false;
switch (true) {
case $mode & self::SOURCE_CALLBACK:
if (!is_callable($data)) {
user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag");
}
$dataCallback = $data;

break;
case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE;
$info = stream_get_meta_data($data);
if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
$fp = fopen('php://memory', 'w+');
stream_copy_to_stream($data, $fp);
rewind($fp);
} else {
$fp = $data;
}
break;
case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) {
user_error("$data is not a valid file");
return false;
}
$fp = @fopen($data, 'rb');
if (!$fp) {
return false;
}
}

if (isset($fp)) {
$stat = fstat($fp);
$size = !empty($stat) ? $stat['size'] : 0;

if ($local_start >= 0) {
fseek($fp, $local_start);
$size-= $local_start;
} elseif ($mode & self::RESUME) {
fseek($fp, $offset);
$size-= $offset;
}

} elseif ($dataCallback) {
$size = 0;
} else {
$size = strlen($data);
}

$sent = 0;
$size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;

$sftp_packet_size = $this->max_sftp_packet;

$sftp_packet_size-= strlen($handle) + 25;
$i = $j = 0;
while ($dataCallback || ($size === 0 || $sent < $size)) {
if ($dataCallback) {
$temp = call_user_func($dataCallback, $sftp_packet_size);
if (is_null($temp)) {
break;
}
} else {
$temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size);
if ($temp === false || $temp === '') {
break;
}
}

$subtemp = $offset + $sent;
$packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
return false;
}
$sent+= strlen($temp);
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $sent);
}

$i++;
$j++;

if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
$i = 0;
break;
}
$i = 0;
}
}

$result = $this->_close_handle($handle);

if (!$this->_read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
$this->_close_handle($handle);
return false;
}

if ($mode & SFTP::SOURCE_LOCAL_FILE) {
if (isset($fp) && is_resource($fp)) {
fclose($fp);
}

if ($this->preserveTime) {
$stat = stat($data);
if ($this->version < 4) {
$attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']);
} else {
$attr = pack(
'N5',
NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
$stat['atime'] / 4294967296,
$stat['atime'],
$stat['mtime'] / 4294967296,
$stat['mtime']
);
}

if (!$this->_setstat($remote_file, $attr, false)) {
user_error('Error setting file time');
}
}
}

return $result;
}











function _read_put_responses($i)
{
while ($i--) {
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
break;
}
}

return $i < 0;
}








function _close_handle($handle)
{
if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
return false;
}



$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}

if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}

return true;
}


















function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
{
if (!$this->_precheck()) {
return false;
}

$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}

$packet = pack('Na*', strlen($remote_file), $remote_file);
$packet.= $this->version >= 5 ?
pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
pack('N2', NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS: 
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}

if (is_resource($local_file)) {
$fp = $local_file;
$stat = fstat($fp);
$res_offset = $stat['size'];
} else {
$res_offset = 0;
if ($local_file !== false && !is_callable($local_file)) {
$fp = fopen($local_file, 'wb');
if (!$fp) {
return false;
}
} else {
$content = '';
}
}

$fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file);

$start = $offset;
$read = 0;
while (true) {
$i = 0;

while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) {
$tempoffset = $start + $read;

$packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;

$packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) {
if ($fclose_check) {
fclose($fp);
}
return false;
}
$packet = null;
$read+= $packet_size;
$i++;
}

if (!$i) {
break;
}

$packets_sent = $i - 1;

$clear_responses = false;
while ($i > 0) {
$i--;

if ($clear_responses) {
$this->_get_sftp_packet($packets_sent - $i);
continue;
} else {
$response = $this->_get_sftp_packet($packets_sent - $i);
}

switch ($this->packet_type) {
case NET_SFTP_DATA:
$temp = substr($response, 4);
$offset+= strlen($temp);
if ($local_file === false) {
$content.= $temp;
} elseif (is_callable($local_file)) {
$local_file($temp);
} else {
fputs($fp, $temp);
}
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $offset);
}
$temp = null;
break;
case NET_SFTP_STATUS:

$this->_logError($response);
$clear_responses = true; 
break;
default:
if ($fclose_check) {
fclose($fp);
}

if ($this->channel_close) {
$this->partial_init = false;
$this->_init_sftp_connection();
return false;
} else {
user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
}
}
$response = null;
}

if ($clear_responses) {
break;
}
}

if ($fclose_check) {
fclose($fp);

if ($this->preserveTime) {
$stat = $this->stat($remote_file);
touch($local_file, $stat['mtime'], $stat['atime']);
}
}

if (!$this->_close_handle($handle)) {
return false;
}


return isset($content) ? $content : true;
}









function delete($path, $recursive = true)
{
if (!$this->_precheck()) {
return false;
}

if (is_object($path)) {

$path = (string) $path;
}

if (!is_string($path) || $path == '') {
return false;
}

$path = $this->_realpath($path);
if ($path === false) {
return false;
}


if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
return false;
}

$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}


if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
if (!$recursive) {
return false;
}
$i = 0;
$result = $this->_delete_recursive($path, $i);
$this->_read_put_responses($i);
return $result;
}

$this->_remove_from_stat_cache($path);

return true;
}











function _delete_recursive($path, &$i)
{
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
$entries = $this->_list($path, true);


if ($entries === NET_SFTP_STATUS_NO_SUCH_FILE) {
return false;
}



if ($entries === false || is_int($entries)) {
$entries = array();
}

unset($entries['.'], $entries['..']);
foreach ($entries as $filename => $props) {
if (!isset($props['type'])) {
return false;
}

$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
if (!$this->_delete_recursive($temp, $i)) {
return false;
}
} else {
if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) {
return false;
}
$this->_remove_from_stat_cache($temp);

$i++;

if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}
}
}

if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) {
return false;
}
$this->_remove_from_stat_cache($path);

$i++;

if ($i >= NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
return false;
}
$i = 0;
}

return true;
}








function file_exists($path)
{
if ($this->use_stat_cache) {
if (!$this->_precheck()) {
return false;
}

$path = $this->_realpath($path);

$result = $this->_query_stat_cache($path);

if (isset($result)) {

return $result !== false;
}
}

return $this->stat($path) !== false;
}








function is_dir($path)
{
$result = $this->_get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_DIRECTORY;
}








function is_file($path)
{
$result = $this->_get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_REGULAR;
}








function is_link($path)
{
$result = $this->_get_lstat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
return $result === NET_SFTP_TYPE_SYMLINK;
}








function is_readable($path)
{
if (!$this->_precheck()) {
return false;
}

$path = $this->_realpath($path);

$packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: 
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}
}








function is_writable($path)
{
if (!$this->_precheck()) {
return false;
}

$path = $this->_realpath($path);

$packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: 
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
}
}










function is_writeable($path)
{
return $this->is_writable($path);
}








function fileatime($path)
{
return $this->_get_stat_cache_prop($path, 'atime');
}








function filemtime($path)
{
return $this->_get_stat_cache_prop($path, 'mtime');
}








function fileperms($path)
{
return $this->_get_stat_cache_prop($path, 'permissions');
}








function fileowner($path)
{
return $this->_get_stat_cache_prop($path, 'uid');
}








function filegroup($path)
{
return $this->_get_stat_cache_prop($path, 'gid');
}








function filesize($path)
{
return $this->_get_stat_cache_prop($path, 'size');
}








function filetype($path)
{
$type = $this->_get_stat_cache_prop($path, 'type');
if ($type === false) {
return false;
}

switch ($type) {
case NET_SFTP_TYPE_BLOCK_DEVICE:
return 'block';
case NET_SFTP_TYPE_CHAR_DEVICE:
return 'char';
case NET_SFTP_TYPE_DIRECTORY:
return 'dir';
case NET_SFTP_TYPE_FIFO:
return 'fifo';
case NET_SFTP_TYPE_REGULAR:
return 'file';
case NET_SFTP_TYPE_SYMLINK:
return 'link';
default:
return false;
}
}











function _get_stat_cache_prop($path, $prop)
{
return $this->_get_xstat_cache_prop($path, $prop, 'stat');
}











function _get_lstat_cache_prop($path, $prop)
{
return $this->_get_xstat_cache_prop($path, $prop, 'lstat');
}












function _get_xstat_cache_prop($path, $prop, $type)
{
if (!$this->_precheck()) {
return false;
}

if ($this->use_stat_cache) {
$path = $this->_realpath($path);

$result = $this->_query_stat_cache($path);

if (is_object($result) && isset($result->$type)) {
return $result->{$type}[$prop];
}
}

$result = $this->$type($path);

if ($result === false || !isset($result[$prop])) {
return false;
}

return $result[$prop];
}











function rename($oldname, $newname)
{
if (!$this->_precheck()) {
return false;
}

$oldname = $this->_realpath($oldname);
$newname = $this->_realpath($newname);
if ($oldname === false || $newname === false) {
return false;
}


$packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
if ($this->version >= 5) {









$packet.= "\0\0\0\0";
}
if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
return false;
}

$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS');
return false;
}


if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
return false;
}




$this->_remove_from_stat_cache($oldname);
$this->_remove_from_stat_cache($newname);

return true;
}












function _parseTime($key, $flags, &$response)
{
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return array();
}
$attr = array();
$attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8)));
if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) {
$attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4)));
}
return $attr;
}










function _parseAttributes(&$response)
{
if ($this->version >= 4) {
$length = 5;
$format = 'Nflags/Ctype';
} else {
$length = 4;
$format = 'Nflags';
}

$attr = array();
if (strlen($response) < $length) {
user_error('Malformed file attributes');
return array();
}
extract(unpack($format, $this->_string_shift($response, $length)));
if (isset($type)) {
$attr['type'] = $type;
}
foreach ($this->attributes as $key => $value) {
switch ($flags & $key) {
case NET_SFTP_ATTR_UIDGID:
if ($this->version > 3) {
continue 2;
}
break;
case NET_SFTP_ATTR_CREATETIME:
case NET_SFTP_ATTR_MODIFYTIME:
case NET_SFTP_ATTR_ACL:
case NET_SFTP_ATTR_OWNERGROUP:
case NET_SFTP_ATTR_SUBSECOND_TIMES:
if ($this->version < 4) {
continue 2;
}
break;
case NET_SFTP_ATTR_BITS:
if ($this->version < 5) {
continue 2;
}
break;
case NET_SFTP_ATTR_ALLOCATION_SIZE:
case NET_SFTP_ATTR_TEXT_HINT:
case NET_SFTP_ATTR_MIME_TYPE:
case NET_SFTP_ATTR_LINK_COUNT:
case NET_SFTP_ATTR_UNTRANSLATED_NAME:
case NET_SFTP_ATTR_CTIME:
if ($this->version < 6) {
continue 2;
}
}
switch ($flags & $key) {
case NET_SFTP_ATTR_SIZE: 






$attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
break;
case NET_SFTP_ATTR_UIDGID: 
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
break;
case NET_SFTP_ATTR_PERMISSIONS: 
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Npermissions', $this->_string_shift($response, 4));


$attr+= array('mode' => $attr['permissions']);
$fileType = $this->_parseMode($attr['permissions']);
if ($fileType !== false) {
$attr+= array('type' => $fileType);
}
break;
case NET_SFTP_ATTR_ACCESSTIME: 
if ($this->version >= 4) {
$attr+= $this->_parseTime('atime', $flags, $response);
break;
}
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
break;
case NET_SFTP_ATTR_CREATETIME: 
$attr+= $this->_parseTime('createtime', $flags, $response);
break;
case NET_SFTP_ATTR_MODIFYTIME: 
$attr+= $this->_parseTime('mtime', $flags, $response);
break;
case NET_SFTP_ATTR_ACL: 



if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Ncount', $this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 16) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16)));
if (strlen($response) < $length) {
user_error('Malformed file attributes');
return $attr;
}
$this->_string_shift($response, $length); 
}
break;
case NET_SFTP_ATTR_OWNERGROUP: 
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
if (strlen($response) < $length) {
user_error('Malformed file attributes');
return $attr;
}
$attr['owner'] = $this->_string_shift($response, $length);

if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
if (strlen($response) < $length) {
user_error('Malformed file attributes');
return $attr;
}
$attr['group'] = $this->_string_shift($response, $length);
break;
case NET_SFTP_ATTR_SUBSECOND_TIMES: 
break;
case NET_SFTP_ATTR_BITS: 





if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8)));
break;
case NET_SFTP_ATTR_ALLOCATION_SIZE: 



$attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
break;
case NET_SFTP_ATTR_TEXT_HINT: 



extract(unpack('Ctext-hint', $this->_string_shift($response)));
break;
case NET_SFTP_ATTR_MIME_TYPE: 

if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
if (strlen($response) < $length) {
user_error('Malformed file attributes');
return $attr;
}
$attr['mime-type'] = $this->_string_shift($response, $length);
break;
case NET_SFTP_ATTR_LINK_COUNT: 

if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Nlink-count', $this->_string_shift($response, 4));
break;
case NET_SFTP_ATTR_UNTRANSLATED_NAME:

if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
if (strlen($response) < $length) {
user_error('Malformed file attributes');
return $attr;
}
$attr['untranslated-name'] = $this->_string_shift($response, $length);
break;
case NET_SFTP_ATTR_CTIME: 


$attr+= $this->_parseTime('ctime', $flags, $response);
break;
case NET_SFTP_ATTR_EXTENDED: 
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Ncount', $this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$attr[$key] = $this->_string_shift($response, $length);
}
}
}
return $attr;
}










function _parseMode($mode)
{


switch ($mode & 0170000) {
case 0000000: 
return false;
case 0040000:
return NET_SFTP_TYPE_DIRECTORY;
case 0100000:
return NET_SFTP_TYPE_REGULAR;
case 0120000:
return NET_SFTP_TYPE_SYMLINK;


case 0010000: 
return NET_SFTP_TYPE_FIFO;
case 0020000: 
return NET_SFTP_TYPE_CHAR_DEVICE;
case 0060000: 
return NET_SFTP_TYPE_BLOCK_DEVICE;
case 0140000: 
return NET_SFTP_TYPE_SOCKET;
case 0160000: 


return NET_SFTP_TYPE_SPECIAL;
default:
return NET_SFTP_TYPE_UNKNOWN;
}
}
















function _parseLongname($longname)
{


if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
switch ($longname[0]) {
case '-':
return NET_SFTP_TYPE_REGULAR;
case 'd':
return NET_SFTP_TYPE_DIRECTORY;
case 'l':
return NET_SFTP_TYPE_SYMLINK;
default:
return NET_SFTP_TYPE_SPECIAL;
}
}

return false;
}














function _send_sftp_packet($type, $data, $request_id = 1)
{


$this->curTimeout = $this->timeout;

$packet = $this->use_request_id ?
pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) :
pack('NCa*', strlen($data) + 1, $type, $data);

$start = strtok(microtime(), ' ') + strtok(''); 
$result = $this->_send_channel_packet(self::CHANNEL, $packet);
$stop = strtok(microtime(), ' ') + strtok('');

if (defined('NET_SFTP_LOGGING')) {
$packet_type = '-> ' . $this->packet_types[$type] .
' (' . round($stop - $start, 4) . 's)';
if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($data), array($packet_type)) . $stop;
@flush();
@ob_flush();
} else {
$this->packet_type_log[] = $packet_type;
if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
$this->packet_log[] = $data;
}
}
}

return $result;
}







function _reset_connection($reason)
{
parent::_reset_connection($reason);
$this->use_request_id = false;
$this->pwd = false;
$this->requestBuffer = array();
$this->partial_init = false;
}














function _get_sftp_packet($request_id = null)
{
$this->channel_close = false;

if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
$this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
$temp = $this->requestBuffer[$request_id]['packet'];
unset($this->requestBuffer[$request_id]);
return $temp;
}



$this->curTimeout = $this->timeout;

$start = strtok(microtime(), ' ') + strtok(''); 


while (strlen($this->packet_buffer) < 4) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if ($temp === true) {
if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->channel_close = true;
}
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
if ($temp === false) {
return false;
}
$this->packet_buffer.= $temp;
}
if (strlen($this->packet_buffer) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
$tempLength = $length;
$tempLength-= strlen($this->packet_buffer);


if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) {
user_error('Invalid SFTP packet size');
return false;
}


while ($tempLength > 0) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if (is_bool($temp)) {
if ($temp && $this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->channel_close = true;
}
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
$this->packet_buffer.= $temp;
$tempLength-= strlen($temp);
}

$stop = strtok(microtime(), ' ') + strtok('');

$this->packet_type = ord($this->_string_shift($this->packet_buffer));

if ($this->use_request_id) {
extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); 
$length-= 5; 
} else {
$length-= 1; 
}

$packet = $this->_string_shift($this->packet_buffer, $length);

if (defined('NET_SFTP_LOGGING')) {
$packet_type = '<- ' . $this->packet_types[$this->packet_type] .
' (' . round($stop - $start, 4) . 's)';
if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
switch (PHP_SAPI) {
case 'cli':
$start = $stop = "\r\n";
break;
default:
$start = '<pre>';
$stop = '</pre>';
}
echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop;
@flush();
@ob_flush();
} else {
$this->packet_type_log[] = $packet_type;
if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
$this->packet_log[] = $packet;
}
}
}

if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) {
$this->requestBuffer[$packet_id] = array(
'packet_type' => $this->packet_type,
'packet' => $packet
);
return $this->_get_sftp_packet($request_id);
}

return $packet;
}









function getSFTPLog()
{
if (!defined('NET_SFTP_LOGGING')) {
return false;
}

switch (NET_SFTP_LOGGING) {
case self::LOG_COMPLEX:
return $this->_format_log($this->packet_log, $this->packet_type_log);
break;

default:
return $this->packet_type_log;
}
}







function getSFTPErrors()
{
return $this->sftp_errors;
}







function getLastSFTPError()
{
return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
}







function getSupportedVersions()
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}

if (!$this->partial_init) {
$this->_partial_init_sftp_connection();
}

$temp = array('version' => $this->defaultVersion);
if (isset($this->extensions['versions'])) {
$temp['extensions'] = $this->extensions['versions'];
}
return $temp;
}







function getNegotiatedVersion()
{
if (!$this->_precheck()) {
return false;
}

return $this->version;
}











function setPreferredVersion($version)
{
$this->preferredVersion = $version;
}








function _disconnect($reason)
{
$this->pwd = false;
parent::_disconnect($reason);
}






function enableDatePreservation()
{
$this->preserveTime = true;
}






function disableDatePreservation()
{
$this->preserveTime = false;
}
}
<?php
















































namespace phpseclib\Math;

use phpseclib\Crypt\Random;









class BigInteger
{










const MONTGOMERY = 0;



const BARRETT = 1;



const POWEROF2 = 2;



const CLASSIC = 3;



const NONE = 4;













const VALUE = 0;



const SIGN = 1;












const VARIABLE = 0;



const DATA = 1;











const MODE_INTERNAL = 1;





const MODE_BCMATH = 2;





const MODE_GMP = 3;









const KARATSUBA_CUTOFF = 25;






static $base;
static $baseFull;
static $maxDigit;
static $msb;





static $max10;





static $max10Len;
static $maxDigit2;








var $value;







var $is_negative = false;







var $precision = -1;







var $bitmask = false;













var $hex;





















function __construct($x = 0, $base = 10)
{
if (!defined('MATH_BIGINTEGER_MODE')) {
switch (true) {
case extension_loaded('gmp'):
define('MATH_BIGINTEGER_MODE', self::MODE_GMP);
break;
case extension_loaded('bcmath'):
define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH);
break;
default:
define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL);
}
}

if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {

$versions = array();


if (function_exists('phpinfo')) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();

preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);

if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));


if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
$versions[$matches[1][$i]] = $fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}


switch (true) {
case !isset($versions['Header']):
case !isset($versions['Library']):
case $versions['Header'] == $versions['Library']:
case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
break;
default:
define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
}
}

if (!defined('PHP_INT_SIZE')) {
define('PHP_INT_SIZE', 4);
}

if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) {
switch (PHP_INT_SIZE) {
case 8: 
self::$base = 31;
self::$baseFull = 0x80000000;
self::$maxDigit = 0x7FFFFFFF;
self::$msb = 0x40000000;
self::$max10 = 1000000000;
self::$max10Len = 9;
self::$maxDigit2 = pow(2, 62);
break;

default:
self::$base = 26;
self::$baseFull = 0x4000000;
self::$maxDigit = 0x3FFFFFF;
self::$msb = 0x2000000;
self::$max10 = 10000000;
self::$max10Len = 7;
self::$maxDigit2 = pow(2, 52); 
}
}

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
switch (true) {
case is_resource($x) && get_resource_type($x) == 'GMP integer':

case $x instanceof \GMP:
$this->value = $x;
return;
}
$this->value = gmp_init(0);
break;
case self::MODE_BCMATH:
$this->value = '0';
break;
default:
$this->value = array();
}



if (empty($x) && (abs($base) != 256 || $x !== '0')) {
return;
}

switch ($base) {
case -256:
if (ord($x[0]) & 0x80) {
$x = ~$x;
$this->is_negative = true;
}
case 256:
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$this->value = function_exists('gmp_import') ?
gmp_import($x) :
gmp_init('0x' . bin2hex($x));
if ($this->is_negative) {
$this->value = gmp_neg($this->value);
}
break;
case self::MODE_BCMATH:

$len = (strlen($x) + 3) & ~3;

$x = str_pad($x, $len, chr(0), STR_PAD_LEFT);

for ($i = 0; $i < $len; $i+= 4) {
$this->value = bcmul($this->value, '4294967296', 0); 
$this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
}

if ($this->is_negative) {
$this->value = '-' . $this->value;
}

break;

default:
while (strlen($x)) {
$this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base));
}
}

if ($this->is_negative) {
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
$this->is_negative = false;
}
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case 16:
case -16:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}

$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x);

$is_negative = false;
if ($base < 0 && hexdec($x[0]) >= 8) {
$this->is_negative = $is_negative = true;
$x = bin2hex(~pack('H*', $x));
}

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
$this->value = gmp_init($temp);
$this->is_negative = false;
break;
case self::MODE_BCMATH:
$x = (strlen($x) & 1) ? '0' . $x : $x;
$temp = new static(pack('H*', $x), 256);
$this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
$this->is_negative = false;
break;
default:
$x = (strlen($x) & 1) ? '0' . $x : $x;
$temp = new static(pack('H*', $x), 256);
$this->value = $temp->value;
}

if ($is_negative) {
$temp = $this->add(new static('-1'));
$this->value = $temp->value;
}
break;
case 10:
case -10:



$x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#s', '', $x);
if (!strlen($x) || $x == '-') {
$x = '0';
}

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$this->value = gmp_init($x);
break;
case self::MODE_BCMATH:


$this->value = $x === '-' ? '0' : (string) $x;
break;
default:
$temp = new static();

$multiplier = new static();
$multiplier->value = array(self::$max10);

if ($x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}

$x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
while (strlen($x)) {
$temp = $temp->multiply($multiplier);
$temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
$x = substr($x, self::$max10Len);
}

$this->value = $temp->value;
}
break;
case 2: 
case -2:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}

$x = preg_replace('#^([01]*).*#s', '$1', $x);
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);

$str = '0x';
while (strlen($x)) {
$part = substr($x, 0, 4);
$str.= dechex(bindec($part));
$x = substr($x, 4);
}

if ($this->is_negative) {
$str = '-' . $str;
}

$temp = new static($str, 8 * $base); 
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;

break;
default:

}
}





















function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
$comparison = $this->compare(new static());
if ($comparison == 0) {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}

$temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy();
$bytes = $temp->toBytes();

if (!strlen($bytes)) { 
$bytes = chr(0);
}

if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) {
$bytes = chr(0) . $bytes;
}

return $comparison < 0 ? ~$bytes : $bytes;
}

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}

if (function_exists('gmp_export')) {
$temp = gmp_export($this->value);
} else {
$temp = gmp_strval(gmp_abs($this->value), 16);
$temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
$temp = pack('H*', $temp);
}

return $this->precision > 0 ?
substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($temp, chr(0));
case self::MODE_BCMATH:
if ($this->value === '0') {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}

$value = '';
$current = $this->value;

if ($current[0] == '-') {
$current = substr($current, 1);
}

while (bccomp($current, '0', 0) > 0) {
$temp = bcmod($current, '16777216');
$value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
$current = bcdiv($current, '16777216', 0);
}

return $this->precision > 0 ?
substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($value, chr(0));
}

if (!count($this->value)) {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}
$result = $this->_int2bytes($this->value[count($this->value) - 1]);

$temp = $this->copy();

for ($i = count($temp->value) - 2; $i >= 0; --$i) {
$temp->_base256_lshift($result, self::$base);
$result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
}

return $this->precision > 0 ?
str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
$result;
}





















function toHex($twos_compliment = false)
{
return bin2hex($this->toBytes($twos_compliment));
}





















function toBits($twos_compliment = false)
{
$hex = $this->toHex($twos_compliment);
$bits = '';
for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) {
$bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits;
}
if ($start) { 
$bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits;
}
$result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');

if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
return '0' . $result;
}

return $result;
}

















function toString()
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_strval($this->value);
case self::MODE_BCMATH:
if ($this->value === '0') {
return '0';
}

return ltrim($this->value, '0');
}

if (!count($this->value)) {
return '0';
}

$temp = $this->copy();
$temp->bitmask = false;
$temp->is_negative = false;

$divisor = new static();
$divisor->value = array(self::$max10);
$result = '';
while (count($temp->value)) {
list($temp, $mod) = $temp->divide($divisor);
$result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
}
$result = ltrim($result, '0');
if (empty($result)) {
$result = '0';
}

if ($this->is_negative) {
$result = '-' . $result;
}

return $result;
}






function getLength()
{
if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
return strlen($this->toBits());
}

$max = count($this->value) - 1;
return $max != -1 ?
$max * self::$base + intval(ceil(log($this->value[$max] + 1, 2))) :
0;
}






function getLengthInBytes()
{
return (int) ceil($this->getLength() / 8);
}













function copy()
{
$temp = new static();
$temp->value = $this->value;
$temp->is_negative = $this->is_negative;
$temp->precision = $this->precision;
$temp->bitmask = $this->bitmask;
return $temp;
}










function __toString()
{
return $this->toString();
}













function __clone()
{
return $this->copy();
}









function __sleep()
{
$this->hex = $this->toHex(true);
$vars = array('hex');
if ($this->precision > 0) {
$vars[] = 'precision';
}
return $vars;
}









function __wakeup()
{
$temp = new static($this->hex, -16);
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
if ($this->precision > 0) {

$this->setPrecision($this->precision);
}
}








function __debugInfo()
{
$opts = array();
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$engine = 'gmp';
break;
case self::MODE_BCMATH:
$engine = 'bcmath';
break;
case self::MODE_INTERNAL:
$engine = 'internal';
$opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit';
}
if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
$opts[] = 'OpenSSL';
}
if (!empty($opts)) {
$engine.= ' (' . implode('.', $opts) . ')';
}
return array(
'value' => '0x' . $this->toHex(true),
'engine' => $engine
);
}





















function add($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_add($this->value, $y->value);

return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcadd($this->value, $y->value, 0);

return $this->_normalize($temp);
}

$temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);

$result = new static();
$result->value = $temp[self::VALUE];
$result->is_negative = $temp[self::SIGN];

return $this->_normalize($result);
}











function _add($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);

if ($x_size == 0) {
return array(
self::VALUE => $y_value,
self::SIGN => $y_negative
);
} elseif ($y_size == 0) {
return array(
self::VALUE => $x_value,
self::SIGN => $x_negative
);
}


if ($x_negative != $y_negative) {
if ($x_value == $y_value) {
return array(
self::VALUE => array(),
self::SIGN => false
);
}

$temp = $this->_subtract($x_value, false, $y_value, false);
$temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
$x_negative : $y_negative;

return $temp;
}

if ($x_size < $y_size) {
$size = $x_size;
$value = $y_value;
} else {
$size = $y_size;
$value = $x_value;
}

$value[count($value)] = 0; 

$carry = 0;
for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
$carry = $sum >= self::$maxDigit2; 
$sum = $carry ? $sum - self::$maxDigit2 : $sum;

$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);

$value[$i] = (int) ($sum - self::$baseFull * $temp); 
$value[$j] = $temp;
}

if ($j == $size) { 
$sum = $x_value[$i] + $y_value[$i] + $carry;
$carry = $sum >= self::$baseFull;
$value[$i] = $carry ? $sum - self::$baseFull : $sum;
++$i; 
}

if ($carry) {
for (; $value[$i] == self::$maxDigit; ++$i) {
$value[$i] = 0;
}
++$value[$i];
}

return array(
self::VALUE => $this->_trim($value),
self::SIGN => $x_negative
);
}





















function subtract($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_sub($this->value, $y->value);

return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcsub($this->value, $y->value, 0);

return $this->_normalize($temp);
}

$temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);

$result = new static();
$result->value = $temp[self::VALUE];
$result->is_negative = $temp[self::SIGN];

return $this->_normalize($result);
}











function _subtract($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);

if ($x_size == 0) {
return array(
self::VALUE => $y_value,
self::SIGN => !$y_negative
);
} elseif ($y_size == 0) {
return array(
self::VALUE => $x_value,
self::SIGN => $x_negative
);
}


if ($x_negative != $y_negative) {
$temp = $this->_add($x_value, false, $y_value, false);
$temp[self::SIGN] = $x_negative;

return $temp;
}

$diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);

if (!$diff) {
return array(
self::VALUE => array(),
self::SIGN => false
);
}


if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;

$x_negative = !$x_negative;

$x_size = count($x_value);
$y_size = count($y_value);
}



$carry = 0;
for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
$sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
$carry = $sum < 0; 
$sum = $carry ? $sum + self::$maxDigit2 : $sum;

$temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);

$x_value[$i] = (int) ($sum - self::$baseFull * $temp);
$x_value[$j] = $temp;
}

if ($j == $y_size) { 
$sum = $x_value[$i] - $y_value[$i] - $carry;
$carry = $sum < 0;
$x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
++$i;
}

if ($carry) {
for (; !$x_value[$i]; ++$i) {
$x_value[$i] = self::$maxDigit;
}
--$x_value[$i];
}

return array(
self::VALUE => $this->_trim($x_value),
self::SIGN => $x_negative
);
}




















function multiply($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_mul($this->value, $x->value);

return $this->_normalize($temp);
case self::MODE_BCMATH:
$temp = new static();
$temp->value = bcmul($this->value, $x->value, 0);

return $this->_normalize($temp);
}

$temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);

$product = new static();
$product->value = $temp[self::VALUE];
$product->is_negative = $temp[self::SIGN];

return $this->_normalize($product);
}











function _multiply($x_value, $x_negative, $y_value, $y_negative)
{







$x_length = count($x_value);
$y_length = count($y_value);

if (!$x_length || !$y_length) { 
return array(
self::VALUE => array(),
self::SIGN => false
);
}

return array(
self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
$this->_trim($this->_regularMultiply($x_value, $y_value)) :
$this->_trim($this->_karatsuba($x_value, $y_value)),
self::SIGN => $x_negative != $y_negative
);
}











function _regularMultiply($x_value, $y_value)
{
$x_length = count($x_value);
$y_length = count($y_value);

if (!$x_length || !$y_length) { 
return array();
}

if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;

$x_length = count($x_value);
$y_length = count($y_value);
}

$product_value = $this->_array_repeat(0, $x_length + $y_length);







$carry = 0;

for ($j = 0; $j < $x_length; ++$j) { 
$temp = $x_value[$j] * $y_value[0] + $carry; 
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
}

$product_value[$j] = $carry;



for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;

for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$k] = (int) ($temp - self::$baseFull * $carry);
}

$product_value[$k] = $carry;
}

return $product_value;
}












function _karatsuba($x_value, $y_value)
{
$m = min(count($x_value) >> 1, count($y_value) >> 1);

if ($m < self::KARATSUBA_CUTOFF) {
return $this->_regularMultiply($x_value, $y_value);
}

$x1 = array_slice($x_value, $m);
$x0 = array_slice($x_value, 0, $m);
$y1 = array_slice($y_value, $m);
$y0 = array_slice($y_value, 0, $m);

$z2 = $this->_karatsuba($x1, $y1);
$z0 = $this->_karatsuba($x0, $y0);

$z1 = $this->_add($x1, false, $x0, false);
$temp = $this->_add($y1, false, $y0, false);
$z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);

$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);

$xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
$xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false);

return $xy[self::VALUE];
}








function _square($x = false)
{
return count($x) < 2 * self::KARATSUBA_CUTOFF ?
$this->_trim($this->_baseSquare($x)) :
$this->_trim($this->_karatsubaSquare($x));
}












function _baseSquare($value)
{
if (empty($value)) {
return array();
}
$square_value = $this->_array_repeat(0, 2 * count($value));

for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
$i2 = $i << 1;

$temp = $square_value[$i2] + $value[$i] * $value[$i];
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$square_value[$i2] = (int) ($temp - self::$baseFull * $carry);


for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$square_value[$k] = (int) ($temp - self::$baseFull * $carry);
}



$square_value[$i + $max_index + 1] = $carry;
}

return $square_value;
}











function _karatsubaSquare($value)
{
$m = count($value) >> 1;

if ($m < self::KARATSUBA_CUTOFF) {
return $this->_baseSquare($value);
}

$x1 = array_slice($value, $m);
$x0 = array_slice($value, 0, $m);

$z2 = $this->_karatsubaSquare($x1);
$z0 = $this->_karatsubaSquare($x0);

$z1 = $this->_add($x1, false, $x0, false);
$z1 = $this->_karatsubaSquare($z1[self::VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);

$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);

$xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
$xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false);

return $xx[self::VALUE];
}




























function divide($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$quotient = new static();
$remainder = new static();

list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);

if (gmp_sign($remainder->value) < 0) {
$remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
}

return array($this->_normalize($quotient), $this->_normalize($remainder));
case self::MODE_BCMATH:
$quotient = new static();
$remainder = new static();

$quotient->value = bcdiv($this->value, $y->value, 0);
$remainder->value = bcmod($this->value, $y->value);

if ($remainder->value[0] == '-') {
$remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
}

return array($this->_normalize($quotient), $this->_normalize($remainder));
}

if (count($y->value) == 1) {
list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
$quotient = new static();
$remainder = new static();
$quotient->value = $q;
$remainder->value = array($r);
$quotient->is_negative = $this->is_negative != $y->is_negative;
return array($this->_normalize($quotient), $this->_normalize($remainder));
}

static $zero;
if (!isset($zero)) {
$zero = new static();
}

$x = $this->copy();
$y = $y->copy();

$x_sign = $x->is_negative;
$y_sign = $y->is_negative;

$x->is_negative = $y->is_negative = false;

$diff = $x->compare($y);

if (!$diff) {
$temp = new static();
$temp->value = array(1);
$temp->is_negative = $x_sign != $y_sign;
return array($this->_normalize($temp), $this->_normalize(new static()));
}

if ($diff < 0) {

if ($x_sign) {
$x = $y->subtract($x);
}
return array($this->_normalize(new static()), $this->_normalize($x));
}


$msb = $y->value[count($y->value) - 1];
for ($shift = 0; !($msb & self::$msb); ++$shift) {
$msb <<= 1;
}
$x->_lshift($shift);
$y->_lshift($shift);
$y_value = &$y->value;

$x_max = count($x->value) - 1;
$y_max = count($y->value) - 1;

$quotient = new static();
$quotient_value = &$quotient->value;
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);

static $temp, $lhs, $rhs;
if (!isset($temp)) {
$temp = new static();
$lhs = new static();
$rhs = new static();
}
$temp_value = &$temp->value;
$rhs_value = &$rhs->value;


$temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);

while ($x->compare($temp) >= 0) {

++$quotient_value[$x_max - $y_max];
$x = $x->subtract($temp);
$x_max = count($x->value) - 1;
}

for ($i = $x_max; $i >= $y_max + 1; --$i) {
$x_value = &$x->value;
$x_window = array(
isset($x_value[$i]) ? $x_value[$i] : 0,
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
);
$y_window = array(
$y_value[$y_max],
($y_max > 0) ? $y_value[$y_max - 1] : 0
);

$q_index = $i - $y_max - 1;
if ($x_window[0] == $y_window[0]) {
$quotient_value[$q_index] = self::$maxDigit;
} else {
$quotient_value[$q_index] = $this->_safe_divide(
$x_window[0] * self::$baseFull + $x_window[1],
$y_window[0]
);
}

$temp_value = array($y_window[1], $y_window[0]);

$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);

$rhs_value = array($x_window[2], $x_window[1], $x_window[0]);

while ($lhs->compare($rhs) > 0) {
--$quotient_value[$q_index];

$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
}

$adjust = $this->_array_repeat(0, $q_index);
$temp_value = array($quotient_value[$q_index]);
$temp = $temp->multiply($y);
$temp_value = &$temp->value;
if (count($temp_value)) {
$temp_value = array_merge($adjust, $temp_value);
}

$x = $x->subtract($temp);

if ($x->compare($zero) < 0) {
$temp_value = array_merge($adjust, $y_value);
$x = $x->add($temp);

--$quotient_value[$q_index];
}

$x_max = count($x_value) - 1;
}


$x->_rshift($shift);

$quotient->is_negative = $x_sign != $y_sign;


if ($x_sign) {
$y->_rshift($shift);
$x = $y->subtract($x);
}

return array($this->_normalize($quotient), $this->_normalize($x));
}











function _divide_digit($dividend, $divisor)
{
$carry = 0;
$result = array();

for ($i = count($dividend) - 1; $i >= 0; --$i) {
$temp = self::$baseFull * $carry + $dividend[$i];
$result[$i] = $this->_safe_divide($temp, $divisor);
$carry = (int) ($temp - $divisor * $result[$i]);
}

return array($result, $carry);
}









































function modPow($e, $n)
{
$n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();

if ($e->compare(new static()) < 0) {
$e = $e->abs();

$temp = $this->modInverse($n);
if ($temp === false) {
return false;
}

return $this->_normalize($temp->modPow($e, $n));
}

if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
$temp = new static();
$temp->value = gmp_powm($this->value, $e->value, $n->value);

return $this->_normalize($temp);
}

if ($this->compare(new static()) < 0 || $this->compare($n) > 0) {
list(, $temp) = $this->divide($n);
return $temp->modPow($e, $n);
}

if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
$components = array(
'modulus' => $n->toBytes(true),
'publicExponent' => $e->toBytes(true)
);

$components = array(
'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
);

$RSAPublicKey = pack(
'Ca*a*a*',
48,
$this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);

$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); 
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;

$encapsulated = pack(
'Ca*a*',
48,
$this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)),
$rsaOID . $RSAPublicKey
);

$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($encapsulated)) .
'-----END PUBLIC KEY-----';

$plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);

if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
return new static($result, 256);
}
}

if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$temp = new static();
$temp->value = bcpowmod($this->value, $e->value, $n->value, 0);

return $this->_normalize($temp);
}

if (empty($e->value)) {
$temp = new static();
$temp->value = array(1);
return $this->_normalize($temp);
}

if ($e->value == array(1)) {
list(, $temp) = $this->divide($n);
return $this->_normalize($temp);
}

if ($e->value == array(2)) {
$temp = new static();
$temp->value = $this->_square($this->value);
list(, $temp) = $temp->divide($n);
return $this->_normalize($temp);
}

return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT));







if ($n->value[0] & 1) {
return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY));
}



for ($i = 0; $i < count($n->value); ++$i) {
if ($n->value[$i]) {
$temp = decbin($n->value[$i]);
$j = strlen($temp) - strrpos($temp, '1') - 1;
$j+= 26 * $i;
break;
}
}


$mod1 = $n->copy();
$mod1->_rshift($j);
$mod2 = new static();
$mod2->value = array(1);
$mod2->_lshift($j);

$part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
$part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);

$y1 = $mod2->modInverse($mod1);
$y2 = $mod1->modInverse($mod2);

$result = $part1->multiply($mod2);
$result = $result->multiply($y1);

$temp = $part2->multiply($mod1);
$temp = $temp->multiply($y2);

$result = $result->add($temp);
list(, $result) = $result->divide($n);

return $this->_normalize($result);
}











function powMod($e, $n)
{
return $this->modPow($e, $n);
}















function _slidingWindow($e, $n, $mode)
{
static $window_ranges = array(7, 25, 81, 241, 673, 1793); 


$e_value = $e->value;
$e_length = count($e_value) - 1;
$e_bits = decbin($e_value[$e_length]);
for ($i = $e_length - 1; $i >= 0; --$i) {
$e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT);
}

$e_length = strlen($e_bits);



for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
}

$n_value = $n->value;


$powers = array();
$powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
$powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);



$temp = 1 << ($window_size - 1);
for ($i = 1; $i < $temp; ++$i) {
$i2 = $i << 1;
$powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
}

$result = array(1);
$result = $this->_prepareReduce($result, $n_value, $mode);

for ($i = 0; $i < $e_length;) {
if (!$e_bits[$i]) {
$result = $this->_squareReduce($result, $n_value, $mode);
++$i;
} else {
for ($j = $window_size - 1; $j > 0; --$j) {
if (!empty($e_bits[$i + $j])) {
break;
}
}


for ($k = 0; $k <= $j; ++$k) {
$result = $this->_squareReduce($result, $n_value, $mode);
}

$result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);

$i += $j + 1;
}
}

$temp = new static();
$temp->value = $this->_reduce($result, $n_value, $mode);

return $temp;
}













function _reduce($x, $n, $mode)
{
switch ($mode) {
case self::MONTGOMERY:
return $this->_montgomery($x, $n);
case self::BARRETT:
return $this->_barrett($x, $n);
case self::POWEROF2:
$lhs = new static();
$lhs->value = $x;
$rhs = new static();
$rhs->value = $n;
return $x->_mod2($n);
case self::CLASSIC:
$lhs = new static();
$lhs->value = $x;
$rhs = new static();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
case self::NONE:
return $x;
default:

}
}











function _prepareReduce($x, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_prepMontgomery($x, $n);
}
return $this->_reduce($x, $n, $mode);
}












function _multiplyReduce($x, $y, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_montgomeryMultiply($x, $y, $n);
}
$temp = $this->_multiply($x, false, $y, false);
return $this->_reduce($temp[self::VALUE], $n, $mode);
}











function _squareReduce($x, $n, $mode)
{
if ($mode == self::MONTGOMERY) {
return $this->_montgomeryMultiply($x, $x, $n);
}
return $this->_reduce($this->_square($x), $n, $mode);
}












function _mod2($n)
{
$temp = new static();
$temp->value = array(1);
return $this->bitwise_and($n->subtract($temp));
}

























function _barrett($n, $m)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);

$m_length = count($m);


if (count($n) > 2 * $m_length) {
$lhs = new static();
$rhs = new static();
$lhs->value = $n;
$rhs->value = $m;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}


if ($m_length < 5) {
return $this->_regularBarrett($n, $m);
}



if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;

$lhs = new static();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
$lhs_value[] = 1;
$rhs = new static();
$rhs->value = $m;

list($u, $m1) = $lhs->divide($rhs);
$u = $u->value;
$m1 = $m1->value;

$cache[self::DATA][] = array(
'u' => $u, 
'm1'=> $m1 
);
} else {
extract($cache[self::DATA][$key]);
}

$cutoff = $m_length + ($m_length >> 1);
$lsd = array_slice($n, 0, $cutoff); 
$msd = array_slice($n, $cutoff); 
$lsd = $this->_trim($lsd);
$temp = $this->_multiply($msd, false, $m1, false);
$n = $this->_add($lsd, false, $temp[self::VALUE], false); 

if ($m_length & 1) {
return $this->_regularBarrett($n[self::VALUE], $m);
}


$temp = array_slice($n[self::VALUE], $m_length - 1);


$temp = $this->_multiply($temp, false, $u, false);


$temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);


$temp = $this->_multiply($temp, false, $m, false);





$result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false);

while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
$result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false);
}

return $result[self::VALUE];
}













function _regularBarrett($x, $n)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);

$n_length = count($n);

if (count($x) > 2 * $n_length) {
$lhs = new static();
$rhs = new static();
$lhs->value = $x;
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}

if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $n;
$lhs = new static();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, 2 * $n_length);
$lhs_value[] = 1;
$rhs = new static();
$rhs->value = $n;
list($temp, ) = $lhs->divide($rhs); 
$cache[self::DATA][] = $temp->value;
}


$temp = array_slice($x, $n_length - 1);

$temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false);

$temp = array_slice($temp[self::VALUE], $n_length + 1);


$result = array_slice($x, 0, $n_length + 1);

$temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);


if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
$corrector_value = $this->_array_repeat(0, $n_length + 1);
$corrector_value[count($corrector_value)] = 1;
$result = $this->_add($result, false, $corrector_value, false);
$result = $result[self::VALUE];
}


$result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]);
while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
$result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false);
}

return $result[self::VALUE];
}















function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
{
$x_length = count($x_value);
$y_length = count($y_value);

if (!$x_length || !$y_length) { 
return array(
self::VALUE => array(),
self::SIGN => false
);
}

if ($x_length < $y_length) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;

$x_length = count($x_value);
$y_length = count($y_value);
}

$product_value = $this->_array_repeat(0, $x_length + $y_length);







$carry = 0;

for ($j = 0; $j < $x_length; ++$j) { 
$temp = $x_value[$j] * $y_value[0] + $carry; 
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$j] = (int) ($temp - self::$baseFull * $carry);
}

if ($j < $stop) {
$product_value[$j] = $carry;
}




for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;

for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$product_value[$k] = (int) ($temp - self::$baseFull * $carry);
}

if ($k < $stop) {
$product_value[$k] = $carry;
}
}

return array(
self::VALUE => $this->_trim($product_value),
self::SIGN => $x_negative != $y_negative
);
}
















function _montgomery($x, $n)
{
static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);

if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $x;
$cache[self::DATA][] = $this->_modInverse67108864($n);
}

$k = count($n);

$result = array(self::VALUE => $x);

for ($i = 0; $i < $k; ++$i) {
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
$temp = $this->_regularMultiply(array($temp), $n);
$temp = array_merge($this->_array_repeat(0, $i), $temp);
$result = $this->_add($result[self::VALUE], false, $temp, false);
}

$result[self::VALUE] = array_slice($result[self::VALUE], $k);

if ($this->_compare($result, false, $n, false) >= 0) {
$result = $this->_subtract($result[self::VALUE], false, $n, false);
}

return $result[self::VALUE];
}















function _montgomeryMultiply($x, $y, $m)
{
$temp = $this->_multiply($x, false, $y, false);
return $this->_montgomery($temp[self::VALUE], $m);






static $cache = array(
self::VARIABLE => array(),
self::DATA => array()
);

if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]);
$cache[self::VARIABLE][] = $m;
$cache[self::DATA][] = $this->_modInverse67108864($m);
}

$n = max(count($x), count($y), count($m));
$x = array_pad($x, $n, 0);
$y = array_pad($y, $n, 0);
$m = array_pad($m, $n, 0);
$a = array(self::VALUE => $this->_array_repeat(0, $n + 1));
for ($i = 0; $i < $n; ++$i) {
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
$temp = $temp * $cache[self::DATA][$key];
$temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
$temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
$a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false);
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
}
if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) {
$a = $this->_subtract($a[self::VALUE], false, $m, false);
}
return $a[self::VALUE];
}











function _prepMontgomery($x, $n)
{
$lhs = new static();
$lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
$rhs = new static();
$rhs->value = $n;

list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}



























function _modInverse67108864($x) 
{
$x = -$x[0];
$result = $x & 0x3; 
$result = ($result * (2 - $x * $result)) & 0xF; 
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; 
$result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; 
$result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); 
return $result & self::$maxDigit;
}




























function modInverse($n)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_invert($this->value, $n->value);

return ($temp->value === false) ? false : $this->_normalize($temp);
}

static $zero, $one;
if (!isset($zero)) {
$zero = new static();
$one = new static(1);
}


$n = $n->abs();

if ($this->compare($zero) < 0) {
$temp = $this->abs();
$temp = $temp->modInverse($n);
return $this->_normalize($n->subtract($temp));
}

extract($this->extendedGCD($n));

if (!$gcd->equals($one)) {
return false;
}

$x = $x->compare($zero) < 0 ? $x->add($n) : $x;

return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
}





























function extendedGCD($n)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
extract(gmp_gcdext($this->value, $n->value));

return array(
'gcd' => $this->_normalize(new static($g)),
'x' => $this->_normalize(new static($s)),
'y' => $this->_normalize(new static($t))
);
case self::MODE_BCMATH:




$u = $this->value;
$v = $n->value;

$a = '1';
$b = '0';
$c = '0';
$d = '1';

while (bccomp($v, '0', 0) != 0) {
$q = bcdiv($u, $v, 0);

$temp = $u;
$u = $v;
$v = bcsub($temp, bcmul($v, $q, 0), 0);

$temp = $a;
$a = $c;
$c = bcsub($temp, bcmul($a, $q, 0), 0);

$temp = $b;
$b = $d;
$d = bcsub($temp, bcmul($b, $q, 0), 0);
}

return array(
'gcd' => $this->_normalize(new static($u)),
'x' => $this->_normalize(new static($a)),
'y' => $this->_normalize(new static($b))
);
}

$y = $n->copy();
$x = $this->copy();
$g = new static();
$g->value = array(1);

while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
$x->_rshift(1);
$y->_rshift(1);
$g->_lshift(1);
}

$u = $x->copy();
$v = $y->copy();

$a = new static();
$b = new static();
$c = new static();
$d = new static();

$a->value = $d->value = $g->value = array(1);
$b->value = $c->value = array();

while (!empty($u->value)) {
while (!($u->value[0] & 1)) {
$u->_rshift(1);
if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
$a = $a->add($y);
$b = $b->subtract($x);
}
$a->_rshift(1);
$b->_rshift(1);
}

while (!($v->value[0] & 1)) {
$v->_rshift(1);
if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
$c = $c->add($y);
$d = $d->subtract($x);
}
$c->_rshift(1);
$d->_rshift(1);
}

if ($u->compare($v) >= 0) {
$u = $u->subtract($v);
$a = $a->subtract($c);
$b = $b->subtract($d);
} else {
$v = $v->subtract($u);
$c = $c->subtract($a);
$d = $d->subtract($b);
}
}

return array(
'gcd' => $this->_normalize($g->multiply($v)),
'x' => $this->_normalize($c),
'y' => $this->_normalize($d)
);
}






















function gcd($n)
{
extract($this->extendedGCD($n));
return $gcd;
}







function abs()
{
$temp = new static();

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp->value = gmp_abs($this->value);
break;
case self::MODE_BCMATH:
$temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
break;
default:
$temp->value = $this->value;
}

return $temp;
}



















function compare($y)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$r = gmp_cmp($this->value, $y->value);
if ($r < -1) {
$r = -1;
}
if ($r > 1) {
$r = 1;
}
return $r;
case self::MODE_BCMATH:
return bccomp($this->value, $y->value, 0);
}

return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
}












function _compare($x_value, $x_negative, $y_value, $y_negative)
{
if ($x_negative != $y_negative) {
return (!$x_negative && $y_negative) ? 1 : -1;
}

$result = $x_negative ? -1 : 1;

if (count($x_value) != count($y_value)) {
return (count($x_value) > count($y_value)) ? $result : -$result;
}
$size = max(count($x_value), count($y_value));

$x_value = array_pad($x_value, $size, 0);
$y_value = array_pad($y_value, $size, 0);

for ($i = count($x_value) - 1; $i >= 0; --$i) {
if ($x_value[$i] != $y_value[$i]) {
return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
}
}

return 0;
}











function equals($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_cmp($this->value, $x->value) == 0;
default:
return $this->value === $x->value && $this->is_negative == $x->is_negative;
}
}










function setPrecision($bits)
{
$this->precision = $bits;
if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
$this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
} else {
$this->bitmask = new static(bcpow('2', $bits, 0));
}

$temp = $this->_normalize($this);
$this->value = $temp->value;
}









function bitwise_and($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_and($this->value, $x->value);

return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();

$length = max(strlen($left), strlen($right));

$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

return $this->_normalize(new static($left & $right, 256));
}

$result = $this->copy();

$length = min(count($x->value), count($this->value));

$result->value = array_slice($result->value, 0, $length);

for ($i = 0; $i < $length; ++$i) {
$result->value[$i]&= $x->value[$i];
}

return $this->_normalize($result);
}









function bitwise_or($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_or($this->value, $x->value);

return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();

$length = max(strlen($left), strlen($right));

$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

return $this->_normalize(new static($left | $right, 256));
}

$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0);

for ($i = 0; $i < $length; ++$i) {
$result->value[$i]|= $x->value[$i];
}

return $this->_normalize($result);
}









function bitwise_xor($x)
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = new static();
$temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value));
return $this->_normalize($temp);
case self::MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();

$length = max(strlen($left), strlen($right));

$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

return $this->_normalize(new static($left ^ $right, 256));
}

$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->is_negative = false;
$result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0);

for ($i = 0; $i < $length; ++$i) {
$result->value[$i]^= $x->value[$i];
}

return $this->_normalize($result);
}








function bitwise_not()
{


$temp = $this->toBytes();
if ($temp == '') {
return $this->_normalize(new static());
}
$pre_msb = decbin(ord($temp[0]));
$temp = ~$temp;
$msb = decbin(ord($temp[0]));
if (strlen($msb) == 8) {
$msb = substr($msb, strpos($msb, '0'));
}
$temp[0] = chr(bindec($msb));


$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
$new_bits = $this->precision - $current_bits;
if ($new_bits <= 0) {
return $this->_normalize(new static($temp, 256));
}


$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
$this->_base256_lshift($leading_ones, $current_bits);

$temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);

return $this->_normalize(new static($leading_ones | $temp, 256));
}











function bitwise_rightShift($shift)
{
$temp = new static();

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
static $two;

if (!isset($two)) {
$two = gmp_init('2');
}

$temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));

break;
case self::MODE_BCMATH:
$temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);

break;
default: 

$temp->value = $this->value;
$temp->_rshift($shift);
}

return $this->_normalize($temp);
}











function bitwise_leftShift($shift)
{
$temp = new static();

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
static $two;

if (!isset($two)) {
$two = gmp_init('2');
}

$temp->value = gmp_mul($this->value, gmp_pow($two, $shift));

break;
case self::MODE_BCMATH:
$temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);

break;
default: 

$temp->value = $this->value;
$temp->_lshift($shift);
}

return $this->_normalize($temp);
}










function bitwise_leftRotate($shift)
{
$bits = $this->toBytes();

if ($this->precision > 0) {
$precision = $this->precision;
if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$mask = $this->bitmask->subtract(new static(1));
$mask = $mask->toBytes();
} else {
$mask = $this->bitmask->toBytes();
}
} else {
$temp = ord($bits[0]);
for ($i = 0; $temp >> $i; ++$i) {
}
$precision = 8 * strlen($bits) - 8 + $i;
$mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
}

if ($shift < 0) {
$shift+= $precision;
}
$shift%= $precision;

if (!$shift) {
return $this->copy();
}

$left = $this->bitwise_leftShift($shift);
$left = $left->bitwise_and(new static($mask, 256));
$right = $this->bitwise_rightShift($precision - $shift);
$result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
return $this->_normalize($result);
}










function bitwise_rightRotate($shift)
{
return $this->bitwise_leftRotate(-$shift);
}










function _random_number_helper($size)
{
if (class_exists('\phpseclib\Crypt\Random')) {
$random = Random::string($size);
} else {
$random = '';

if ($size & 1) {
$random.= chr(mt_rand(0, 255));
}

$blocks = $size >> 1;
for ($i = 0; $i < $blocks; ++$i) {

$random.= pack('n', mt_rand(0, 0xFFFF));
}
}

return new static($random, 256);
}

















function random($arg1, $arg2 = false)
{
if ($arg1 === false) {
return false;
}

if ($arg2 === false) {
$max = $arg1;
$min = $this;
} else {
$min = $arg1;
$max = $arg2;
}

$compare = $max->compare($min);

if (!$compare) {
return $this->_normalize($min);
} elseif ($compare < 0) {

$temp = $max;
$max = $min;
$min = $temp;
}

static $one;
if (!isset($one)) {
$one = new static(1);
}

$max = $max->subtract($min->subtract($one));
$size = strlen(ltrim($max->toBytes(), chr(0)));
















$random_max = new static(chr(1) . str_repeat("\0", $size), 256);
$random = $this->_random_number_helper($size);

list($max_multiple) = $random_max->divide($max);
$max_multiple = $max_multiple->multiply($max);

while ($random->compare($max_multiple) >= 0) {
$random = $random->subtract($max_multiple);
$random_max = $random_max->subtract($max_multiple);
$random = $random->bitwise_leftShift(8);
$random = $random->add($this->_random_number_helper(1));
$random_max = $random_max->bitwise_leftShift(8);
list($max_multiple) = $random_max->divide($max);
$max_multiple = $max_multiple->multiply($max);
}
list(, $random) = $random->divide($max);

return $this->_normalize($random->add($min));
}














function randomPrime($arg1, $arg2 = false, $timeout = false)
{
if ($arg1 === false) {
return false;
}

if ($arg2 === false) {
$max = $arg1;
$min = $this;
} else {
$min = $arg1;
$max = $arg2;
}

$compare = $max->compare($min);

if (!$compare) {
return $min->isPrime() ? $min : false;
} elseif ($compare < 0) {

$temp = $max;
$max = $min;
$min = $temp;
}

$length = $max->getLength();
if ($length > 8196) {
user_error('Generation of random prime numbers larger than 8196 has been disabled');
}

static $one, $two;
if (!isset($one)) {
$one = new static(1);
$two = new static(2);
}

$start = time();

$x = $this->random($min, $max);


if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) {
$p = new static();
$p->value = gmp_nextprime($x->value);

if ($p->compare($max) <= 0) {
return $p;
}

if (!$min->equals($x)) {
$x = $x->subtract($one);
}

return $x->randomPrime($min, $x);
}

if ($x->equals($two)) {
return $x;
}

$x->_make_odd();
if ($x->compare($max) > 0) {

if ($min->equals($max)) {
return false;
}
$x = $min->copy();
$x->_make_odd();
}

$initial_x = $x->copy();

while (true) {
if ($timeout !== false && time() - $start > $timeout) {
return false;
}

if ($x->isPrime()) {
return $x;
}

$x = $x->add($two);

if ($x->compare($max) > 0) {
$x = $min->copy();
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
}

if ($x->equals($initial_x)) {
return false;
}
}
}









function _make_odd()
{
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
gmp_setbit($this->value, 0);
break;
case self::MODE_BCMATH:
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
$this->value = bcadd($this->value, '1');
}
break;
default:
$this->value[0] |= 1;
}
}















function isPrime($t = false)
{
$length = $this->getLength();




if ($length > 8196) {
user_error('Primality testing is not supported for numbers larger than 8196 bits');
}

if (!$t) {


if ($length >= 163) { $t = 2; } 
else if ($length >= 106) { $t = 3; } 
else if ($length >= 81 ) { $t = 4; } 
else if ($length >= 68 ) { $t = 5; } 
else if ($length >= 56 ) { $t = 6; } 
else if ($length >= 50 ) { $t = 7; } 
else if ($length >= 43 ) { $t = 8; } 
else if ($length >= 37 ) { $t = 9; } 
else if ($length >= 31 ) { $t = 12; } 
else if ($length >= 25 ) { $t = 15; } 
else if ($length >= 18 ) { $t = 18; } 
else { $t = 27; }

}



switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
return gmp_prob_prime($this->value, $t) != 0;
case self::MODE_BCMATH:
if ($this->value === '2') {
return true;
}
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
return false;
}
break;
default:
if ($this->value == array(2)) {
return true;
}
if (~$this->value[0] & 1) {
return false;
}
}

static $primes, $zero, $one, $two;

if (!isset($primes)) {
$primes = array(
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
953, 967, 971, 977, 983, 991, 997
);

if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
for ($i = 0; $i < count($primes); ++$i) {
$primes[$i] = new static($primes[$i]);
}
}

$zero = new static();
$one = new static(1);
$two = new static(2);
}

if ($this->equals($one)) {
return false;
}


if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
foreach ($primes as $prime) {
list(, $r) = $this->divide($prime);
if ($r->equals($zero)) {
return $this->equals($prime);
}
}
} else {
$value = $this->value;
foreach ($primes as $prime) {
list(, $r) = $this->_divide_digit($value, $prime);
if (!$r) {
return count($value) == 1 && $value[0] == $prime;
}
}
}

$n = $this->copy();
$n_1 = $n->subtract($one);
$n_2 = $n->subtract($two);

$r = $n_1->copy();
$r_value = $r->value;

if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
$s = 0;

while ($r->value[strlen($r->value) - 1] % 2 == 0) {
$r->value = bcdiv($r->value, '2', 0);
++$s;
}
} else {
for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
$temp = ~$r_value[$i] & 0xFFFFFF;
for ($j = 1; ($temp >> $j) & 1; ++$j) {
}
if ($j != 25) {
break;
}
}
$s = 26 * $i + $j;
$r->_rshift($s);
}

for ($i = 0; $i < $t; ++$i) {
$a = $this->random($two, $n_2);
$y = $a->modPow($r, $n);

if (!$y->equals($one) && !$y->equals($n_1)) {
for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
$y = $y->modPow($two, $n);
if ($y->equals($one)) {
return false;
}
}

if (!$y->equals($n_1)) {
return false;
}
}
}
return true;
}









function _lshift($shift)
{
if ($shift == 0) {
return;
}

$num_digits = (int) ($shift / self::$base);
$shift %= self::$base;
$shift = 1 << $shift;

$carry = 0;

for ($i = 0; $i < count($this->value); ++$i) {
$temp = $this->value[$i] * $shift + $carry;
$carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
$this->value[$i] = (int) ($temp - $carry * self::$baseFull);
}

if ($carry) {
$this->value[count($this->value)] = $carry;
}

while ($num_digits--) {
array_unshift($this->value, 0);
}
}









function _rshift($shift)
{
if ($shift == 0) {
return;
}

$num_digits = (int) ($shift / self::$base);
$shift %= self::$base;
$carry_shift = self::$base - $shift;
$carry_mask = (1 << $shift) - 1;

if ($num_digits) {
$this->value = array_slice($this->value, $num_digits);
}

$carry = 0;

for ($i = count($this->value) - 1; $i >= 0; --$i) {
$temp = $this->value[$i] >> $shift | $carry;
$carry = ($this->value[$i] & $carry_mask) << $carry_shift;
$this->value[$i] = $temp;
}

$this->value = $this->_trim($this->value);
}











function _normalize($result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;

switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
if ($this->bitmask !== false) {
$flip = gmp_cmp($result->value, gmp_init(0)) < 0;
if ($flip) {
$result->value = gmp_neg($result->value);
}
$result->value = gmp_and($result->value, $result->bitmask->value);
if ($flip) {
$result->value = gmp_neg($result->value);
}
}

return $result;
case self::MODE_BCMATH:
if (!empty($result->bitmask->value)) {
$result->value = bcmod($result->value, $result->bitmask->value);
}

return $result;
}

$value = &$result->value;

if (!count($value)) {
$result->is_negative = false;
return $result;
}

$value = $this->_trim($value);

if (!empty($result->bitmask->value)) {
$length = min(count($value), count($this->bitmask->value));
$value = array_slice($value, 0, $length);

for ($i = 0; $i < $length; ++$i) {
$value[$i] = $value[$i] & $this->bitmask->value[$i];
}
}

return $result;
}










function _trim($value)
{
for ($i = count($value) - 1; $i >= 0; --$i) {
if ($value[$i]) {
break;
}
unset($value[$i]);
}

return $value;
}









function _array_repeat($input, $multiplier)
{
return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
}











function _base256_lshift(&$x, $shift)
{
if ($shift == 0) {
return;
}

$num_bytes = $shift >> 3; 
$shift &= 7; 

$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
}











function _base256_rshift(&$x, $shift)
{
if ($shift == 0) {
$x = ltrim($x, chr(0));
return '';
}

$num_bytes = $shift >> 3; 
$shift &= 7; 

$remainder = '';
if ($num_bytes) {
$start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
$remainder = substr($x, $start);
$x = substr($x, 0, -$num_bytes);
}

$carry = 0;
$carry_shift = 8 - $shift;
for ($i = 0; $i < strlen($x); ++$i) {
$temp = (ord($x[$i]) >> $shift) | $carry;
$carry = (ord($x[$i]) << $carry_shift) & 0xFF;
$x[$i] = chr($temp);
}
$x = ltrim($x, chr(0));

$remainder = chr($carry >> $carry_shift) . $remainder;

return ltrim($remainder, chr(0));
}











function _int2bytes($x)
{
return ltrim(pack('N', $x), chr(0));
}








function _bytes2int($x)
{
$temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
return $temp['int'];
}











function _encodeASN1Length($length)
{
if ($length <= 0x7F) {
return chr($length);
}

$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}














function _safe_divide($x, $y)
{
if (self::$base === 26) {
return (int) ($x / $y);
}


return ($x - ($x % $y)) / $y;
}
}
<?php
















namespace MongoDB;

use MongoDB\Driver\WriteResult;
use MongoDB\Exception\BadMethodCallException;




class InsertManyResult
{
private WriteResult $writeResult;

private array $insertedIds;

private bool $isAcknowledged;

public function __construct(WriteResult $writeResult, array $insertedIds)
{
$this->writeResult = $writeResult;
$this->insertedIds = $insertedIds;
$this->isAcknowledged = $writeResult->isAcknowledged();
}










public function getInsertedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getInsertedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}












public function getInsertedIds()
{
return $this->insertedIds;
}









public function isAcknowledged()
{
return $this->writeResult->isAcknowledged();
}
}
<?php
















namespace MongoDB;

use Iterator;
use MongoDB\Driver\ClientEncryption;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\CreateEncryptedCollectionException;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\GridFS\Bucket;
use MongoDB\Model\BSONArray;
use MongoDB\Model\BSONDocument;
use MongoDB\Model\CollectionInfoIterator;
use MongoDB\Operation\Aggregate;
use MongoDB\Operation\CreateCollection;
use MongoDB\Operation\CreateEncryptedCollection;
use MongoDB\Operation\DatabaseCommand;
use MongoDB\Operation\DropCollection;
use MongoDB\Operation\DropDatabase;
use MongoDB\Operation\DropEncryptedCollection;
use MongoDB\Operation\ListCollectionNames;
use MongoDB\Operation\ListCollections;
use MongoDB\Operation\ModifyCollection;
use MongoDB\Operation\RenameCollection;
use MongoDB\Operation\Watch;
use Throwable;
use Traversable;

use function is_array;
use function strlen;

class Database
{
private const DEFAULT_TYPE_MAP = [
'array' => BSONArray::class,
'document' => BSONDocument::class,
'root' => BSONDocument::class,
];

private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8;

private string $databaseName;

private Manager $manager;

private ReadConcern $readConcern;

private ReadPreference $readPreference;

private array $typeMap;

private WriteConcern $writeConcern;




























public function __construct(Manager $manager, string $databaseName, array $options = [])
{
if (strlen($databaseName) < 1) {
throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName);
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

$this->manager = $manager;
$this->databaseName = $databaseName;
$this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern();
$this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference();
$this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP;
$this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern();
}







public function __debugInfo()
{
return [
'databaseName' => $this->databaseName,
'manager' => $this->manager,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}













public function __get(string $collectionName)
{
return $this->selectCollection($collectionName);
}






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















public function aggregate(array $pipeline, array $options = [])
{
$hasWriteStage = is_last_pipeline_operator_write($pipeline);

if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
$options['readPreference'] = $this->readPreference;
}

$server = $hasWriteStage
? select_server_for_aggregate_write_stage($this->manager, $options)
: select_server($this->manager, $options);






if (
! isset($options['readConcern']) &&
! is_in_transaction($options) &&
( ! $hasWriteStage || server_supports_feature($server, self::WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE))
) {
$options['readConcern'] = $this->readConcern;
}

if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

if ($hasWriteStage && ! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

$operation = new Aggregate($this->databaseName, null, $pipeline, $options);

return $operation->execute($server);
}











public function command($command, array $options = [])
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$operation = new DatabaseCommand($this->databaseName, $command, $options);
$server = select_server($this->manager, $options);

return $operation->execute($server);
}
















public function createCollection(string $collectionName, array $options = [])
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

if (! isset($options['encryptedFields'])) {
$options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager);
}

$operation = isset($options['encryptedFields'])
? new CreateEncryptedCollection($this->databaseName, $collectionName, $options)
: new CreateCollection($this->databaseName, $collectionName, $options);

$server = select_server_for_write($this->manager, $options);

return $operation->execute($server);
}





















public function createEncryptedCollection(string $collectionName, ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, array $options): array
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

$operation = new CreateEncryptedCollection($this->databaseName, $collectionName, $options);
$server = select_server_for_write($this->manager, $options);

try {
$operation->createDataKeys($clientEncryption, $kmsProvider, $masterKey, $encryptedFields);
$result = $operation->execute($server);

return [$result, $encryptedFields];
} catch (Throwable $e) {
throw new CreateEncryptedCollectionException($e, $encryptedFields ?? []);
}
}











public function drop(array $options = [])
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$server = select_server_for_write($this->manager, $options);

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

$operation = new DropDatabase($this->databaseName, $options);

return $operation->execute($server);
}












public function dropCollection(string $collectionName, array $options = [])
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$server = select_server_for_write($this->manager, $options);

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

if (! isset($options['encryptedFields'])) {
$options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager)
?? get_encrypted_fields_from_server($this->databaseName, $collectionName, $this->manager, $server);
}

$operation = isset($options['encryptedFields'])
? new DropEncryptedCollection($this->databaseName, $collectionName, $options)
: new DropCollection($this->databaseName, $collectionName, $options);

return $operation->execute($server);
}






public function getDatabaseName()
{
return $this->databaseName;
}






public function getManager()
{
return $this->manager;
}







public function getReadConcern()
{
return $this->readConcern;
}






public function getReadPreference()
{
return $this->readPreference;
}






public function getTypeMap()
{
return $this->typeMap;
}







public function getWriteConcern()
{
return $this->writeConcern;
}








public function listCollectionNames(array $options = []): Iterator
{
$operation = new ListCollectionNames($this->databaseName, $options);
$server = select_server($this->manager, $options);

return $operation->execute($server);
}









public function listCollections(array $options = [])
{
$operation = new ListCollections($this->databaseName, $options);
$server = select_server($this->manager, $options);

return $operation->execute($server);
}












public function modifyCollection(string $collectionName, array $collectionOptions, array $options = [])
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$server = select_server_for_write($this->manager, $options);

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

$operation = new ModifyCollection($this->databaseName, $collectionName, $collectionOptions, $options);

return $operation->execute($server);
}














public function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = [])
{
if (! isset($toDatabaseName)) {
$toDatabaseName = $this->databaseName;
}

if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$server = select_server_for_write($this->manager, $options);

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

$operation = new RenameCollection($this->databaseName, $fromCollectionName, $toDatabaseName, $toCollectionName, $options);

return $operation->execute($server);
}










public function selectCollection(string $collectionName, array $options = [])
{
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];

return new Collection($this->manager, $this->databaseName, $collectionName, $options);
}









public function selectGridFSBucket(array $options = [])
{
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];

return new Bucket($this->manager, $this->databaseName, $options);
}










public function watch(array $pipeline = [], array $options = [])
{
if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
$options['readPreference'] = $this->readPreference;
}

$server = select_server($this->manager, $options);

if (! isset($options['readConcern']) && ! is_in_transaction($options)) {
$options['readConcern'] = $this->readConcern;
}

if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$operation = new Watch($this->manager, $this->databaseName, null, $pipeline, $options);

return $operation->execute($server);
}









public function withOptions(array $options = [])
{
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];

return new Database($this->manager, $this->databaseName, $options);
}
}
<?php
















namespace MongoDB;

use Exception;
use MongoDB\BSON\Document;
use MongoDB\BSON\PackedArray;
use MongoDB\BSON\Serializable;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\RuntimeException;
use MongoDB\Operation\ListCollections;
use MongoDB\Operation\WithTransaction;
use Psr\Log\LoggerInterface;
use ReflectionClass;
use ReflectionException;

use function array_is_list;
use function array_key_first;
use function assert;
use function end;
use function get_object_vars;
use function is_array;
use function is_object;
use function is_string;
use function MongoDB\BSON\fromPHP;
use function MongoDB\BSON\toPHP;
use function substr;







function add_logger(LoggerInterface $logger): void
{
PsrLogAdapter::addLogger($logger);
}







function remove_logger(LoggerInterface $logger): void
{
PsrLogAdapter::removeLogger($logger);
}







function all_servers_support_write_stage_on_secondary(array $servers): bool
{


static $wireVersionForWriteStageOnSecondary = 13;

foreach ($servers as $server) {

if ($server->getType() === Server::TYPE_LOAD_BALANCER) {
continue;
}

if (! server_supports_feature($server, $wireVersionForWriteStageOnSecondary)) {
return false;
}
}

return true;
}














function apply_type_map_to_document($document, array $typeMap)
{
if (! is_document($document)) {
throw InvalidArgumentException::expectedDocumentType('$document', $document);
}

return toPHP(fromPHP($document), $typeMap);
}















function document_to_array($document): array
{
if ($document instanceof Document || $document instanceof PackedArray) {





return $document->toPHP([
'array' => 'bson',
'document' => 'bson',
'root' => 'array',
]);
} elseif ($document instanceof Serializable) {
$document = $document->bsonSerialize();
}

if (is_object($document)) {



$document = get_object_vars($document);
}

if (! is_array($document)) {
throw InvalidArgumentException::expectedDocumentType('$document', $document);
}

return $document;
}












function get_encrypted_fields_from_driver(string $databaseName, string $collectionName, Manager $manager)
{
$encryptedFieldsMap = (array) $manager->getEncryptedFieldsMap();

return $encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null;
}










function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Manager $manager, Server $server)
{

if ($manager->getEncryptedFieldsMap() === null) {
return null;
}

$collectionInfoIterator = (new ListCollections($databaseName, ['filter' => ['name' => $collectionName]]))->execute($server);

foreach ($collectionInfoIterator as $collectionInfo) {




return $collectionInfo['options']['encryptedFields'] ?? null;
}

return null;
}










function is_document($document): bool
{
return is_array($document) || (is_object($document) && ! $document instanceof PackedArray);
}













function is_first_key_operator($document): bool
{
if ($document instanceof PackedArray) {
return false;
}

$document = document_to_array($document);

$firstKey = array_key_first($document);

if (! is_string($firstKey)) {
return false;
}

return '$' === ($firstKey[0] ?? null);
}



























function is_pipeline($pipeline, bool $allowEmpty = false): bool
{
if ($pipeline instanceof PackedArray) {




$pipeline = $pipeline->toPHP([
'array' => 'bson',
'document' => 'bson',
'root' => 'array',
]);
} elseif ($pipeline instanceof Serializable) {
$pipeline = $pipeline->bsonSerialize();
}

if (! is_array($pipeline)) {
return false;
}

if ($pipeline === []) {
return $allowEmpty;
}

if (! array_is_list($pipeline)) {
return false;
}

foreach ($pipeline as $stage) {
if (! is_document($stage)) {
return false;
}

if (! is_first_key_operator($stage)) {
return false;
}
}

return true;
}







function is_in_transaction(array $options): bool
{
if (isset($options['session']) && $options['session'] instanceof Session && $options['session']->isInTransaction()) {
return true;
}

return false;
}










function is_last_pipeline_operator_write(array $pipeline): bool
{
$lastOp = end($pipeline);

if ($lastOp === false) {
return false;
}

if (! is_array($lastOp) && ! is_object($lastOp)) {
return false;
}

$key = array_key_first(document_to_array($lastOp));

return $key === '$merge' || $key === '$out';
}










function is_mapreduce_output_inline($out): bool
{
if (! is_array($out) && ! is_object($out)) {
return false;
}

return array_key_first(document_to_array($out)) === 'inline';
}










function is_write_concern_acknowledged(WriteConcern $writeConcern): bool
{



return ($writeConcern->getW() !== 0 && $writeConcern->getW() !== -1) || $writeConcern->getJournal() === true;
}








function server_supports_feature(Server $server, int $feature): bool
{
$info = $server->getInfo();
$maxWireVersion = isset($info['maxWireVersion']) ? (integer) $info['maxWireVersion'] : 0;
$minWireVersion = isset($info['minWireVersion']) ? (integer) $info['minWireVersion'] : 0;

return $minWireVersion <= $feature && $maxWireVersion >= $feature;
}







function is_string_array($input): bool
{
if (! is_array($input)) {
return false;
}

foreach ($input as $item) {
if (! is_string($item)) {
return false;
}
}

return true;
}












function recursive_copy($element)
{
if (is_array($element)) {
foreach ($element as $key => $value) {
$element[$key] = recursive_copy($value);
}

return $element;
}

if (! is_object($element)) {
return $element;
}

if (! (new ReflectionClass($element))->isCloneable()) {
return $element;
}

return clone $element;
}














function create_field_path_type_map(array $typeMap, string $fieldPath): array
{

if (isset($typeMap['fieldPaths']) && is_array($typeMap['fieldPaths'])) {
$fieldPaths = $typeMap['fieldPaths'];

$typeMap['fieldPaths'] = [];
foreach ($fieldPaths as $existingFieldPath => $type) {
$typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath] = $type;
}
}


if (isset($typeMap['root'])) {
$typeMap['fieldPaths'][$fieldPath] = $typeMap['root'];
}




if (substr($fieldPath, -2, 2) === '.$') {
$typeMap['fieldPaths'][substr($fieldPath, 0, -2)] = 'array';
}

$typeMap['root'] = 'object';

return $typeMap;
}


























function with_transaction(Session $session, callable $callback, array $transactionOptions = []): void
{
$operation = new WithTransaction($callback, $transactionOptions);
$operation->execute($session);
}






function extract_session_from_options(array $options): ?Session
{
if (isset($options['session']) && $options['session'] instanceof Session) {
return $options['session'];
}

return null;
}






function extract_read_preference_from_options(array $options): ?ReadPreference
{
if (isset($options['readPreference']) && $options['readPreference'] instanceof ReadPreference) {
return $options['readPreference'];
}

return null;
}










function select_server(Manager $manager, array $options): Server
{
$session = extract_session_from_options($options);
$server = $session instanceof Session ? $session->getServer() : null;


if ($server !== null) {
return $server;
}


$readPreference = extract_read_preference_from_options($options);


if ($readPreference === null && $session instanceof Session && $session->isInTransaction()) {


$readPreference = extract_read_preference_from_options($session->getTransactionOptions() ?? []);
}


return $manager->selectServer($readPreference);
}









function select_server_for_aggregate_write_stage(Manager $manager, array &$options): Server
{
$readPreference = extract_read_preference_from_options($options);







if ($readPreference === null || $readPreference->getModeString() === ReadPreference::PRIMARY) {
return select_server($manager, $options);
}

$server = null;
$serverSelectionError = null;

try {
$server = select_server($manager, $options);
} catch (DriverRuntimeException $serverSelectionError) {
}




if (! all_servers_support_write_stage_on_secondary($manager->getServers())) {
$options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY);

if ($server === null || $server->isSecondary()) {
return select_server($manager, $options);
}
}



if ($serverSelectionError !== null) {
throw $serverSelectionError;
}

assert($server instanceof Server);

return $server;
}











function select_server_for_write(Manager $manager, array $options): Server
{
return select_server($manager, $options + ['readPreference' => new ReadPreference(ReadPreference::PRIMARY)]);
}
<?php
















namespace MongoDB\GridFS;

use MongoDB\BSON\UTCDateTime;

use function assert;
use function explode;
use function in_array;
use function is_integer;
use function is_resource;
use function stream_context_get_options;
use function stream_get_wrappers;
use function stream_wrapper_register;
use function stream_wrapper_unregister;

use const SEEK_CUR;
use const SEEK_END;
use const SEEK_SET;
use const STREAM_IS_URL;








class StreamWrapper
{

public $context;

private ?string $protocol = null;


private $stream;

public function __destruct()
{



$this->stream = null;
}




public function getFile(): object
{
assert($this->stream !== null);

return $this->stream->getFile();
}






public static function register(string $protocol = 'gridfs'): void
{
if (in_array($protocol, stream_get_wrappers())) {
stream_wrapper_unregister($protocol);
}

stream_wrapper_register($protocol, static::class, STREAM_IS_URL);
}






public function stream_close(): void
{
if (! $this->stream) {
return;
}

$this->stream->close();
}






public function stream_eof(): bool
{
if (! $this->stream instanceof ReadableStream) {
return false;
}

return $this->stream->isEOF();
}










public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool
{
$this->initProtocol($path);

if ($mode === 'r') {
return $this->initReadableStream();
}

if ($mode === 'w') {
return $this->initWritableStream();
}

return false;
}










public function stream_read(int $length): string
{
if (! $this->stream instanceof ReadableStream) {
return '';
}

return $this->stream->readBytes($length);
}









public function stream_seek(int $offset, int $whence = SEEK_SET): bool
{
assert($this->stream !== null);

$size = $this->stream->getSize();

if ($whence === SEEK_CUR) {
$offset += $this->stream->tell();
}

if ($whence === SEEK_END) {
$offset += $size;
}


if ($this->stream instanceof WritableStream) {
return $offset === $size;
}

if ($offset < 0 || $offset > $size) {
return false;
}

$this->stream->seek($offset);

return true;
}






public function stream_stat(): array
{
assert($this->stream !== null);

$stat = $this->getStatTemplate();

$stat[2] = $stat['mode'] = $this->stream instanceof ReadableStream
? 0100444 
: 0100222; 
$stat[7] = $stat['size'] = $this->stream->getSize();

$file = $this->stream->getFile();

if (isset($file->uploadDate) && $file->uploadDate instanceof UTCDateTime) {
$timestamp = $file->uploadDate->toDateTime()->getTimestamp();
$stat[9] = $stat['mtime'] = $timestamp;
$stat[10] = $stat['ctime'] = $timestamp;
}

if (isset($file->chunkSize) && is_integer($file->chunkSize)) {
$stat[11] = $stat['blksize'] = $file->chunkSize;
}

return $stat;
}







public function stream_tell(): int
{
assert($this->stream !== null);

return $this->stream->tell();
}








public function stream_write(string $data): int
{
if (! $this->stream instanceof WritableStream) {
return 0;
}

return $this->stream->writeBytes($data);
}




private function getStatTemplate(): array
{
return [

0 => 0, 'dev' => 0,
1 => 0, 'ino' => 0,
2 => 0, 'mode' => 0,
3 => 0, 'nlink' => 0,
4 => 0, 'uid' => 0,
5 => 0, 'gid' => 0,
6 => -1, 'rdev' => -1,
7 => 0, 'size' => 0,
8 => 0, 'atime' => 0,
9 => 0, 'mtime' => 0,
10 => 0, 'ctime' => 0,
11 => -1, 'blksize' => -1,
12 => -1, 'blocks' => -1,

];
}






private function initProtocol(string $path): void
{
$parts = explode('://', $path, 2);
$this->protocol = $parts[0] ?: 'gridfs';
}






private function initReadableStream(): bool
{
assert(is_resource($this->context));
$context = stream_context_get_options($this->context);

assert($this->protocol !== null);
$this->stream = new ReadableStream(
$context[$this->protocol]['collectionWrapper'],
$context[$this->protocol]['file'],
);

return true;
}






private function initWritableStream(): bool
{
assert(is_resource($this->context));
$context = stream_context_get_options($this->context);

assert($this->protocol !== null);
$this->stream = new WritableStream(
$context[$this->protocol]['collectionWrapper'],
$context[$this->protocol]['filename'],
$context[$this->protocol]['options'],
);

return true;
}
}
<?php
















namespace MongoDB\GridFS;

use Iterator;
use MongoDB\BSON\Binary;
use MongoDB\Driver\CursorInterface;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\GridFS\Exception\CorruptFileException;

use function assert;
use function ceil;
use function floor;
use function is_integer;
use function is_object;
use function property_exists;
use function sprintf;
use function strlen;
use function substr;






class ReadableStream
{
private ?string $buffer = null;

private int $bufferOffset = 0;

private int $chunkSize;

private int $chunkOffset = 0;


private ?Iterator $chunksIterator = null;

private CollectionWrapper $collectionWrapper;

private int $expectedLastChunkSize = 0;

private object $file;

private int $length;

private int $numChunks = 0;








public function __construct(CollectionWrapper $collectionWrapper, object $file)
{
if (! isset($file->chunkSize) || ! is_integer($file->chunkSize) || $file->chunkSize < 1) {
throw new CorruptFileException('file.chunkSize is not an integer >= 1');
}

if (! isset($file->length) || ! is_integer($file->length) || $file->length < 0) {
throw new CorruptFileException('file.length is not an integer > 0');
}

if (! isset($file->_id) && ! property_exists($file, '_id')) {
throw new CorruptFileException('file._id does not exist');
}

$this->file = $file;
$this->chunkSize = $file->chunkSize;
$this->length = $file->length;

$this->collectionWrapper = $collectionWrapper;

if ($this->length > 0) {
$this->numChunks = (integer) ceil($this->length / $this->chunkSize);
$this->expectedLastChunkSize = $this->length - (($this->numChunks - 1) * $this->chunkSize);
}
}







public function __debugInfo(): array
{
return [
'bucketName' => $this->collectionWrapper->getBucketName(),
'databaseName' => $this->collectionWrapper->getDatabaseName(),
'file' => $this->file,
];
}

public function close(): void
{

}

public function getFile(): object
{
return $this->file;
}

public function getSize(): int
{
return $this->length;
}




public function isEOF(): bool
{
if ($this->chunkOffset === $this->numChunks - 1) {
return $this->bufferOffset >= $this->expectedLastChunkSize;
}

return $this->chunkOffset >= $this->numChunks;
}










public function readBytes(int $length): string
{
if ($length < 0) {
throw new InvalidArgumentException(sprintf('$length must be >= 0; given: %d', $length));
}

if ($this->chunksIterator === null) {
$this->initChunksIterator();
}

if ($this->buffer === null && ! $this->initBufferFromCurrentChunk()) {
return '';
}

assert($this->buffer !== null);

$data = '';

while (strlen($data) < $length) {
if ($this->bufferOffset >= strlen($this->buffer) && ! $this->initBufferFromNextChunk()) {
break;
}

$initialDataLength = strlen($data);
$data .= substr($this->buffer, $this->bufferOffset, $length - $initialDataLength);
$this->bufferOffset += strlen($data) - $initialDataLength;
}

return $data;
}






public function seek(int $offset): void
{
if ($offset < 0 || $offset > $this->file->length) {
throw new InvalidArgumentException(sprintf('$offset must be >= 0 and <= %d; given: %d', $this->file->length, $offset));
}





$lastChunkOffset = $this->chunkOffset;
$this->chunkOffset = (integer) floor($offset / $this->chunkSize);
$this->bufferOffset = $offset % $this->chunkSize;

if ($lastChunkOffset === $this->chunkOffset) {
return;
}

if ($this->chunksIterator === null) {
return;
}


$this->buffer = null;




if ($lastChunkOffset > $this->chunkOffset) {
$this->chunksIterator = null;

return;
}





$numChunks = $this->chunkOffset - $lastChunkOffset;
for ($i = 0; $i < $numChunks; $i++) {
$this->chunksIterator->next();
}
}






public function tell(): int
{
return ($this->chunkOffset * $this->chunkSize) + $this->bufferOffset;
}







private function initBufferFromCurrentChunk(): bool
{
if ($this->chunkOffset === 0 && $this->numChunks === 0) {
return false;
}

if ($this->chunksIterator === null) {
return false;
}

if (! $this->chunksIterator->valid()) {
throw CorruptFileException::missingChunk($this->chunkOffset);
}

$currentChunk = $this->chunksIterator->current();
assert(is_object($currentChunk));

if ($currentChunk->n !== $this->chunkOffset) {
throw CorruptFileException::unexpectedIndex($currentChunk->n, $this->chunkOffset);
}

if (! $currentChunk->data instanceof Binary) {
throw CorruptFileException::invalidChunkData($this->chunkOffset);
}

$this->buffer = $currentChunk->data->getData();

$actualChunkSize = strlen($this->buffer);

$expectedChunkSize = $this->chunkOffset === $this->numChunks - 1
? $this->expectedLastChunkSize
: $this->chunkSize;

if ($actualChunkSize !== $expectedChunkSize) {
throw CorruptFileException::unexpectedSize($actualChunkSize, $expectedChunkSize);
}

return true;
}







private function initBufferFromNextChunk(): bool
{
if ($this->chunkOffset === $this->numChunks - 1) {
return false;
}

if ($this->chunksIterator === null) {
return false;
}

$this->bufferOffset = 0;
$this->chunkOffset++;
$this->chunksIterator->next();

return $this->initBufferFromCurrentChunk();
}




private function initChunksIterator(): void
{
$this->chunksIterator = $this->collectionWrapper->findChunksByFileId($this->file->_id, $this->chunkOffset);
$this->chunksIterator->rewind();
}
}
<?php
















namespace MongoDB\GridFS;

use ArrayIterator;
use Iterator;
use MongoDB\Collection;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadPreference;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\UpdateResult;
use MultipleIterator;

use function abs;
use function assert;
use function count;
use function is_numeric;
use function is_object;
use function sprintf;






class CollectionWrapper
{
private string $bucketName;

private Collection $chunksCollection;

private string $databaseName;

private bool $checkedIndexes = false;

private Collection $filesCollection;











public function __construct(Manager $manager, string $databaseName, string $bucketName, array $collectionOptions = [])
{
$this->databaseName = $databaseName;
$this->bucketName = $bucketName;

$this->filesCollection = new Collection($manager, $databaseName, sprintf('%s.files', $bucketName), $collectionOptions);
$this->chunksCollection = new Collection($manager, $databaseName, sprintf('%s.chunks', $bucketName), $collectionOptions);
}






public function deleteChunksByFilesId($id): void
{
$this->chunksCollection->deleteMany(['files_id' => $id]);
}






public function deleteFileAndChunksById($id): void
{
$this->filesCollection->deleteOne(['_id' => $id]);
$this->chunksCollection->deleteMany(['files_id' => $id]);
}




public function dropCollections(): void
{
$this->filesCollection->drop(['typeMap' => []]);
$this->chunksCollection->drop(['typeMap' => []]);
}








public function findChunksByFileId($id, int $fromChunk = 0)
{
return $this->chunksCollection->find(
[
'files_id' => $id,
'n' => ['$gte' => $fromChunk],
],
[
'sort' => ['n' => 1],
'typeMap' => ['root' => 'stdClass'],
],
);
}
















public function findFileByFilenameAndRevision(string $filename, int $revision): ?object
{
$filename = $filename;
$revision = $revision;

if ($revision < 0) {
$skip = abs($revision) - 1;
$sortOrder = -1;
} else {
$skip = $revision;
$sortOrder = 1;
}

$file = $this->filesCollection->findOne(
['filename' => $filename],
[
'skip' => $skip,
'sort' => ['uploadDate' => $sortOrder],
'typeMap' => ['root' => 'stdClass'],
],
);
assert(is_object($file) || $file === null);

return $file;
}






public function findFileById($id): ?object
{
$file = $this->filesCollection->findOne(
['_id' => $id],
['typeMap' => ['root' => 'stdClass']],
);
assert(is_object($file) || $file === null);

return $file;
}









public function findFiles($filter, array $options = [])
{
return $this->filesCollection->find($filter, $options);
}








public function findOneFile($filter, array $options = [])
{
return $this->filesCollection->findOne($filter, $options);
}

public function getBucketName(): string
{
return $this->bucketName;
}

public function getChunksCollection(): Collection
{
return $this->chunksCollection;
}

public function getDatabaseName(): string
{
return $this->databaseName;
}

public function getFilesCollection(): Collection
{
return $this->filesCollection;
}






public function insertChunk($chunk): void
{
if (! $this->checkedIndexes) {
$this->ensureIndexes();
}

$this->chunksCollection->insertOne($chunk);
}








public function insertFile($file): void
{
if (! $this->checkedIndexes) {
$this->ensureIndexes();
}

$this->filesCollection->insertOne($file);
}






public function updateFilenameForId($id, string $filename): UpdateResult
{
return $this->filesCollection->updateOne(
['_id' => $id],
['$set' => ['filename' => $filename]],
);
}




private function ensureChunksIndex(): void
{
$expectedIndex = ['files_id' => 1, 'n' => 1];

foreach ($this->chunksCollection->listIndexes() as $index) {
if ($index->isUnique() && $this->indexKeysMatch($expectedIndex, $index->getKey())) {
return;
}
}

$this->chunksCollection->createIndex($expectedIndex, ['unique' => true]);
}




private function ensureFilesIndex(): void
{
$expectedIndex = ['filename' => 1, 'uploadDate' => 1];

foreach ($this->filesCollection->listIndexes() as $index) {
if ($this->indexKeysMatch($expectedIndex, $index->getKey())) {
return;
}
}

$this->filesCollection->createIndex($expectedIndex);
}







private function ensureIndexes(): void
{
if ($this->checkedIndexes) {
return;
}

$this->checkedIndexes = true;

if (! $this->isFilesCollectionEmpty()) {
return;
}

$this->ensureFilesIndex();
$this->ensureChunksIndex();
}

private function indexKeysMatch(array $expectedKeys, array $actualKeys): bool
{
if (count($expectedKeys) !== count($actualKeys)) {
return false;
}

$iterator = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
$iterator->attachIterator(new ArrayIterator($expectedKeys));
$iterator->attachIterator(new ArrayIterator($actualKeys));

foreach ($iterator as $key => $value) {
[$expectedKey, $actualKey] = $key;
[$expectedValue, $actualValue] = $value;

if ($expectedKey !== $actualKey) {
return false;
}





if (! is_numeric($actualValue) || (int) $expectedValue !== (int) $actualValue) {
return false;
}
}

return true;
}




private function isFilesCollectionEmpty(): bool
{
return null === $this->filesCollection->findOne([], [
'readPreference' => new ReadPreference(ReadPreference::PRIMARY),
'projection' => ['_id' => 1],
'typeMap' => [],
]);
}
}
<?php
















namespace MongoDB\GridFS;

use Iterator;
use MongoDB\BSON\Document;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Collection;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\GridFS\Exception\CorruptFileException;
use MongoDB\GridFS\Exception\FileNotFoundException;
use MongoDB\GridFS\Exception\StreamException;
use MongoDB\Model\BSONArray;
use MongoDB\Model\BSONDocument;
use MongoDB\Operation\Find;

use function array_intersect_key;
use function array_key_exists;
use function assert;
use function fopen;
use function get_resource_type;
use function in_array;
use function is_array;
use function is_bool;
use function is_integer;
use function is_object;
use function is_resource;
use function is_string;
use function method_exists;
use function MongoDB\apply_type_map_to_document;
use function MongoDB\BSON\fromPHP;
use function MongoDB\BSON\toJSON;
use function property_exists;
use function sprintf;
use function stream_context_create;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function stream_get_wrappers;
use function urlencode;





class Bucket
{
private const DEFAULT_BUCKET_NAME = 'fs';

private const DEFAULT_CHUNK_SIZE_BYTES = 261120;

private const DEFAULT_TYPE_MAP = [
'array' => BSONArray::class,
'document' => BSONDocument::class,
'root' => BSONDocument::class,
];

private const STREAM_WRAPPER_PROTOCOL = 'gridfs';

private ?DocumentCodec $codec = null;

private CollectionWrapper $collectionWrapper;

private string $databaseName;

private Manager $manager;

private string $bucketName;

private bool $disableMD5;

private int $chunkSizeBytes;

private ReadConcern $readConcern;

private ReadPreference $readPreference;

private array $typeMap;

private WriteConcern $writeConcern;




























public function __construct(Manager $manager, string $databaseName, array $options = [])
{
$options += [
'bucketName' => self::DEFAULT_BUCKET_NAME,
'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES,
'disableMD5' => false,
];

if (! is_string($options['bucketName'])) {
throw InvalidArgumentException::invalidType('"bucketName" option', $options['bucketName'], 'string');
}

if (! is_integer($options['chunkSizeBytes'])) {
throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
}

if ($options['chunkSizeBytes'] < 1) {
throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes']));
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (! is_bool($options['disableMD5'])) {
throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean');
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['codec']) && isset($options['typeMap'])) {
throw InvalidArgumentException::cannotCombineCodecAndTypeMap();
}

$this->manager = $manager;
$this->databaseName = $databaseName;
$this->bucketName = $options['bucketName'];
$this->chunkSizeBytes = $options['chunkSizeBytes'];
$this->codec = $options['codec'] ?? null;
$this->disableMD5 = $options['disableMD5'];
$this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern();
$this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference();
$this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP;
$this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern();

$collectionOptions = array_intersect_key($options, ['readConcern' => 1, 'readPreference' => 1, 'typeMap' => 1, 'writeConcern' => 1]);

$this->collectionWrapper = new CollectionWrapper($manager, $databaseName, $options['bucketName'], $collectionOptions);
$this->registerStreamWrapper();
}







public function __debugInfo()
{
return [
'bucketName' => $this->bucketName,
'codec' => $this->codec,
'databaseName' => $this->databaseName,
'disableMD5' => $this->disableMD5,
'manager' => $this->manager,
'chunkSizeBytes' => $this->chunkSizeBytes,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}












public function delete($id)
{
$file = $this->collectionWrapper->findFileById($id);
$this->collectionWrapper->deleteFileAndChunksById($id);

if ($file === null) {
throw FileNotFoundException::byId($id, $this->getFilesNamespace());
}
}












public function downloadToStream($id, $destination)
{
if (! is_resource($destination) || get_resource_type($destination) != 'stream') {
throw InvalidArgumentException::invalidType('$destination', $destination, 'resource');
}

$source = $this->openDownloadStream($id);
if (@stream_copy_to_stream($source, $destination) === false) {
throw StreamException::downloadFromIdFailed($id, $source, $destination);
}
}





























public function downloadToStreamByName(string $filename, $destination, array $options = [])
{
if (! is_resource($destination) || get_resource_type($destination) != 'stream') {
throw InvalidArgumentException::invalidType('$destination', $destination, 'resource');
}

$source = $this->openDownloadStreamByName($filename, $options);
if (@stream_copy_to_stream($source, $destination) === false) {
throw StreamException::downloadFromFilenameFailed($filename, $source, $destination);
}
}








public function drop()
{
$this->collectionWrapper->dropCollections();
}













public function find($filter = [], array $options = [])
{
if ($this->codec && ! array_key_exists('codec', $options)) {
$options['codec'] = $this->codec;
}

return $this->collectionWrapper->findFiles($filter, $options);
}













public function findOne($filter = [], array $options = [])
{
if ($this->codec && ! array_key_exists('codec', $options)) {
$options['codec'] = $this->codec;
}

return $this->collectionWrapper->findOneFile($filter, $options);
}






public function getBucketName()
{
return $this->bucketName;
}






public function getChunksCollection()
{
return $this->collectionWrapper->getChunksCollection();
}






public function getChunkSizeBytes()
{
return $this->chunkSizeBytes;
}






public function getDatabaseName()
{
return $this->databaseName;
}









public function getFileDocumentForStream($stream)
{
$file = $this->getRawFileDocumentForStream($stream);

if ($this->codec) {
return $this->codec->decode(Document::fromPHP($file));
}


return apply_type_map_to_document($file, $this->typeMap);
}










public function getFileIdForStream($stream)
{
$file = $this->getRawFileDocumentForStream($stream);




$typeMap = ['root' => 'stdClass'] + $this->typeMap;
$file = apply_type_map_to_document($file, $typeMap);
assert(is_object($file));

if (! isset($file->_id) && ! property_exists($file, '_id')) {
throw new CorruptFileException('file._id does not exist');
}

return $file->_id;
}






public function getFilesCollection()
{
return $this->collectionWrapper->getFilesCollection();
}







public function getReadConcern()
{
return $this->readConcern;
}






public function getReadPreference()
{
return $this->readPreference;
}






public function getTypeMap()
{
return $this->typeMap;
}







public function getWriteConcern()
{
return $this->writeConcern;
}









public function openDownloadStream($id)
{
$file = $this->collectionWrapper->findFileById($id);

if ($file === null) {
throw FileNotFoundException::byId($id, $this->getFilesNamespace());
}

return $this->openDownloadStreamByFile($file);
}


























public function openDownloadStreamByName(string $filename, array $options = [])
{
$options += ['revision' => -1];

$file = $this->collectionWrapper->findFileByFilenameAndRevision($filename, $options['revision']);

if ($file === null) {
throw FileNotFoundException::byFilenameAndRevision($filename, $options['revision'], $this->getFilesNamespace());
}

return $this->openDownloadStreamByFile($file);
}





















public function openUploadStream(string $filename, array $options = [])
{
$options += [
'chunkSizeBytes' => $this->chunkSizeBytes,
'disableMD5' => $this->disableMD5,
];

$path = $this->createPathForUpload();
$context = stream_context_create([
self::STREAM_WRAPPER_PROTOCOL => [
'collectionWrapper' => $this->collectionWrapper,
'filename' => $filename,
'options' => $options,
],
]);

return fopen($path, 'w', false, $context);
}










public function rename($id, string $newFilename)
{
$updateResult = $this->collectionWrapper->updateFilenameForId($id, $newFilename);

if ($updateResult->getModifiedCount() === 1) {
return;
}






$found = $updateResult->getMatchedCount() !== null
? $updateResult->getMatchedCount() === 1
: $this->collectionWrapper->findFileById($id) !== null;

if (! $found) {
throw FileNotFoundException::byId($id, $this->getFilesNamespace());
}
}

























public function uploadFromStream(string $filename, $source, array $options = [])
{
if (! is_resource($source) || get_resource_type($source) != 'stream') {
throw InvalidArgumentException::invalidType('$source', $source, 'resource');
}

$destination = $this->openUploadStream($filename, $options);

if (@stream_copy_to_stream($source, $destination) === false) {
$destinationUri = $this->createPathForFile($this->getRawFileDocumentForStream($destination));

throw StreamException::uploadFailed($filename, $source, $destinationUri);
}

return $this->getFileIdForStream($destination);
}






private function createPathForFile(object $file): string
{
if (is_array($file->_id) || (is_object($file->_id) && ! method_exists($file->_id, '__toString'))) {
$id = toJSON(fromPHP(['_id' => $file->_id]));
} else {
$id = (string) $file->_id;
}

return sprintf(
'%s://%s/%s.files/%s',
self::STREAM_WRAPPER_PROTOCOL,
urlencode($this->databaseName),
urlencode($this->bucketName),
urlencode($id),
);
}




private function createPathForUpload(): string
{
return sprintf(
'%s://%s/%s.files',
self::STREAM_WRAPPER_PROTOCOL,
urlencode($this->databaseName),
urlencode($this->bucketName),
);
}




private function getFilesNamespace(): string
{
return sprintf('%s.%s.files', $this->databaseName, $this->bucketName);
}










private function getRawFileDocumentForStream($stream): object
{
if (! is_resource($stream) || get_resource_type($stream) != 'stream') {
throw InvalidArgumentException::invalidType('$stream', $stream, 'resource');
}

$metadata = stream_get_meta_data($stream);

if (! isset($metadata['wrapper_data']) || ! $metadata['wrapper_data'] instanceof StreamWrapper) {
throw InvalidArgumentException::invalidType('$stream wrapper data', $metadata['wrapper_data'] ?? null, StreamWrapper::class);
}

return $metadata['wrapper_data']->getFile();
}







private function openDownloadStreamByFile(object $file)
{
$path = $this->createPathForFile($file);
$context = stream_context_create([
self::STREAM_WRAPPER_PROTOCOL => [
'collectionWrapper' => $this->collectionWrapper,
'file' => $file,
],
]);

return fopen($path, 'r', false, $context);
}




private function registerStreamWrapper(): void
{
if (in_array(self::STREAM_WRAPPER_PROTOCOL, stream_get_wrappers())) {
return;
}

StreamWrapper::register(self::STREAM_WRAPPER_PROTOCOL);
}
}
<?php
















namespace MongoDB\GridFS\Exception;

use MongoDB\Exception\RuntimeException;

use function MongoDB\BSON\fromPHP;
use function MongoDB\BSON\toJSON;
use function sprintf;

class FileNotFoundException extends RuntimeException
{








public static function byFilenameAndRevision(string $filename, int $revision, string $namespace)
{
return new self(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace));
}








public static function byId($id, string $namespace)
{
$json = toJSON(fromPHP(['_id' => $id]));

return new self(sprintf('File "%s" not found in "%s"', $json, $namespace));
}
}
<?php
















namespace MongoDB\GridFS\Exception;

use MongoDB\Exception\RuntimeException;

use function sprintf;

class CorruptFileException extends RuntimeException
{



public static function invalidChunkData(int $chunkIndex): self
{
return new self(sprintf('Invalid data found for index "%d"', $chunkIndex));
}







public static function missingChunk(int $expectedIndex)
{
return new self(sprintf('Chunk not found for index "%d"', $expectedIndex));
}








public static function unexpectedIndex(int $index, int $expectedIndex)
{
return new self(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index));
}








public static function unexpectedSize(int $size, int $expectedSize)
{
return new self(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size));
}
}
<?php

namespace MongoDB\GridFS\Exception;

use MongoDB\Exception\RuntimeException;

use function MongoDB\BSON\fromPHP;
use function MongoDB\BSON\toJSON;
use function sprintf;
use function stream_get_meta_data;

class StreamException extends RuntimeException
{




public static function downloadFromFilenameFailed(string $filename, $source, $destination): self
{
$sourceMetadata = stream_get_meta_data($source);
$destinationMetadata = stream_get_meta_data($destination);

return new self(sprintf('Downloading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $filename));
}






public static function downloadFromIdFailed($id, $source, $destination): self
{
$idString = toJSON(fromPHP(['_id' => $id]));
$sourceMetadata = stream_get_meta_data($source);
$destinationMetadata = stream_get_meta_data($destination);

return new self(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString));
}


public static function uploadFailed(string $filename, $source, string $destinationUri): self
{
$sourceMetadata = stream_get_meta_data($source);

return new self(sprintf('Uploading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationUri, $filename));
}
}
<?php
















namespace MongoDB\GridFS;

use HashContext;
use MongoDB\BSON\Binary;
use MongoDB\BSON\ObjectId;
use MongoDB\BSON\UTCDateTime;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Exception\InvalidArgumentException;

use function array_intersect_key;
use function hash_final;
use function hash_init;
use function hash_update;
use function is_bool;
use function is_integer;
use function is_string;
use function MongoDB\is_document;
use function MongoDB\is_string_array;
use function sprintf;
use function strlen;
use function substr;






class WritableStream
{
private const DEFAULT_CHUNK_SIZE_BYTES = 261120;

private string $buffer = '';

private int $chunkOffset = 0;

private int $chunkSize;

private bool $disableMD5;

private CollectionWrapper $collectionWrapper;

private array $file;

private ?HashContext $hashCtx = null;

private bool $isClosed = false;

private int $length = 0;





























public function __construct(CollectionWrapper $collectionWrapper, string $filename, array $options = [])
{
$options += [
'_id' => new ObjectId(),
'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES,
'disableMD5' => false,
];

if (isset($options['aliases']) && ! is_string_array($options['aliases'])) {
throw InvalidArgumentException::invalidType('"aliases" option', $options['aliases'], 'array of strings');
}

if (! is_integer($options['chunkSizeBytes'])) {
throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
}

if ($options['chunkSizeBytes'] < 1) {
throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes']));
}

if (! is_bool($options['disableMD5'])) {
throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean');
}

if (isset($options['contentType']) && ! is_string($options['contentType'])) {
throw InvalidArgumentException::invalidType('"contentType" option', $options['contentType'], 'string');
}

if (isset($options['metadata']) && ! is_document($options['metadata'])) {
throw InvalidArgumentException::expectedDocumentType('"metadata" option', $options['metadata']);
}

$this->chunkSize = $options['chunkSizeBytes'];
$this->collectionWrapper = $collectionWrapper;
$this->disableMD5 = $options['disableMD5'];

if (! $this->disableMD5) {
$this->hashCtx = hash_init('md5');
}

$this->file = [
'_id' => $options['_id'],
'chunkSize' => $this->chunkSize,
'filename' => $filename,
'length' => null,
'uploadDate' => null,
] + array_intersect_key($options, ['aliases' => 1, 'contentType' => 1, 'metadata' => 1]);
}






public function __debugInfo(): array
{
return [
'bucketName' => $this->collectionWrapper->getBucketName(),
'databaseName' => $this->collectionWrapper->getDatabaseName(),
'file' => $this->file,
];
}




public function close(): void
{
if ($this->isClosed) {

return;
}

if (strlen($this->buffer) > 0) {
$this->insertChunkFromBuffer();
}

$this->fileCollectionInsert();
$this->isClosed = true;
}




public function getFile(): object
{
return (object) $this->file;
}






public function getSize(): int
{
return $this->length + strlen($this->buffer);
}










public function tell(): int
{
return $this->getSize();
}









public function writeBytes(string $data): int
{
if ($this->isClosed) {

return 0;
}

$bytesRead = 0;

while ($bytesRead != strlen($data)) {
$initialBufferLength = strlen($this->buffer);
$this->buffer .= substr($data, $bytesRead, $this->chunkSize - $initialBufferLength);
$bytesRead += strlen($this->buffer) - $initialBufferLength;

if (strlen($this->buffer) == $this->chunkSize) {
$this->insertChunkFromBuffer();
}
}

return $bytesRead;
}

private function abort(): void
{
try {
$this->collectionWrapper->deleteChunksByFilesId($this->file['_id']);
} catch (DriverRuntimeException $e) {

}

$this->isClosed = true;
}

private function fileCollectionInsert(): void
{
$this->file['length'] = $this->length;
$this->file['uploadDate'] = new UTCDateTime();

if (! $this->disableMD5 && $this->hashCtx) {
$this->file['md5'] = hash_final($this->hashCtx);
}

try {
$this->collectionWrapper->insertFile($this->file);
} catch (DriverRuntimeException $e) {
$this->abort();

throw $e;
}
}

private function insertChunkFromBuffer(): void
{
if (strlen($this->buffer) == 0) {
return;
}

$data = $this->buffer;
$this->buffer = '';

$chunk = [
'files_id' => $this->file['_id'],
'n' => $this->chunkOffset,
'data' => new Binary($data),
];

if (! $this->disableMD5 && $this->hashCtx) {
hash_update($this->hashCtx, $data);
}

try {
$this->collectionWrapper->insertChunk($chunk);
} catch (DriverRuntimeException $e) {
$this->abort();

throw $e;
}

$this->length += strlen($data);
$this->chunkOffset++;
}
}
<?php
















namespace MongoDB\Model;

use IteratorIterator;
use Traversable;













class CollectionInfoCommandIterator extends IteratorIterator implements CollectionInfoIterator
{
private ?string $databaseName = null;


public function __construct(Traversable $iterator, ?string $databaseName = null)
{
parent::__construct($iterator);

$this->databaseName = $databaseName;
}







public function current(): CollectionInfo
{
$info = parent::current();

if ($this->databaseName !== null && isset($info['idIndex']) && ! isset($info['idIndex']['ns'])) {
$info['idIndex']['ns'] = $this->databaseName . '.' . $info['name'];
}

return new CollectionInfo($info);
}
}
<?php
















namespace MongoDB\Model;

use Iterator;
use ReturnTypeWillChange;









interface IndexInfoIterator extends Iterator
{





#[ReturnTypeWillChange]
public function current();
}
<?php
















namespace MongoDB\Model;

use Iterator;
use MongoDB\BSON\Document;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\CursorId;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Server;

use function assert;
use function iterator_to_array;
use function sprintf;
use function trigger_error;

use const E_USER_WARNING;






class CodecCursor implements CursorInterface, Iterator
{
private const TYPEMAP = ['root' => 'bson'];

private Cursor $cursor;


private DocumentCodec $codec;


private ?object $current = null;


public function current(): ?object
{
if (! $this->current && $this->valid()) {
$value = $this->cursor->current();
assert($value instanceof Document);
$this->current = $this->codec->decode($value);
}

return $this->current;
}






public static function fromCursor(Cursor $cursor, DocumentCodec $codec): self
{
$cursor->setTypeMap(self::TYPEMAP);

return new self($cursor, $codec);
}

public function getId(): CursorId
{
return $this->cursor->getId();
}

public function getServer(): Server
{
return $this->cursor->getServer();
}

public function isDead(): bool
{
return $this->cursor->isDead();
}

public function key(): int
{
return $this->cursor->key();
}

public function next(): void
{
$this->current = null;
$this->cursor->next();
}

public function rewind(): void
{
$this->current = null;
$this->cursor->rewind();
}

public function setTypeMap(array $typemap): void
{

trigger_error(sprintf('Discarding type map for %s', __METHOD__), E_USER_WARNING);
}


public function toArray(): array
{
return iterator_to_array($this);
}

public function valid(): bool
{
return $this->cursor->valid();
}


private function __construct(Cursor $cursor, DocumentCodec $codec)
{
$this->cursor = $cursor;
$this->codec = $codec;
}
}
<?php
















namespace MongoDB\Model;

use Iterator;
use ReturnTypeWillChange;









interface DatabaseInfoIterator extends Iterator
{





#[ReturnTypeWillChange]
public function current();
}
<?php
















namespace MongoDB\Model;

use Iterator;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use ReturnTypeWillChange;

use function is_array;
use function MongoDB\BSON\toPHP;
use function sprintf;
use function strlen;
use function substr;
use function unpack;






class BSONIterator implements Iterator
{
private const BSON_SIZE = 4;

private string $buffer;

private int $bufferLength;


private $current = null;

private int $key = 0;

private int $position = 0;

private array $options;





#[ReturnTypeWillChange]
public function current()
{
return $this->current;
}





#[ReturnTypeWillChange]
public function key()
{
return $this->key;
}





#[ReturnTypeWillChange]
public function next()
{
$this->key++;
$this->current = null;
$this->advance();
}





#[ReturnTypeWillChange]
public function rewind()
{
$this->key = 0;
$this->position = 0;
$this->current = null;
$this->advance();
}


#[ReturnTypeWillChange]
public function valid(): bool
{
return $this->current !== null;
}














public function __construct(string $data, array $options = [])
{
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (! isset($options['typeMap'])) {
$options['typeMap'] = [];
}

$this->buffer = $data;
$this->bufferLength = strlen($data);
$this->options = $options;
}

private function advance(): void
{
if ($this->position === $this->bufferLength) {
return;
}

if ($this->bufferLength - $this->position < self::BSON_SIZE) {
throw new UnexpectedValueException(sprintf('Expected at least %d bytes; %d remaining', self::BSON_SIZE, $this->bufferLength - $this->position));
}

[, $documentLength] = unpack('V', substr($this->buffer, $this->position, self::BSON_SIZE));

if ($this->bufferLength - $this->position < $documentLength) {
throw new UnexpectedValueException(sprintf('Expected %d bytes; %d remaining', $documentLength, $this->bufferLength - $this->position));
}

$this->current = toPHP(substr($this->buffer, $this->position, $documentLength), $this->options['typeMap']);
$this->position += $documentLength;
}
}
<?php
















namespace MongoDB\Model;

use MongoDB\BSON\Serializable;
use MongoDB\Exception\InvalidArgumentException;
use stdClass;

use function is_string;
use function MongoDB\is_document;











class SearchIndexInput implements Serializable
{

private array $index;





public function __construct(array $index)
{
if (! isset($index['definition'])) {
throw new InvalidArgumentException('Required "definition" document is missing from search index specification');
}

if (! is_document($index['definition'])) {
throw InvalidArgumentException::expectedDocumentType('"definition" option', $index['definition']);
}


if (isset($index['name']) && ! is_string($index['name'])) {
throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string');
}

$this->index = $index;
}







public function bsonSerialize(): stdClass
{
return (object) $this->index;
}
}
<?php
















namespace MongoDB\Model;

use ArrayAccess;
use MongoDB\Exception\BadMethodCallException;
use ReturnTypeWillChange;

use function array_key_exists;











class DatabaseInfo implements ArrayAccess
{
private array $info;


public function __construct(array $info)
{
$this->info = $info;
}







public function __debugInfo()
{
return $this->info;
}






public function getName()
{
return (string) $this->info['name'];
}






public function getSizeOnDisk()
{

return (integer) $this->info['sizeOnDisk'];
}






public function isEmpty()
{
return (boolean) $this->info['empty'];
}








#[ReturnTypeWillChange]
public function offsetExists($key)
{
return array_key_exists($key, $this->info);
}








#[ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->info[$key];
}










#[ReturnTypeWillChange]
public function offsetSet($key, $value)
{
throw BadMethodCallException::classIsImmutable(self::class);
}









#[ReturnTypeWillChange]
public function offsetUnset($key)
{
throw BadMethodCallException::classIsImmutable(self::class);
}
}
<?php
















namespace MongoDB\Model;

use IteratorIterator;
use Traversable;

use function array_key_exists;















class IndexInfoIteratorIterator extends IteratorIterator implements IndexInfoIterator
{
private ?string $ns = null;


public function __construct(Traversable $iterator, ?string $ns = null)
{
parent::__construct($iterator);

$this->ns = $ns;
}







public function current(): IndexInfo
{
$info = parent::current();

if (! array_key_exists('ns', $info) && $this->ns !== null) {
$info['ns'] = $this->ns;
}

return new IndexInfo($info);
}
}
<?php
















namespace MongoDB\Model;

use Iterator;
use ReturnTypeWillChange;









interface CollectionInfoIterator extends Iterator
{





#[ReturnTypeWillChange]
public function current();
}
<?php
















namespace MongoDB\Model;

use ArrayIterator;
use ArrayObject;
use JsonSerializable;
use MongoDB\BSON\Serializable;
use MongoDB\BSON\Unserializable;
use ReturnTypeWillChange;

use function MongoDB\recursive_copy;









class BSONDocument extends ArrayObject implements JsonSerializable, Serializable, Unserializable
{



public function __clone()
{
foreach ($this as $key => $value) {
$this[$key] = recursive_copy($value);
}
}









public function __construct(array $input = [], int $flags = ArrayObject::ARRAY_AS_PROPS, string $iteratorClass = ArrayIterator::class)
{
parent::__construct($input, $flags, $iteratorClass);
}








public static function __set_state(array $properties)
{
$document = new self();
$document->exchangeArray($properties);

return $document;
}







#[ReturnTypeWillChange]
public function bsonSerialize()
{
return (object) $this->getArrayCopy();
}







#[ReturnTypeWillChange]
public function bsonUnserialize(array $data)
{
parent::__construct($data, ArrayObject::ARRAY_AS_PROPS);
}







#[ReturnTypeWillChange]
public function jsonSerialize()
{
return (object) $this->getArrayCopy();
}
}
<?php
















namespace MongoDB\Model;

use Iterator;
use IteratorIterator;
use MongoDB\BSON\Document;
use MongoDB\BSON\Serializable;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Monitoring\CommandFailedEvent;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\ResumeTokenException;
use MongoDB\Exception\UnexpectedValueException;
use ReturnTypeWillChange;

use function assert;
use function count;
use function is_array;
use function is_object;
use function MongoDB\Driver\Monitoring\addSubscriber;
use function MongoDB\Driver\Monitoring\removeSubscriber;
use function MongoDB\is_document;












class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
{
private int $batchPosition = 0;

private int $batchSize;

private bool $isRewindNop;

private bool $isValid = false;

private ?object $postBatchResumeToken = null;


private $resumeToken;

private Server $server;






#[ReturnTypeWillChange]
public function current()
{
return $this->valid() ? parent::current() : null;
}









final public function getInnerIterator(): Iterator
{
$cursor = parent::getInnerIterator();
assert($cursor instanceof CursorInterface);
assert($cursor instanceof Iterator);

return $cursor;
}










public function getResumeToken()
{
return $this->resumeToken;
}




public function getServer(): Server
{
return $this->server;
}





#[ReturnTypeWillChange]
public function key()
{
return $this->valid() ? parent::key() : null;
}


public function next(): void
{





$getMore = $this->isAtEndOfBatch();

if ($getMore) {
addSubscriber($this);
}

try {
parent::next();

$this->onIteration(! $getMore);
} finally {
if ($getMore) {
removeSubscriber($this);
}
}
}


public function rewind(): void
{
if ($this->isRewindNop) {
return;
}

parent::rewind();

$this->onIteration(false);
}





public function valid(): bool
{
return $this->isValid;
}






public function __construct(CursorInterface $cursor, int $firstBatchSize, $initialResumeToken, ?object $postBatchResumeToken)
{
if (! $cursor instanceof Iterator) {
throw InvalidArgumentException::invalidType(
'$cursor',
$cursor,
CursorInterface::class . '&' . Iterator::class,
);
}

if (isset($initialResumeToken) && ! is_document($initialResumeToken)) {
throw InvalidArgumentException::expectedDocumentType('$initialResumeToken', $initialResumeToken);
}

parent::__construct($cursor);

$this->batchSize = $firstBatchSize;
$this->isRewindNop = ($firstBatchSize === 0);
$this->postBatchResumeToken = $postBatchResumeToken;
$this->resumeToken = $initialResumeToken;
$this->server = $cursor->getServer();
}


final public function commandFailed(CommandFailedEvent $event): void
{
}


final public function commandStarted(CommandStartedEvent $event): void
{
if ($event->getCommandName() !== 'getMore') {
return;
}

$this->batchPosition = 0;
$this->batchSize = 0;
$this->postBatchResumeToken = null;
}


final public function commandSucceeded(CommandSucceededEvent $event): void
{
if ($event->getCommandName() !== 'getMore') {
return;
}

$reply = $event->getReply();

if (! isset($reply->cursor->nextBatch) || ! is_array($reply->cursor->nextBatch)) {
throw new UnexpectedValueException('getMore command did not return a "cursor.nextBatch" array');
}

$this->batchSize = count($reply->cursor->nextBatch);

if (isset($reply->cursor->postBatchResumeToken) && is_object($reply->cursor->postBatchResumeToken)) {
$this->postBatchResumeToken = $reply->cursor->postBatchResumeToken;
}
}









private function extractResumeToken($document)
{
if (! is_document($document)) {
throw InvalidArgumentException::expectedDocumentType('$document', $document);
}

if ($document instanceof Serializable) {
return $this->extractResumeToken($document->bsonSerialize());
}

if ($document instanceof Document) {
$resumeToken = $document->get('_id');

if ($resumeToken instanceof Document) {
$resumeToken = $resumeToken->toPHP();
}
} else {
$resumeToken = is_array($document)
? ($document['_id'] ?? null)
: ($document->_id ?? null);
}

if (! isset($resumeToken)) {
$this->isValid = false;

throw ResumeTokenException::notFound();
}

if (! is_array($resumeToken) && ! is_object($resumeToken)) {
$this->isValid = false;

throw ResumeTokenException::invalidType($resumeToken);
}

return $resumeToken;
}




private function isAtEndOfBatch(): bool
{
return $this->batchPosition + 1 >= $this->batchSize;
}






private function onIteration(bool $incrementBatchPosition): void
{
$this->isValid = parent::valid();




if ($this->isRewindNop && $this->valid()) {
$this->isRewindNop = false;
}

if ($incrementBatchPosition && $this->valid()) {
$this->batchPosition++;
}







if ($this->isAtEndOfBatch() && $this->postBatchResumeToken !== null) {
$this->resumeToken = $this->postBatchResumeToken;
} elseif ($this->valid()) {
$this->resumeToken = $this->extractResumeToken($this->current());
}
}
}
<?php
















namespace MongoDB\Model;

use Countable;
use Iterator;
use IteratorIterator;
use ReturnTypeWillChange;
use Traversable;

use function count;
use function current;
use function next;
use function reset;













class CachingIterator implements Countable, Iterator
{
private const FIELD_KEY = 0;
private const FIELD_VALUE = 1;


private array $items = [];


private Iterator $iterator;

private bool $iteratorAdvanced = false;

private bool $iteratorExhausted = false;









public function __construct(Traversable $traversable)
{
$this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable);

$this->iterator->rewind();
$this->storeCurrentItem();
}


public function count(): int
{
$this->exhaustIterator();

return count($this->items);
}





#[ReturnTypeWillChange]
public function current()
{
$currentItem = current($this->items);

return $currentItem !== false ? $currentItem[self::FIELD_VALUE] : null;
}






#[ReturnTypeWillChange]
public function key()
{
$currentItem = current($this->items);

return $currentItem !== false ? $currentItem[self::FIELD_KEY] : null;
}


public function next(): void
{
if (! $this->iteratorExhausted) {
$this->iteratorAdvanced = true;
$this->iterator->next();

$this->storeCurrentItem();
}

next($this->items);
}


public function rewind(): void
{



if ($this->iteratorAdvanced) {
$this->exhaustIterator();
}

reset($this->items);
}


public function valid(): bool
{
return $this->key() !== null;
}




private function exhaustIterator(): void
{
while (! $this->iteratorExhausted) {
$this->next();
}
}




private function storeCurrentItem(): void
{
if (! $this->iterator->valid()) {
$this->iteratorExhausted = true;

return;
}


$this->items[] = [
self::FIELD_KEY => $this->iterator->key(),
self::FIELD_VALUE => $this->iterator->current(),
];
}
}
<?php
















namespace MongoDB\Model;

use ArrayAccess;
use MongoDB\Exception\BadMethodCallException;
use ReturnTypeWillChange;

use function array_key_exists;
use function array_search;
use function trigger_error;

use const E_USER_DEPRECATED;
















class IndexInfo implements ArrayAccess
{
private array $info;


public function __construct(array $info)
{
$this->info = $info;
}







public function __debugInfo()
{
return $this->info;
}






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






public function getKey()
{
return (array) $this->info['key'];
}






public function getName()
{
return (string) $this->info['name'];
}






public function getNamespace()
{
return (string) $this->info['ns'];
}






public function getVersion()
{
return (integer) $this->info['v'];
}






public function is2dSphere()
{
return array_search('2dsphere', $this->getKey(), true) !== false;
}







public function isGeoHaystack()
{
trigger_error('MongoDB 5.0 removes support for "geoHaystack" indexes, the method "IndexInfo::isGeoHaystack()" will be removed in a future release', E_USER_DEPRECATED);

return array_search('geoHaystack', $this->getKey(), true) !== false;
}







public function isSparse()
{
return ! empty($this->info['sparse']);
}






public function isText()
{
return array_search('text', $this->getKey(), true) !== false;
}







public function isTtl()
{
return array_key_exists('expireAfterSeconds', $this->info);
}







public function isUnique()
{
return ! empty($this->info['unique']);
}








#[ReturnTypeWillChange]
public function offsetExists($key)
{
return array_key_exists($key, $this->info);
}













#[ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->info[$key];
}










#[ReturnTypeWillChange]
public function offsetSet($key, $value)
{
throw BadMethodCallException::classIsImmutable(self::class);
}









#[ReturnTypeWillChange]
public function offsetUnset($key)
{
throw BadMethodCallException::classIsImmutable(self::class);
}
}
<?php
















namespace MongoDB\Model;

use ArrayAccess;
use MongoDB\Exception\BadMethodCallException;
use ReturnTypeWillChange;

use function array_key_exists;












class CollectionInfo implements ArrayAccess
{
private array $info;


public function __construct(array $info)
{
$this->info = $info;
}







public function __debugInfo()
{
return $this->info;
}








public function getCappedMax()
{

return isset($this->info['options']['max']) ? (integer) $this->info['options']['max'] : null;
}








public function getCappedSize()
{

return isset($this->info['options']['size']) ? (integer) $this->info['options']['size'] : null;
}




public function getIdIndex(): array
{
return (array) ($this->info['idIndex'] ?? []);
}






public function getInfo(): array
{
return (array) ($this->info['info'] ?? []);
}







public function getName()
{
return (string) $this->info['name'];
}







public function getOptions()
{
return (array) ($this->info['options'] ?? []);
}






public function getType(): string
{
return (string) $this->info['type'];
}








public function isCapped()
{
return ! empty($this->info['options']['capped']);
}








#[ReturnTypeWillChange]
public function offsetExists($key)
{
return array_key_exists($key, $this->info);
}








#[ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->info[$key];
}










#[ReturnTypeWillChange]
public function offsetSet($key, $value)
{
throw BadMethodCallException::classIsImmutable(self::class);
}









#[ReturnTypeWillChange]
public function offsetUnset($key)
{
throw BadMethodCallException::classIsImmutable(self::class);
}
}
<?php
















namespace MongoDB\Model;

use Iterator;
use IteratorIterator;
use ReturnTypeWillChange;
use Traversable;

use function call_user_func;











class CallbackIterator implements Iterator
{

private $callback;


private Iterator $iterator;





public function __construct(Traversable $traversable, callable $callback)
{
$this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable);
$this->callback = $callback;
}





#[ReturnTypeWillChange]
public function current()
{
return call_user_func($this->callback, $this->iterator->current(), $this->iterator->key());
}





#[ReturnTypeWillChange]
public function key()
{
return $this->iterator->key();
}


public function next(): void
{
$this->iterator->next();
}


public function rewind(): void
{
$this->iterator->rewind();
}


public function valid(): bool
{
return $this->iterator->valid();
}
}
<?php
















namespace MongoDB\Model;

use ArrayObject;
use JsonSerializable;
use MongoDB\BSON\Serializable;
use MongoDB\BSON\Unserializable;
use ReturnTypeWillChange;

use function array_values;
use function MongoDB\recursive_copy;









class BSONArray extends ArrayObject implements JsonSerializable, Serializable, Unserializable
{



public function __clone()
{
foreach ($this as $key => $value) {
$this[$key] = recursive_copy($value);
}
}








public static function __set_state(array $properties)
{
$array = new self();
$array->exchangeArray($properties);

return $array;
}










#[ReturnTypeWillChange]
public function bsonSerialize()
{
return array_values($this->getArrayCopy());
}







#[ReturnTypeWillChange]
public function bsonUnserialize(array $data)
{
parent::__construct($data);
}










#[ReturnTypeWillChange]
public function jsonSerialize()
{
return array_values($this->getArrayCopy());
}
}
<?php
















namespace MongoDB\Model;

use MongoDB\BSON\Serializable;
use MongoDB\Exception\InvalidArgumentException;
use stdClass;

use function is_float;
use function is_int;
use function is_string;
use function MongoDB\document_to_array;
use function MongoDB\is_document;
use function sprintf;











class IndexInput implements Serializable
{
private array $index;





public function __construct(array $index)
{
if (! isset($index['key'])) {
throw new InvalidArgumentException('Required "key" document is missing from index specification');
}

if (! is_document($index['key'])) {
throw InvalidArgumentException::expectedDocumentType('"key" option', $index['key']);
}

foreach ($index['key'] as $fieldName => $order) {
if (! is_int($order) && ! is_float($order) && ! is_string($order)) {
throw InvalidArgumentException::invalidType(sprintf('order value for "%s" field within "key" option', $fieldName), $order, 'numeric or string');
}
}

if (! isset($index['name'])) {
$index['name'] = $this->generateIndexName($index['key']);
}

if (! is_string($index['name'])) {
throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string');
}

$this->index = $index;
}




public function __toString(): string
{
return $this->index['name'];
}







public function bsonSerialize(): stdClass
{
return (object) $this->index;
}








private function generateIndexName($document): string
{
$document = document_to_array($document);

$name = '';

foreach ($document as $field => $type) {
$name .= ($name !== '' ? '_' : '') . $field . '_' . $type;
}

return $name;
}
}
<?php
















namespace MongoDB\Model;

use function current;
use function key;
use function next;
use function reset;











class DatabaseInfoLegacyIterator implements DatabaseInfoIterator
{
private array $databases;

public function __construct(array $databases)
{
$this->databases = $databases;
}







public function current(): DatabaseInfo
{
return new DatabaseInfo(current($this->databases));
}






public function key(): int
{
return key($this->databases);
}






public function next(): void
{
next($this->databases);
}






public function rewind(): void
{
reset($this->databases);
}






public function valid(): bool
{
return key($this->databases) !== null;
}
}
<?php
















namespace MongoDB;

use IteratorAggregate;
use ReturnTypeWillChange;
use stdClass;
use Traversable;

use function call_user_func;













class MapReduceResult implements IteratorAggregate
{




private $getIterator;

private int $executionTimeMS;

private array $counts;

private array $timing;






public function getCounts()
{
return $this->counts;
}






public function getExecutionTimeMS()
{
return $this->executionTimeMS;
}







#[ReturnTypeWillChange]
public function getIterator()
{
return call_user_func($this->getIterator);
}









public function getTiming()
{
return $this->timing;
}







public function __construct(callable $getIterator, stdClass $result)
{
$this->getIterator = $getIterator;
$this->executionTimeMS = isset($result->timeMillis) ? (integer) $result->timeMillis : 0;
$this->counts = isset($result->counts) ? (array) $result->counts : [];
$this->timing = isset($result->timing) ? (array) $result->timing : [];
}
}
<?php
















namespace MongoDB;

use Countable;
use Iterator;
use MongoDB\BSON\JavascriptInterface;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\BSONArray;
use MongoDB\Model\BSONDocument;
use MongoDB\Model\IndexInfo;
use MongoDB\Model\IndexInfoIterator;
use MongoDB\Operation\Aggregate;
use MongoDB\Operation\BulkWrite;
use MongoDB\Operation\Count;
use MongoDB\Operation\CountDocuments;
use MongoDB\Operation\CreateIndexes;
use MongoDB\Operation\CreateSearchIndexes;
use MongoDB\Operation\DeleteMany;
use MongoDB\Operation\DeleteOne;
use MongoDB\Operation\Distinct;
use MongoDB\Operation\DropCollection;
use MongoDB\Operation\DropEncryptedCollection;
use MongoDB\Operation\DropIndexes;
use MongoDB\Operation\DropSearchIndex;
use MongoDB\Operation\EstimatedDocumentCount;
use MongoDB\Operation\Explain;
use MongoDB\Operation\Explainable;
use MongoDB\Operation\Find;
use MongoDB\Operation\FindOne;
use MongoDB\Operation\FindOneAndDelete;
use MongoDB\Operation\FindOneAndReplace;
use MongoDB\Operation\FindOneAndUpdate;
use MongoDB\Operation\InsertMany;
use MongoDB\Operation\InsertOne;
use MongoDB\Operation\ListIndexes;
use MongoDB\Operation\ListSearchIndexes;
use MongoDB\Operation\MapReduce;
use MongoDB\Operation\RenameCollection;
use MongoDB\Operation\ReplaceOne;
use MongoDB\Operation\UpdateMany;
use MongoDB\Operation\UpdateOne;
use MongoDB\Operation\UpdateSearchIndex;
use MongoDB\Operation\Watch;

use function array_diff_key;
use function array_intersect_key;
use function array_key_exists;
use function current;
use function is_array;
use function strlen;

class Collection
{
private const DEFAULT_TYPE_MAP = [
'array' => BSONArray::class,
'document' => BSONDocument::class,
'root' => BSONDocument::class,
];

private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8;

private ?DocumentCodec $codec = null;

private string $collectionName;

private string $databaseName;

private Manager $manager;

private ReadConcern $readConcern;

private ReadPreference $readPreference;

private array $typeMap;

private WriteConcern $writeConcern;































public function __construct(Manager $manager, string $databaseName, string $collectionName, array $options = [])
{
if (strlen($databaseName) < 1) {
throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName);
}

if (strlen($collectionName) < 1) {
throw new InvalidArgumentException('$collectionName is invalid: ' . $collectionName);
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

$this->manager = $manager;
$this->databaseName = $databaseName;
$this->collectionName = $collectionName;

$this->codec = $options['codec'] ?? null;
$this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern();
$this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference();
$this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP;
$this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern();
}







public function __debugInfo()
{
return [
'codec' => $this->codec,
'collectionName' => $this->collectionName,
'databaseName' => $this->databaseName,
'manager' => $this->manager,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}







public function __toString()
{
return $this->databaseName . '.' . $this->collectionName;
}













public function aggregate(array $pipeline, array $options = [])
{
$hasWriteStage = is_last_pipeline_operator_write($pipeline);

$options = $this->inheritReadPreference($options);

$server = $hasWriteStage
? select_server_for_aggregate_write_stage($this->manager, $options)
: select_server($this->manager, $options);




if (! $hasWriteStage || server_supports_feature($server, self::WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE)) {
$options = $this->inheritReadConcern($options);
}

$options = $this->inheritCodecOrTypeMap($options);

if ($hasWriteStage) {
$options = $this->inheritWriteOptions($options);
}

$operation = new Aggregate($this->databaseName, $this->collectionName, $pipeline, $options);

return $operation->execute($server);
}












public function bulkWrite(array $operations, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodec($options);

$operation = new BulkWrite($this->databaseName, $this->collectionName, $operations, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}















public function count($filter = [], array $options = [])
{
$options = $this->inheritReadOptions($options);

$operation = new Count($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server($this->manager, $options));
}













public function countDocuments($filter = [], array $options = [])
{
$options = $this->inheritReadOptions($options);

$operation = new CountDocuments($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server($this->manager, $options));
}














public function createIndex($key, array $options = [])
{
$commandOptionKeys = ['commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1];
$indexOptions = array_diff_key($options, $commandOptionKeys);
$commandOptions = array_intersect_key($options, $commandOptionKeys);

return current($this->createIndexes([['key' => $key] + $indexOptions], $commandOptions));
}




























public function createIndexes(array $indexes, array $options = [])
{
$options = $this->inheritWriteOptions($options);

$operation = new CreateIndexes($this->databaseName, $this->collectionName, $indexes, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}














public function createSearchIndex($definition, array $options = []): string
{
$index = ['definition' => $definition];
if (isset($options['name'])) {
$index['name'] = $options['name'];
unset($options['name']);
}

$names = $this->createSearchIndexes([$index], $options);

return current($names);
}

























public function createSearchIndexes(array $indexes, array $options = []): array
{
$operation = new CreateSearchIndexes($this->databaseName, $this->collectionName, $indexes, $options);
$server = select_server_for_write($this->manager, $options);

return $operation->execute($server);
}













public function deleteMany($filter, array $options = [])
{
$options = $this->inheritWriteOptions($options);

$operation = new DeleteMany($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}













public function deleteOne($filter, array $options = [])
{
$options = $this->inheritWriteOptions($options);

$operation = new DeleteOne($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}














public function distinct(string $fieldName, $filter = [], array $options = [])
{
$options = $this->inheritReadOptions($options);
$options = $this->inheritTypeMap($options);

$operation = new Distinct($this->databaseName, $this->collectionName, $fieldName, $filter, $options);

return $operation->execute(select_server($this->manager, $options));
}











public function drop(array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritTypeMap($options);

$server = select_server_for_write($this->manager, $options);

if (! isset($options['encryptedFields'])) {
$options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $this->collectionName, $this->manager)
?? get_encrypted_fields_from_server($this->databaseName, $this->collectionName, $this->manager, $server);
}

$operation = isset($options['encryptedFields'])
? new DropEncryptedCollection($this->databaseName, $this->collectionName, $options)
: new DropCollection($this->databaseName, $this->collectionName, $options);

return $operation->execute($server);
}












public function dropIndex($indexName, array $options = [])
{
$indexName = (string) $indexName;

if ($indexName === '*') {
throw new InvalidArgumentException('dropIndexes() must be used to drop multiple indexes');
}

$options = $this->inheritWriteOptions($options);
$options = $this->inheritTypeMap($options);

$operation = new DropIndexes($this->databaseName, $this->collectionName, $indexName, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}











public function dropIndexes(array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritTypeMap($options);

$operation = new DropIndexes($this->databaseName, $this->collectionName, '*', $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}











public function dropSearchIndex(string $name, array $options = []): void
{
$operation = new DropSearchIndex($this->databaseName, $this->collectionName, $name);
$server = select_server_for_write($this->manager, $options);

$operation->execute($server);
}












public function estimatedDocumentCount(array $options = [])
{
$options = $this->inheritReadOptions($options);

$operation = new EstimatedDocumentCount($this->databaseName, $this->collectionName, $options);

return $operation->execute(select_server($this->manager, $options));
}













public function explain(Explainable $explainable, array $options = [])
{
$options = $this->inheritReadPreference($options);
$options = $this->inheritTypeMap($options);

$operation = new Explain($this->databaseName, $explainable, $options);

return $operation->execute(select_server($this->manager, $options));
}













public function find($filter = [], array $options = [])
{
$options = $this->inheritReadOptions($options);
$options = $this->inheritCodecOrTypeMap($options);

$operation = new Find($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server($this->manager, $options));
}













public function findOne($filter = [], array $options = [])
{
$options = $this->inheritReadOptions($options);
$options = $this->inheritCodecOrTypeMap($options);

$operation = new FindOne($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server($this->manager, $options));
}
















public function findOneAndDelete($filter, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodecOrTypeMap($options);

$operation = new FindOneAndDelete($this->databaseName, $this->collectionName, $filter, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}





















public function findOneAndReplace($filter, $replacement, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodecOrTypeMap($options);

$operation = new FindOneAndReplace($this->databaseName, $this->collectionName, $filter, $replacement, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}





















public function findOneAndUpdate($filter, $update, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodecOrTypeMap($options);

$operation = new FindOneAndUpdate($this->databaseName, $this->collectionName, $filter, $update, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}






public function getCollectionName()
{
return $this->collectionName;
}






public function getDatabaseName()
{
return $this->databaseName;
}






public function getManager()
{
return $this->manager;
}







public function getNamespace()
{
return $this->databaseName . '.' . $this->collectionName;
}







public function getReadConcern()
{
return $this->readConcern;
}






public function getReadPreference()
{
return $this->readPreference;
}






public function getTypeMap()
{
return $this->typeMap;
}







public function getWriteConcern()
{
return $this->writeConcern;
}












public function insertMany(array $documents, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodec($options);

$operation = new InsertMany($this->databaseName, $this->collectionName, $documents, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}












public function insertOne($document, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodec($options);

$operation = new InsertOne($this->databaseName, $this->collectionName, $document, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}









public function listIndexes(array $options = [])
{
$operation = new ListIndexes($this->databaseName, $this->collectionName, $options);

return $operation->execute(select_server($this->manager, $options));
}











public function listSearchIndexes(array $options = []): Iterator
{
$operation = new ListSearchIndexes($this->databaseName, $this->collectionName, $options);
$server = select_server($this->manager, $options);

return $operation->execute($server);
}
















public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [])
{
$hasOutputCollection = ! is_mapreduce_output_inline($out);


if ($hasOutputCollection) {
$options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY);
} else {
$options = $this->inheritReadPreference($options);
}




if (! $hasOutputCollection || $this->readConcern->getLevel() !== ReadConcern::MAJORITY) {
$options = $this->inheritReadConcern($options);
}

$options = $this->inheritWriteOptions($options);
$options = $this->inheritTypeMap($options);

$operation = new MapReduce($this->databaseName, $this->collectionName, $map, $reduce, $out, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}













public function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = [])
{
if (! isset($toDatabaseName)) {
$toDatabaseName = $this->databaseName;
}

$options = $this->inheritWriteOptions($options);
$options = $this->inheritTypeMap($options);

$operation = new RenameCollection($this->databaseName, $this->collectionName, $toDatabaseName, $toCollectionName, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}














public function replaceOne($filter, $replacement, array $options = [])
{
$options = $this->inheritWriteOptions($options);
$options = $this->inheritCodec($options);

$operation = new ReplaceOne($this->databaseName, $this->collectionName, $filter, $replacement, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}














public function updateMany($filter, $update, array $options = [])
{
$options = $this->inheritWriteOptions($options);

$operation = new UpdateMany($this->databaseName, $this->collectionName, $filter, $update, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}














public function updateOne($filter, $update, array $options = [])
{
$options = $this->inheritWriteOptions($options);

$operation = new UpdateOne($this->databaseName, $this->collectionName, $filter, $update, $options);

return $operation->execute(select_server_for_write($this->manager, $options));
}












public function updateSearchIndex(string $name, $definition, array $options = []): void
{
$operation = new UpdateSearchIndex($this->databaseName, $this->collectionName, $name, $definition, $options);
$server = select_server_for_write($this->manager, $options);

$operation->execute($server);
}










public function watch(array $pipeline = [], array $options = [])
{
$options = $this->inheritReadOptions($options);
$options = $this->inheritCodecOrTypeMap($options);

$operation = new Watch($this->manager, $this->databaseName, $this->collectionName, $pipeline, $options);

return $operation->execute(select_server($this->manager, $options));
}









public function withOptions(array $options = [])
{
$options += [
'codec' => $this->codec,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];

return new Collection($this->manager, $this->databaseName, $this->collectionName, $options);
}

private function inheritCodec(array $options): array
{

if (isset($options['typeMap'])) {
return $options;
}

if (! array_key_exists('codec', $options)) {
$options['codec'] = $this->codec;
}

return $options;
}

private function inheritCodecOrTypeMap(array $options): array
{

if (isset($options['typeMap'])) {
return $options;
}



if (! $this->codec || array_key_exists('codec', $options)) {
return $this->inheritTypeMap($options);
}



$options['codec'] = $this->codec;

return $options;
}

private function inheritReadConcern(array $options): array
{

if (! isset($options['readConcern']) && ! is_in_transaction($options)) {
$options['readConcern'] = $this->readConcern;
}

return $options;
}

private function inheritReadOptions(array $options): array
{
$options = $this->inheritReadConcern($options);

return $this->inheritReadPreference($options);
}

private function inheritReadPreference(array $options): array
{

if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
$options['readPreference'] = $this->readPreference;
}

return $options;
}

private function inheritTypeMap(array $options): array
{

if (! isset($options['typeMap']) && ! isset($options['codec'])) {
$options['typeMap'] = $this->typeMap;
}

return $options;
}

private function inheritWriteOptions(array $options): array
{

if (! is_in_transaction($options)) {
if (! isset($options['writeConcern'])) {
$options['writeConcern'] = $this->writeConcern;
}
}

return $options;
}
}
<?php
















namespace MongoDB;

use MongoDB\Driver\WriteResult;
use MongoDB\Exception\BadMethodCallException;




class BulkWriteResult
{
private WriteResult $writeResult;

private array $insertedIds;

private bool $isAcknowledged;

public function __construct(WriteResult $writeResult, array $insertedIds)
{
$this->writeResult = $writeResult;
$this->insertedIds = $insertedIds;
$this->isAcknowledged = $writeResult->isAcknowledged();
}










public function getDeletedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getDeletedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}










public function getInsertedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getInsertedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}












public function getInsertedIds()
{
return $this->insertedIds;
}










public function getMatchedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getMatchedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}













public function getModifiedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getModifiedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}










public function getUpsertedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getUpsertedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}















public function getUpsertedIds()
{
if ($this->isAcknowledged) {
return $this->writeResult->getUpsertedIds();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}









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
















namespace MongoDB\Codec;

use MongoDB\Exception\UnsupportedValueException;





trait DecodeIfSupported
{




abstract public function canDecode($value): bool;








abstract public function decode($value);






public function decodeIfSupported($value)
{
return $this->canDecode($value) ? $this->decode($value) : $value;
}
}
<?php
















namespace MongoDB\Codec;

use MongoDB\Exception\UnsupportedValueException;





trait EncodeIfSupported
{




abstract public function canEncode($value): bool;








abstract public function encode($value);






public function encodeIfSupported($value)
{
return $this->canEncode($value) ? $this->encode($value) : $value;
}
}
<?php
















namespace MongoDB\Codec;










interface Codec extends Decoder, Encoder
{
}
<?php
















namespace MongoDB\Codec;

use MongoDB\BSON\Document;
use MongoDB\Exception\UnsupportedValueException;








interface DocumentCodec extends Codec
{






public function decode($value): object;






public function encode($value): Document;
}
<?php
















namespace MongoDB\Codec;

use MongoDB\Exception\UnsupportedValueException;





interface Encoder
{






public function canEncode($value): bool;











public function encode($value);











public function encodeIfSupported($value);
}
<?php
















namespace MongoDB\Codec;

use MongoDB\Exception\UnsupportedValueException;





interface Decoder
{






public function canDecode($value): bool;











public function decode($value);











public function decodeIfSupported($value);
}
<?php
















namespace MongoDB;

use MongoDB\Driver\WriteResult;
use MongoDB\Exception\BadMethodCallException;




class DeleteResult
{
private WriteResult $writeResult;

private bool $isAcknowledged;

public function __construct(WriteResult $writeResult)
{
$this->writeResult = $writeResult;
$this->isAcknowledged = $writeResult->isAcknowledged();
}










public function getDeletedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getDeletedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}









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
















namespace MongoDB;

use MongoDB\Driver\Monitoring\LogSubscriber;
use MongoDB\Exception\UnexpectedValueException;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use SplObjectStorage;

use function MongoDB\Driver\Monitoring\addSubscriber;
use function MongoDB\Driver\Monitoring\removeSubscriber;
use function sprintf;









final class PsrLogAdapter implements LogSubscriber
{
public const EMERGENCY = 0;
public const ALERT = 1;
public const CRITICAL = 2;
public const ERROR = 3;
public const WARN = 4;
public const NOTICE = 5;
public const INFO = 6;
public const DEBUG = 7;
public const TRACE = 8;

private static ?self $instance = null;


private SplObjectStorage $loggers;

private const SPEC_TO_PSR = [
self::EMERGENCY => LogLevel::EMERGENCY,
self::ALERT => LogLevel::ALERT,
self::CRITICAL => LogLevel::CRITICAL,
self::ERROR => LogLevel::ERROR,
self::WARN => LogLevel::WARNING,
self::NOTICE => LogLevel::NOTICE,
self::INFO => LogLevel::INFO,
self::DEBUG => LogLevel::DEBUG,

self::TRACE => LogLevel::DEBUG,
];

private const MONGOC_TO_PSR = [
LogSubscriber::LEVEL_ERROR => LogLevel::ERROR,


LogSubscriber::LEVEL_CRITICAL => LogLevel::ERROR,
LogSubscriber::LEVEL_WARNING => LogLevel::WARNING,
LogSubscriber::LEVEL_MESSAGE => LogLevel::NOTICE,
LogSubscriber::LEVEL_INFO => LogLevel::INFO,
LogSubscriber::LEVEL_DEBUG => LogLevel::DEBUG,
];

public static function addLogger(LoggerInterface $logger): void
{
$instance = self::getInstance();

$instance->loggers->attach($logger);

addSubscriber($instance);
}






public function log(int $mongocLevel, string $domain, string $message): void
{
if (! isset(self::MONGOC_TO_PSR[$mongocLevel])) {
throw new UnexpectedValueException(sprintf(
'Expected level to be >= %d and <= %d, %d given for domain "%s" and message: %s',
LogSubscriber::LEVEL_ERROR,
LogSubscriber::LEVEL_DEBUG,
$mongocLevel,
$domain,
$message,
));
}

$instance = self::getInstance();
$psrLevel = self::MONGOC_TO_PSR[$mongocLevel];
$context = ['domain' => $domain];

foreach ($instance->loggers as $logger) {
$logger->log($psrLevel, $message, $context);
}
}

public static function removeLogger(LoggerInterface $logger): void
{
$instance = self::getInstance();
$instance->loggers->detach($logger);

if ($instance->loggers->count() === 0) {
removeSubscriber($instance);
}
}






public static function writeLog(int $specLevel, string $domain, string $message): void
{
if (! isset(self::SPEC_TO_PSR[$specLevel])) {
throw new UnexpectedValueException(sprintf(
'Expected level to be >= %d and <= %d, %d given for domain "%s" and message: %s',
self::EMERGENCY,
self::TRACE,
$specLevel,
$domain,
$message,
));
}

$instance = self::getInstance();
$psrLevel = self::SPEC_TO_PSR[$specLevel];
$context = ['domain' => $domain];

foreach ($instance->loggers as $logger) {
$logger->log($psrLevel, $message, $context);
}
}

private function __construct()
{
$this->loggers = new SplObjectStorage();
}

private static function getInstance(): self
{
return self::$instance ??= new self();
}
}
<?php
















namespace MongoDB;

use MongoDB\Driver\WriteResult;
use MongoDB\Exception\BadMethodCallException;




class UpdateResult
{
private WriteResult $writeResult;

private bool $isAcknowledged;

public function __construct(WriteResult $writeResult)
{
$this->writeResult = $writeResult;
$this->isAcknowledged = $writeResult->isAcknowledged();
}










public function getMatchedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getMatchedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}













public function getModifiedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getModifiedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}










public function getUpsertedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getUpsertedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}
















public function getUpsertedId()
{
if ($this->isAcknowledged) {
foreach ($this->writeResult->getUpsertedIds() as $id) {
return $id;
}

return null;
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}










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
















namespace MongoDB\Command;

use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Model\CachingIterator;
use MongoDB\Operation\Executable;

use function is_bool;
use function is_integer;
use function MongoDB\is_document;







class ListCollections implements Executable
{
private string $databaseName;

private array $options;






























public function __construct(string $databaseName, array $options = [])
{
if (isset($options['authorizedCollections']) && ! is_bool($options['authorizedCollections'])) {
throw InvalidArgumentException::invalidType('"authorizedCollections" option', $options['authorizedCollections'], 'boolean');
}

if (isset($options['filter']) && ! is_document($options['filter'])) {
throw InvalidArgumentException::expectedDocumentType('"filter" option', $options['filter']);
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['nameOnly']) && ! is_bool($options['nameOnly'])) {
throw InvalidArgumentException::invalidType('"nameOnly" option', $options['nameOnly'], 'boolean');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

$this->databaseName = $databaseName;
$this->options = $options;
}








public function execute(Server $server): CachingIterator
{

$cursor = $server->executeReadCommand($this->databaseName, $this->createCommand(), $this->createOptions());
$cursor->setTypeMap(['root' => 'array', 'document' => 'array']);

return new CachingIterator($cursor);
}




private function createCommand(): Command
{
$cmd = ['listCollections' => 1];

if (! empty($this->options['filter'])) {
$cmd['filter'] = (object) $this->options['filter'];
}

foreach (['authorizedCollections', 'comment', 'maxTimeMS', 'nameOnly'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return new Command($cmd);
}









private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}
}
<?php
















namespace MongoDB\Command;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Operation\Executable;

use function current;
use function is_array;
use function is_bool;
use function is_integer;
use function MongoDB\is_document;







class ListDatabases implements Executable
{
private array $options;





























public function __construct(array $options = [])
{
if (isset($options['authorizedDatabases']) && ! is_bool($options['authorizedDatabases'])) {
throw InvalidArgumentException::invalidType('"authorizedDatabases" option', $options['authorizedDatabases'], 'boolean');
}

if (isset($options['filter']) && ! is_document($options['filter'])) {
throw InvalidArgumentException::expectedDocumentType('"filter" option', $options['filter']);
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['nameOnly']) && ! is_bool($options['nameOnly'])) {
throw InvalidArgumentException::invalidType('"nameOnly" option', $options['nameOnly'], 'boolean');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

$this->options = $options;
}









public function execute(Server $server): array
{
$cursor = $server->executeReadCommand('admin', $this->createCommand(), $this->createOptions());
$cursor->setTypeMap(['root' => 'array', 'document' => 'array']);
$result = current($cursor->toArray());

if (! isset($result['databases']) || ! is_array($result['databases'])) {
throw new UnexpectedValueException('listDatabases command did not return a "databases" array');
}

return $result['databases'];
}




private function createCommand(): Command
{
$cmd = ['listDatabases' => 1];

if (! empty($this->options['filter'])) {
$cmd['filter'] = (object) $this->options['filter'];
}

foreach (['authorizedDatabases', 'comment', 'maxTimeMS', 'nameOnly'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return new Command($cmd);
}









private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}
}
<?php
















namespace MongoDB;

use Iterator;
use Jean85\PrettyVersions;
use MongoDB\Driver\ClientEncryption;
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\BSONArray;
use MongoDB\Model\BSONDocument;
use MongoDB\Model\DatabaseInfoIterator;
use MongoDB\Operation\DropDatabase;
use MongoDB\Operation\ListDatabaseNames;
use MongoDB\Operation\ListDatabases;
use MongoDB\Operation\Watch;
use Throwable;

use function is_array;
use function is_string;

class Client
{
public const DEFAULT_URI = 'mongodb://127.0.0.1/';

private const DEFAULT_TYPE_MAP = [
'array' => BSONArray::class,
'document' => BSONDocument::class,
'root' => BSONDocument::class,
];

private const HANDSHAKE_SEPARATOR = '/';

private static ?string $version = null;

private Manager $manager;

private ReadConcern $readConcern;

private ReadPreference $readPreference;

private string $uri;

private array $typeMap;

private WriteConcern $writeConcern;
























public function __construct(?string $uri = null, array $uriOptions = [], array $driverOptions = [])
{
$driverOptions += ['typeMap' => self::DEFAULT_TYPE_MAP];

if (! is_array($driverOptions['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" driver option', $driverOptions['typeMap'], 'array');
}

if (isset($driverOptions['autoEncryption']['keyVaultClient'])) {
if ($driverOptions['autoEncryption']['keyVaultClient'] instanceof self) {
$driverOptions['autoEncryption']['keyVaultClient'] = $driverOptions['autoEncryption']['keyVaultClient']->manager;
} elseif (! $driverOptions['autoEncryption']['keyVaultClient'] instanceof Manager) {
throw InvalidArgumentException::invalidType('"keyVaultClient" autoEncryption option', $driverOptions['autoEncryption']['keyVaultClient'], [self::class, Manager::class]);
}
}

$driverOptions['driver'] = $this->mergeDriverInfo($driverOptions['driver'] ?? []);

$this->uri = $uri ?? self::DEFAULT_URI;
$this->typeMap = $driverOptions['typeMap'];

unset($driverOptions['typeMap']);

$this->manager = new Manager($uri, $uriOptions, $driverOptions);
$this->readConcern = $this->manager->getReadConcern();
$this->readPreference = $this->manager->getReadPreference();
$this->writeConcern = $this->manager->getWriteConcern();
}







public function __debugInfo()
{
return [
'manager' => $this->manager,
'uri' => $this->uri,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}













public function __get(string $databaseName)
{
return $this->selectDatabase($databaseName);
}






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








public function createClientEncryption(array $options)
{
if (isset($options['keyVaultClient'])) {
if ($options['keyVaultClient'] instanceof self) {
$options['keyVaultClient'] = $options['keyVaultClient']->manager;
} elseif (! $options['keyVaultClient'] instanceof Manager) {
throw InvalidArgumentException::invalidType('"keyVaultClient" option', $options['keyVaultClient'], [self::class, Manager::class]);
}
}

return $this->manager->createClientEncryption($options);
}












public function dropDatabase(string $databaseName, array $options = [])
{
if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$server = select_server_for_write($this->manager, $options);

if (! isset($options['writeConcern']) && ! is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}

$operation = new DropDatabase($databaseName, $options);

return $operation->execute($server);
}






public function getManager()
{
return $this->manager;
}







public function getReadConcern()
{
return $this->readConcern;
}






public function getReadPreference()
{
return $this->readPreference;
}






public function getTypeMap()
{
return $this->typeMap;
}







public function getWriteConcern()
{
return $this->writeConcern;
}









public function listDatabaseNames(array $options = []): Iterator
{
$operation = new ListDatabaseNames($options);
$server = select_server($this->manager, $options);

return $operation->execute($server);
}










public function listDatabases(array $options = [])
{
$operation = new ListDatabases($options);
$server = select_server($this->manager, $options);

return $operation->execute($server);
}











public function selectCollection(string $databaseName, string $collectionName, array $options = [])
{
$options += ['typeMap' => $this->typeMap];

return new Collection($this->manager, $databaseName, $collectionName, $options);
}










public function selectDatabase(string $databaseName, array $options = [])
{
$options += ['typeMap' => $this->typeMap];

return new Database($this->manager, $databaseName, $options);
}








public function startSession(array $options = [])
{
return $this->manager->startSession($options);
}










public function watch(array $pipeline = [], array $options = [])
{
if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
$options['readPreference'] = $this->readPreference;
}

$server = select_server($this->manager, $options);

if (! isset($options['readConcern']) && ! is_in_transaction($options)) {
$options['readConcern'] = $this->readConcern;
}

if (! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}

$operation = new Watch($this->manager, null, null, $pipeline, $options);

return $operation->execute($server);
}

private static function getVersion(): string
{
if (self::$version === null) {
try {
self::$version = PrettyVersions::getVersion('mongodb/mongodb')->getPrettyVersion();
} catch (Throwable $t) {
return 'unknown';
}
}

return self::$version;
}

private function mergeDriverInfo(array $driver): array
{
$mergedDriver = [
'name' => 'PHPLIB',
'version' => self::getVersion(),
];

if (isset($driver['name'])) {
if (! is_string($driver['name'])) {
throw InvalidArgumentException::invalidType('"name" handshake option', $driver['name'], 'string');
}

$mergedDriver['name'] .= self::HANDSHAKE_SEPARATOR . $driver['name'];
}

if (isset($driver['version'])) {
if (! is_string($driver['version'])) {
throw InvalidArgumentException::invalidType('"version" handshake option', $driver['version'], 'string');
}

$mergedDriver['version'] .= self::HANDSHAKE_SEPARATOR . $driver['version'];
}

if (isset($driver['platform'])) {
$mergedDriver['platform'] = $driver['platform'];
}

return $mergedDriver;
}
}
<?php
















namespace MongoDB;

use MongoDB\Driver\WriteResult;
use MongoDB\Exception\BadMethodCallException;




class InsertOneResult
{
private WriteResult $writeResult;


private $insertedId;

private bool $isAcknowledged;


public function __construct(WriteResult $writeResult, $insertedId)
{
$this->writeResult = $writeResult;
$this->insertedId = $insertedId;
$this->isAcknowledged = $writeResult->isAcknowledged();
}










public function getInsertedCount()
{
if ($this->isAcknowledged) {
return $this->writeResult->getInsertedCount();
}

throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__);
}










public function getInsertedId()
{
return $this->insertedId;
}













public function isAcknowledged()
{
return $this->writeResult->isAcknowledged();
}
}
<?php
















namespace MongoDB\Exception;

use InvalidArgumentException;

use function get_debug_type;
use function sprintf;

class UnsupportedValueException extends InvalidArgumentException implements Exception
{

private $value;


public function getValue()
{
return $this->value;
}


public static function invalidDecodableValue($value): self
{
return new self(sprintf('Could not decode value of type "%s".', get_debug_type($value)), $value);
}


public static function invalidEncodableValue($value): self
{
return new self(sprintf('Could not encode value of type "%s".', get_debug_type($value)), $value);
}


private function __construct(string $message, $value)
{
parent::__construct($message);

$this->value = $value;
}
}
<?php
















namespace MongoDB\Exception;

use function get_debug_type;
use function sprintf;

class ResumeTokenException extends RuntimeException
{






public static function invalidType($value)
{
return new self(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value)));
}






public static function notFound()
{
return new self('Resume token not found in change document');
}
}
<?php
















namespace MongoDB\Exception;

class UnsupportedException extends RuntimeException
{





public static function allowDiskUseNotSupported()
{
return new self('The "allowDiskUse" option is not supported by the server executing this operation');
}









public static function arrayFiltersNotSupported()
{
return new self('Array filters are not supported by the server executing this operation');
}









public static function collationNotSupported()
{
return new self('Collations are not supported by the server executing this operation');
}







public static function commitQuorumNotSupported()
{
return new self('The "commitQuorum" option is not supported by the server executing this operation');
}






public static function explainNotSupported()
{
return new self('Explain is not supported by the server executing this operation');
}






public static function hintNotSupported()
{
return new self('Hint is not supported by the server executing this operation');
}






public static function readConcernNotSupported()
{
return new self('Read concern is not supported by the server executing this command');
}






public static function readConcernNotSupportedInTransaction()
{
return new self('The "readConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.');
}






public static function writeConcernNotSupported()
{
return new self('Write concern is not supported by the server executing this command');
}






public static function writeConcernNotSupportedInTransaction()
{
return new self('The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.');
}
}
<?php
















namespace MongoDB\Exception;

use Throwable;

use function get_class;
use function sprintf;





final class CreateEncryptedCollectionException extends RuntimeException
{
private array $encryptedFields;

public function __construct(Throwable $previous, array $encryptedFields)
{
parent::__construct(sprintf('Creating encrypted collection failed due to previous %s: %s', get_class($previous), $previous->getMessage()), 0, $previous);

$this->encryptedFields = $encryptedFields;
}












public function getEncryptedFields(): array
{
return $this->encryptedFields;
}
}
<?php
















namespace MongoDB\Exception;

use MongoDB\Driver\Exception\Exception as DriverException;

interface Exception extends DriverException
{
}
<?php
















namespace MongoDB\Exception;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;

class RuntimeException extends DriverRuntimeException implements Exception
{
}
<?php
















namespace MongoDB\Exception;

use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;

use function array_pop;
use function assert;
use function count;
use function get_debug_type;
use function implode;
use function is_array;
use function sprintf;

class InvalidArgumentException extends DriverInvalidArgumentException implements Exception
{
public static function cannotCombineCodecAndTypeMap(): self
{
return new self('Cannot provide both "codec" and "typeMap" options');
}







public static function expectedDocumentType(string $name, $value): self
{
return new self(sprintf('Expected %s to have type "document" (array or object) but found "%s"', $name, get_debug_type($value)));
}









public static function invalidType(string $name, $value, $expectedType)
{
if (is_array($expectedType)) {
$expectedType = self::expectedTypesToString($expectedType);
}

return new self(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value)));
}


private static function expectedTypesToString(array $types): string
{
assert(count($types) > 0);

if (count($types) < 3) {
return implode('" or "', $types);
}

$lastType = array_pop($types);

return sprintf('%s", or "%s', implode('", "', $types), $lastType);
}
}
<?php
















namespace MongoDB\Exception;

use MongoDB\Driver\Exception\UnexpectedValueException as DriverUnexpectedValueException;

class UnexpectedValueException extends DriverUnexpectedValueException implements Exception
{
}
<?php
















namespace MongoDB\Exception;

use BadMethodCallException as BaseBadMethodCallException;

use function sprintf;

class BadMethodCallException extends BaseBadMethodCallException implements Exception
{






public static function classIsImmutable(string $class)
{
return new self(sprintf('%s is immutable', $class));
}







public static function unacknowledgedWriteResultAccess(string $method)
{
return new self(sprintf('%s should not be called for an unacknowledged write result', $method));
}
}
<?php
















namespace MongoDB;

use Iterator;
use MongoDB\BSON\Document;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\CursorId;
use MongoDB\Driver\Exception\ConnectionException;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Exception\ServerException;
use MongoDB\Exception\BadMethodCallException;
use MongoDB\Exception\ResumeTokenException;
use MongoDB\Model\ChangeStreamIterator;
use ReturnTypeWillChange;

use function assert;
use function call_user_func;
use function in_array;










class ChangeStream implements Iterator
{




public const CURSOR_NOT_FOUND = 43;

private const RESUMABLE_ERROR_CODES = [
6, 
7, 
89, 
91, 
189, 
262, 
9001, 
10107, 
11600, 
11602, 
13435, 
13436, 
63, 
150, 
13388, 
234, 
133, 
];

private const WIRE_VERSION_FOR_RESUMABLE_CHANGE_STREAM_ERROR = 9;


private $resumeCallable;

private ChangeStreamIterator $iterator;

private int $key = 0;





private bool $hasAdvanced = false;

private ?DocumentCodec $codec;





#[ReturnTypeWillChange]
public function current()
{
$value = $this->iterator->current();

if (! $this->codec) {
return $value;
}

assert($value instanceof Document);

return $this->codec->decode($value);
}


public function getCursorId()
{
return $this->iterator->getInnerIterator()->getId();
}










public function getResumeToken()
{
return $this->iterator->getResumeToken();
}





#[ReturnTypeWillChange]
public function key()
{
if ($this->valid()) {
return $this->key;
}

return null;
}






#[ReturnTypeWillChange]
public function next()
{
try {
$this->iterator->next();
$this->onIteration($this->hasAdvanced);
} catch (RuntimeException $e) {
$this->resumeOrThrow($e);
}
}






#[ReturnTypeWillChange]
public function rewind()
{
try {
$this->iterator->rewind();



$this->onIteration(false);
} catch (RuntimeException $e) {
$this->resumeOrThrow($e);
}
}





#[ReturnTypeWillChange]
public function valid()
{
return $this->iterator->valid();
}






public function __construct(ChangeStreamIterator $iterator, callable $resumeCallable, ?DocumentCodec $codec = null)
{
$this->iterator = $iterator;
$this->resumeCallable = $resumeCallable;
$this->codec = $codec;

if ($codec) {
$this->iterator->getInnerIterator()->setTypeMap(['root' => 'bson']);
}
}






private function isResumableError(RuntimeException $exception): bool
{
if ($exception instanceof ConnectionException) {
return true;
}

if (! $exception instanceof ServerException) {
return false;
}

if ($exception->getCode() === self::CURSOR_NOT_FOUND) {
return true;
}

if (server_supports_feature($this->iterator->getServer(), self::WIRE_VERSION_FOR_RESUMABLE_CHANGE_STREAM_ERROR)) {
return $exception->hasErrorLabel('ResumableChangeStreamError');
}

return in_array($exception->getCode(), self::RESUMABLE_ERROR_CODES);
}







private function onIteration(bool $incrementKey): void
{






if ((string) $this->getCursorId() === '0') {
$this->resumeCallable = null;
}



if (! $this->valid()) {
return;
}

if ($incrementKey) {
$this->key++;
}

$this->hasAdvanced = true;
}




private function resume(): void
{
if (! $this->resumeCallable) {
throw new BadMethodCallException('Cannot resume a closed change stream.');
}

$this->iterator = call_user_func($this->resumeCallable, $this->getResumeToken(), $this->hasAdvanced);

$this->iterator->rewind();

if ($this->codec) {
$this->iterator->getInnerIterator()->setTypeMap(['root' => 'bson']);
}

$this->onIteration($this->hasAdvanced);
}






private function resumeOrThrow(RuntimeException $exception): void
{
if ($this->isResumableError($exception)) {
$this->resume();

return;
}

throw $exception;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function MongoDB\is_document;







class UpdateSearchIndex implements Executable
{
private string $databaseName;
private string $collectionName;
private string $name;
private object $definition;
private array $options = [];











public function __construct(string $databaseName, string $collectionName, string $name, $definition, array $options = [])
{
if ($name === '') {
throw new InvalidArgumentException('Index name cannot be empty');
}

if (! is_document($definition)) {
throw InvalidArgumentException::expectedDocumentType('$definition', $definition);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->name = $name;
$this->definition = (object) $definition;
$this->options = $options;
}








public function execute(Server $server): void
{
$cmd = [
'updateSearchIndex' => $this->collectionName,
'name' => $this->name,
'definition' => $this->definition,
];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

$server->executeCommand($this->databaseName, new Command($cmd));
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\CommandException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;







class DropSearchIndex implements Executable
{
private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26;

private string $databaseName;
private string $collectionName;
private string $name;
private array $options;










public function __construct(string $databaseName, string $collectionName, string $name, array $options = [])
{
if ($name === '') {
throw new InvalidArgumentException('Index name cannot be empty');
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->name = $name;
$this->options = $options;
}








public function execute(Server $server): void
{
$cmd = [
'dropSearchIndex' => $this->collectionName,
'name' => $this->name,
];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

try {
$server->executeCommand($this->databaseName, new Command($cmd));
} catch (CommandException $e) {

if ($e->getCode() !== self::ERROR_CODE_NAMESPACE_NOT_FOUND) {
throw $e;
}
}
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;

use function current;
use function is_array;








class DropDatabase implements Executable
{
private string $databaseName;

private array $options;





















public function __construct(string $databaseName, array $options = [])
{
if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->options = $options;
}








public function execute(Server $server)
{
$cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}




private function createCommand(): Command
{
$cmd = ['dropDatabase' => 1];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function current;








class FindOne implements Executable, Explainable
{
private Find $find;






































































public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
{
$this->find = new Find(
$databaseName,
$collectionName,
$filter,
['limit' => 1] + $options,
);
}









public function execute(Server $server)
{
$cursor = $this->find->execute($server);
$document = current($cursor->toArray());

return $document === false ? null : $document;
}







public function getCommandDocument()
{
return $this->find->getCommandDocument();
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\InsertManyResult;

use function array_is_list;
use function is_bool;
use function MongoDB\is_document;
use function sprintf;







class InsertMany implements Executable
{
private string $databaseName;

private string $collectionName;


private array $documents;

private array $options;































public function __construct(string $databaseName, string $collectionName, array $documents, array $options = [])
{
$options += ['ordered' => true];

if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (! is_bool($options['ordered'])) {
throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->documents = $this->validateDocuments($documents, $options['codec'] ?? null);
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$bulk = new Bulk($this->createBulkWriteOptions());
$insertedIds = [];

foreach ($this->documents as $i => $document) {
$insertedIds[$i] = $bulk->insert($document);
}

$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());

return new InsertManyResult($writeResult, $insertedIds);
}






private function createBulkWriteOptions(): array
{
$options = ['ordered' => $this->options['ordered']];

foreach (['bypassDocumentValidation', 'comment'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
}

return $options;
}






private function createExecuteOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}





private function validateDocuments(array $documents, ?DocumentCodec $codec): array
{
if (empty($documents)) {
throw new InvalidArgumentException('$documents is empty');
}

if (! array_is_list($documents)) {
throw new InvalidArgumentException('$documents is not a list');
}

foreach ($documents as $i => $document) {
if ($codec) {
$document = $documents[$i] = $codec->encode($document);
}

if (! is_document($document)) {
throw InvalidArgumentException::expectedDocumentType(sprintf('$documents[%d]', $i), $document);
}
}

return $documents;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Command\ListCollections as ListCollectionsCommand;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Model\CollectionInfoCommandIterator;
use MongoDB\Model\CollectionInfoIterator;







class ListCollections implements Executable
{
private string $databaseName;

private ListCollectionsCommand $listCollections;


























public function __construct(string $databaseName, array $options = [])
{
$this->databaseName = $databaseName;
$this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options);
}








public function execute(Server $server)
{
return new CollectionInfoCommandIterator($this->listCollections->execute($server), $this->databaseName);
}
}
<?php
















namespace MongoDB\Operation;

use Iterator;
use MongoDB\BSON\TimestampInterface;
use MongoDB\ChangeStream;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Monitoring\CommandFailedEvent;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\ChangeStreamIterator;

use function array_intersect_key;
use function array_key_exists;
use function array_unshift;
use function count;
use function is_array;
use function is_bool;
use function is_object;
use function is_string;
use function MongoDB\Driver\Monitoring\addSubscriber;
use function MongoDB\Driver\Monitoring\removeSubscriber;
use function MongoDB\is_document;
use function MongoDB\select_server;
use function MongoDB\server_supports_feature;










class Watch implements Executable,  CommandSubscriber
{
public const FULL_DOCUMENT_DEFAULT = 'default';
public const FULL_DOCUMENT_UPDATE_LOOKUP = 'updateLookup';
public const FULL_DOCUMENT_WHEN_AVAILABLE = 'whenAvailable';
public const FULL_DOCUMENT_REQUIRED = 'required';

public const FULL_DOCUMENT_BEFORE_CHANGE_OFF = 'off';
public const FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE = 'whenAvailable';
public const FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED = 'required';

private const WIRE_VERSION_FOR_START_AT_OPERATION_TIME = 7;

private Aggregate $aggregate;

private array $aggregateOptions;

private array $changeStreamOptions;

private ?string $collectionName = null;

private string $databaseName;

private int $firstBatchSize = 0;

private bool $hasResumed = false;

private Manager $manager;

private ?TimestampInterface $operationTime = null;

private array $pipeline;

private ?object $postBatchResumeToken = null;

private ?DocumentCodec $codec;






































































































public function __construct(Manager $manager, ?string $databaseName, ?string $collectionName, array $pipeline, array $options = [])
{
if (isset($collectionName) && ! isset($databaseName)) {
throw new InvalidArgumentException('$collectionName should also be null if $databaseName is null');
}

$options += [
'readPreference' => new ReadPreference(ReadPreference::PRIMARY),
];

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['codec']) && isset($options['typeMap'])) {
throw InvalidArgumentException::cannotCombineCodecAndTypeMap();
}

if (array_key_exists('fullDocument', $options) && ! is_string($options['fullDocument'])) {
throw InvalidArgumentException::invalidType('"fullDocument" option', $options['fullDocument'], 'string');
}

if (isset($options['fullDocumentBeforeChange']) && ! is_string($options['fullDocumentBeforeChange'])) {
throw InvalidArgumentException::invalidType('"fullDocumentBeforeChange" option', $options['fullDocumentBeforeChange'], 'string');
}

if (! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['resumeAfter']) && ! is_document($options['resumeAfter'])) {
throw InvalidArgumentException::expectedDocumentType('"resumeAfter" option', $options['resumeAfter']);
}

if (isset($options['startAfter']) && ! is_document($options['startAfter'])) {
throw InvalidArgumentException::expectedDocumentType('"startAfter" option', $options['startAfter']);
}

if (isset($options['startAtOperationTime']) && ! $options['startAtOperationTime'] instanceof TimestampInterface) {
throw InvalidArgumentException::invalidType('"startAtOperationTime" option', $options['startAtOperationTime'], TimestampInterface::class);
}

if (isset($options['showExpandedEvents']) && ! is_bool($options['showExpandedEvents'])) {
throw InvalidArgumentException::invalidType('"showExpandedEvents" option', $options['showExpandedEvents'], 'bool');
}






if (! isset($options['session'])) {
try {
$options['session'] = $manager->startSession(['causalConsistency' => false]);
} catch (RuntimeException $e) {


}
}

$this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'collation' => 1, 'comment' => 1, 'maxAwaitTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]);
$this->changeStreamOptions = array_intersect_key($options, ['fullDocument' => 1, 'fullDocumentBeforeChange' => 1, 'resumeAfter' => 1, 'showExpandedEvents' => 1, 'startAfter' => 1, 'startAtOperationTime' => 1]);


if ($databaseName === null) {
$databaseName = 'admin';
$this->changeStreamOptions['allChangesForCluster'] = true;
}

$this->manager = $manager;
$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->pipeline = $pipeline;
$this->codec = $options['codec'] ?? null;

$this->aggregate = $this->createAggregate();
}









public function execute(Server $server)
{
return new ChangeStream(
$this->createChangeStreamIterator($server),
fn ($resumeToken, $hasAdvanced): ChangeStreamIterator => $this->resume($resumeToken, $hasAdvanced),
$this->codec,
);
}


final public function commandFailed(CommandFailedEvent $event): void
{
}


final public function commandStarted(CommandStartedEvent $event): void
{
if ($event->getCommandName() !== 'aggregate') {
return;
}

$this->firstBatchSize = 0;
$this->postBatchResumeToken = null;
}


final public function commandSucceeded(CommandSucceededEvent $event): void
{
if ($event->getCommandName() !== 'aggregate') {
return;
}

$reply = $event->getReply();

if (! isset($reply->cursor->firstBatch) || ! is_array($reply->cursor->firstBatch)) {
throw new UnexpectedValueException('aggregate command did not return a "cursor.firstBatch" array');
}

$this->firstBatchSize = count($reply->cursor->firstBatch);

if (isset($reply->cursor->postBatchResumeToken) && is_object($reply->cursor->postBatchResumeToken)) {
$this->postBatchResumeToken = $reply->cursor->postBatchResumeToken;
}

if (
$this->shouldCaptureOperationTime($event->getServer()) &&
isset($reply->operationTime) && $reply->operationTime instanceof TimestampInterface
) {
$this->operationTime = $reply->operationTime;
}
}






private function createAggregate(): Aggregate
{
$pipeline = $this->pipeline;
array_unshift($pipeline, ['$changeStream' => (object) $this->changeStreamOptions]);

return new Aggregate($this->databaseName, $this->collectionName, $pipeline, $this->aggregateOptions);
}




private function createChangeStreamIterator(Server $server): ChangeStreamIterator
{
return new ChangeStreamIterator(
$this->executeAggregate($server),
$this->firstBatchSize,
$this->getInitialResumeToken(),
$this->postBatchResumeToken,
);
}









private function executeAggregate(Server $server)
{
addSubscriber($this);

try {
return $this->aggregate->execute($server);
} finally {
removeSubscriber($this);
}
}







private function getInitialResumeToken()
{
if ($this->firstBatchSize === 0 && isset($this->postBatchResumeToken)) {
return $this->postBatchResumeToken;
}

if (isset($this->changeStreamOptions['startAfter'])) {
return $this->changeStreamOptions['startAfter'];
}

if (isset($this->changeStreamOptions['resumeAfter'])) {
return $this->changeStreamOptions['resumeAfter'];
}

return null;
}








private function resume($resumeToken = null, bool $hasAdvanced = false): ChangeStreamIterator
{
if (isset($resumeToken) && ! is_array($resumeToken) && ! is_object($resumeToken)) {
throw InvalidArgumentException::invalidType('$resumeToken', $resumeToken, 'array or object');
}

$this->hasResumed = true;





$server = select_server($this->manager, $this->aggregateOptions);

$resumeOption = isset($this->changeStreamOptions['startAfter']) && ! $hasAdvanced ? 'startAfter' : 'resumeAfter';

unset($this->changeStreamOptions['resumeAfter']);
unset($this->changeStreamOptions['startAfter']);
unset($this->changeStreamOptions['startAtOperationTime']);

if ($resumeToken !== null) {
$this->changeStreamOptions[$resumeOption] = $resumeToken;
}

if ($resumeToken === null && $this->operationTime !== null) {
$this->changeStreamOptions['startAtOperationTime'] = $this->operationTime;
}


$this->aggregate = $this->createAggregate();

return $this->createChangeStreamIterator($server);
}






private function shouldCaptureOperationTime(Server $server): bool
{
if ($this->hasResumed) {
return false;
}

if (
isset($this->changeStreamOptions['resumeAfter']) ||
isset($this->changeStreamOptions['startAfter']) ||
isset($this->changeStreamOptions['startAtOperationTime'])
) {
return false;
}

if ($this->firstBatchSize > 0) {
return false;
}

if ($this->postBatchResumeToken !== null) {
return false;
}

if (! server_supports_feature($server, self::WIRE_VERSION_FOR_START_AT_OPERATION_TIME)) {
return false;
}

return true;
}
}
<?php
















namespace MongoDB\Operation;

use ArrayIterator;
use MongoDB\BSON\JavascriptInterface;
use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\MapReduceResult;
use stdClass;

use function assert;
use function current;
use function is_array;
use function is_bool;
use function is_integer;
use function is_object;
use function is_string;
use function MongoDB\create_field_path_type_map;
use function MongoDB\document_to_array;
use function MongoDB\is_document;
use function MongoDB\is_mapreduce_output_inline;
use function trigger_error;

use const E_USER_DEPRECATED;








class MapReduce implements Executable
{
private string $databaseName;

private string $collectionName;

private JavascriptInterface $map;

private JavascriptInterface $reduce;


private $out;

private array $options;
























































































public function __construct(string $databaseName, string $collectionName, JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [])
{
if (! is_string($out) && ! is_array($out) && ! is_object($out)) {
throw InvalidArgumentException::invalidType('$out', $out, 'string or array or object');
}

if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['finalize']) && ! $options['finalize'] instanceof JavascriptInterface) {
throw InvalidArgumentException::invalidType('"finalize" option', $options['finalize'], JavascriptInterface::class);
}

if (isset($options['jsMode']) && ! is_bool($options['jsMode'])) {
throw InvalidArgumentException::invalidType('"jsMode" option', $options['jsMode'], 'boolean');
}

if (isset($options['limit']) && ! is_integer($options['limit'])) {
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['query']) && ! is_document($options['query'])) {
throw InvalidArgumentException::expectedDocumentType('"query" option', $options['query']);
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['scope']) && ! is_document($options['scope'])) {
throw InvalidArgumentException::expectedDocumentType('"scope" option', $options['scope']);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['sort']) && ! is_document($options['sort'])) {
throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['verbose']) && ! is_bool($options['verbose'])) {
throw InvalidArgumentException::invalidType('"verbose" option', $options['verbose'], 'boolean');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (isset($options['readConcern']) && $options['readConcern']->isDefault()) {
unset($options['readConcern']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}


if ($map->getScope() !== null) {
@trigger_error('Use of Javascript with scope in "$map" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED);
}

if ($reduce->getScope() !== null) {
@trigger_error('Use of Javascript with scope in "$reduce" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED);
}

if (isset($options['finalize']) && $options['finalize']->getScope() !== null) {
@trigger_error('Use of Javascript with scope in "finalize" option for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED);
}

$this->checkOutDeprecations($out);

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->map = $map;
$this->reduce = $reduce;
$this->out = $out;
$this->options = $options;
}










public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction) {
if (isset($this->options['readConcern'])) {
throw UnsupportedException::readConcernNotSupportedInTransaction();
}

if (isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}
}

$hasOutputCollection = ! is_mapreduce_output_inline($this->out);

$command = $this->createCommand();
$options = $this->createOptions($hasOutputCollection);







$cursor = $hasOutputCollection
? $server->executeReadWriteCommand($this->databaseName, $command, $options)
: $server->executeCommand($this->databaseName, $command, $options);

if (isset($this->options['typeMap']) && ! $hasOutputCollection) {
$cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'results.$'));
}

$result = current($cursor->toArray());
assert($result instanceof stdClass);

$getIterator = $this->createGetIteratorCallable($result, $server);

return new MapReduceResult($getIterator, $result);
}


private function checkOutDeprecations($out): void
{
if (is_string($out)) {
return;
}

$out = document_to_array($out);

if (isset($out['nonAtomic']) && ! $out['nonAtomic']) {
@trigger_error('Specifying false for "out.nonAtomic" is deprecated.', E_USER_DEPRECATED);
}

if (isset($out['sharded']) && ! $out['sharded']) {
@trigger_error('Specifying false for "out.sharded" is deprecated.', E_USER_DEPRECATED);
}
}




private function createCommand(): Command
{
$cmd = [
'mapReduce' => $this->collectionName,
'map' => $this->map,
'reduce' => $this->reduce,
'out' => $this->out,
];

foreach (['bypassDocumentValidation', 'comment', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'verbose'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

foreach (['collation', 'query', 'scope', 'sort'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option];
}
}

return new Command($cmd);
}







private function createGetIteratorCallable(stdClass $result, Server $server): callable
{

if (isset($result->results) && is_array($result->results)) {
$results = $result->results;

return fn () => new ArrayIterator($results);
}

if (isset($result->result) && (is_string($result->result) || is_object($result->result))) {
$options = isset($this->options['typeMap']) ? ['typeMap' => $this->options['typeMap']] : [];

$find = is_string($result->result)
? new Find($this->databaseName, $result->result, [], $options)
: new Find($result->result->db, $result->result->collection, [], $options);

return fn () => $find->execute($server);
}

throw new UnexpectedValueException('mapReduce command did not return inline results or an output collection');
}







private function createOptions(bool $hasOutputCollection): array
{
$options = [];

if (isset($this->options['readConcern'])) {
$options['readConcern'] = $this->options['readConcern'];
}

if (! $hasOutputCollection && isset($this->options['readPreference'])) {
$options['readPreference'] = $this->options['readPreference'];
}

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if ($hasOutputCollection && isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function current;
use function is_array;
use function is_integer;







class DropIndexes implements Executable
{
private string $databaseName;

private string $collectionName;

private string $indexName;

private array $options;


























public function __construct(string $databaseName, string $collectionName, string $indexName, array $options = [])
{
if ($indexName === '') {
throw new InvalidArgumentException('$indexName cannot be empty');
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->indexName = $indexName;
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}




private function createCommand(): Command
{
$cmd = [
'dropIndexes' => $this->collectionName,
'index' => $this->indexName,
];

foreach (['comment', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\UpdateResult;

use function is_array;
use function is_bool;
use function is_object;
use function is_string;
use function MongoDB\is_document;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;
use function MongoDB\is_write_concern_acknowledged;
use function MongoDB\server_supports_feature;










class Update implements Executable, Explainable
{
private const WIRE_VERSION_FOR_HINT = 8;

private string $databaseName;

private string $collectionName;


private $filter;


private $update;

private array $options;

















































public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (! is_array($update) && ! is_object($update)) {
throw InvalidArgumentException::invalidType('$update', $filter, 'array or object');
}

$options += [
'multi' => false,
'upsert' => false,
];

if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) {
throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array');
}

if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']);
}

if (! is_bool($options['multi'])) {
throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean');
}

if ($options['multi'] && ! is_first_key_operator($update) && ! is_pipeline($update)) {
throw new InvalidArgumentException('"multi" option cannot be true unless $update has update operator(s) or non-empty pipeline');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (! is_bool($options['upsert'])) {
throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['let']) && ! is_document($options['let'])) {
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->filter = $filter;
$this->update = $update;
$this->options = $options;
}









public function execute(Server $server)
{


if (
isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) &&
isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT)
) {
throw UnsupportedException::hintNotSupported();
}

$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$bulk = new Bulk($this->createBulkWriteOptions());
$bulk->update($this->filter, $this->update, $this->createUpdateOptions());

$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());

return new UpdateResult($writeResult);
}







public function getCommandDocument()
{
$cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]];

if (isset($this->options['bypassDocumentValidation'])) {
$cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation'];
}

return $cmd;
}






private function createBulkWriteOptions(): array
{
$options = [];

foreach (['bypassDocumentValidation', 'comment'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
}

if (isset($this->options['let'])) {
$options['let'] = (object) $this->options['let'];
}

return $options;
}






private function createExecuteOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}







private function createUpdateOptions(): array
{
$updateOptions = [
'multi' => $this->options['multi'],
'upsert' => $this->options['upsert'],
];

foreach (['arrayFilters', 'hint'] as $option) {
if (isset($this->options[$option])) {
$updateOptions[$option] = $this->options[$option];
}
}

if (isset($this->options['collation'])) {
$updateOptions['collation'] = (object) $this->options['collation'];
}

return $updateOptions;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function array_key_exists;
use function is_array;
use function is_integer;
use function is_object;
use function MongoDB\is_document;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;







class FindOneAndUpdate implements Executable, Explainable
{
public const RETURN_DOCUMENT_BEFORE = 1;
public const RETURN_DOCUMENT_AFTER = 2;

private FindAndModify $findAndModify;
































































public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (! is_array($update) && ! is_object($update)) {
throw InvalidArgumentException::invalidType('$update', $update, 'array or object');
}

if (! is_first_key_operator($update) && ! is_pipeline($update)) {
throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update');
}

if (isset($options['projection']) && ! is_document($options['projection'])) {
throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']);
}

if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) {
throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer');
}

if (
isset($options['returnDocument']) &&
$options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER &&
$options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE
) {
throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']);
}

if (isset($options['projection'])) {
$options['fields'] = $options['projection'];
}

if (isset($options['returnDocument'])) {
$options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER;
}

unset($options['projection'], $options['returnDocument']);

$this->findAndModify = new FindAndModify(
$databaseName,
$collectionName,
['query' => $filter, 'update' => $update] + $options,
);
}









public function execute(Server $server)
{
return $this->findAndModify->execute($server);
}







public function getCommandDocument()
{
return $this->findAndModify->getCommandDocument();
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;

use function is_array;
use function MongoDB\is_document;






class DatabaseCommand implements Executable
{
private string $databaseName;

private Command $command;

private array $options;






















public function __construct(string $databaseName, $command, array $options = [])
{
if (! is_document($command)) {
throw InvalidArgumentException::expectedDocumentType('$command', $command);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

$this->databaseName = $databaseName;
$this->command = $command instanceof Command ? $command : new Command($command);
$this->options = $options;
}







public function execute(Server $server)
{
$cursor = $server->executeCommand($this->databaseName, $this->command, $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return $cursor;
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['readPreference'])) {
$options['readPreference'] = $this->options['readPreference'];
}

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function current;
use function is_array;
use function is_string;
use function MongoDB\server_supports_feature;







class Explain implements Executable
{
public const VERBOSITY_ALL_PLANS = 'allPlansExecution';
public const VERBOSITY_EXEC_STATS = 'executionStats';
public const VERBOSITY_QUERY = 'queryPlanner';

private const WIRE_VERSION_FOR_AGGREGATE = 7;

private string $databaseName;

private Explainable $explainable;

private array $options;
























public function __construct(string $databaseName, Explainable $explainable, array $options = [])
{
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['verbosity']) && ! is_string($options['verbosity'])) {
throw InvalidArgumentException::invalidType('"verbosity" option', $options['verbosity'], 'string');
}

$this->databaseName = $databaseName;
$this->explainable = $explainable;
$this->options = $options;
}









public function execute(Server $server)
{
if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::WIRE_VERSION_FOR_AGGREGATE)) {
throw UnsupportedException::explainNotSupported();
}

$cursor = $server->executeCommand($this->databaseName, $this->createCommand(), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}




private function createCommand(): Command
{
$cmd = ['explain' => $this->explainable->getCommandDocument()];

foreach (['comment', 'verbosity'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['readPreference'])) {
$options['readPreference'] = $this->options['readPreference'];
}

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Command\ListDatabases as ListDatabasesCommand;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Model\DatabaseInfoIterator;
use MongoDB\Model\DatabaseInfoLegacyIterator;







class ListDatabases implements Executable
{
private ListDatabasesCommand $listDatabases;

























public function __construct(array $options = [])
{
$this->listDatabases = new ListDatabasesCommand(['nameOnly' => false] + $options);
}









public function execute(Server $server)
{
return new DatabaseInfoLegacyIterator($this->listDatabases->execute($server));
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\InsertOneResult;

use function is_bool;
use function MongoDB\is_document;







class InsertOne implements Executable
{
private string $databaseName;

private string $collectionName;


private $document;

private array $options;


























public function __construct(string $databaseName, string $collectionName, $document, array $options = [])
{
if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->document = $this->validateDocument($document, $options['codec'] ?? null);
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if (isset($this->options['writeConcern']) && $inTransaction) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$bulk = new Bulk($this->createBulkWriteOptions());

$insertedId = $bulk->insert($this->document);

$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());

return new InsertOneResult($writeResult, $insertedId);
}






private function createBulkWriteOptions(): array
{
$options = [];

foreach (['bypassDocumentValidation', 'comment'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
}

return $options;
}






private function createExecuteOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}





private function validateDocument($document, ?DocumentCodec $codec)
{
if ($codec) {
$document = $codec->encode($document);
}

if (! is_document($document)) {
throw InvalidArgumentException::expectedDocumentType('$document', $document);
}

return $document;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\UpdateResult;

use function is_array;
use function is_object;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;







class UpdateOne implements Executable, Explainable
{
private Update $update;












































public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
{
if (! is_array($update) && ! is_object($update)) {
throw InvalidArgumentException::invalidType('$update', $update, 'array or object');
}

if (! is_first_key_operator($update) && ! is_pipeline($update)) {
throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update');
}

$this->update = new Update(
$databaseName,
$collectionName,
$filter,
$update,
['multi' => false] + $options,
);
}









public function execute(Server $server)
{
return $this->update->execute($server);
}







public function getCommandDocument()
{
return $this->update->getCommandDocument();
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\BSON\Binary;
use MongoDB\BSON\PackedArray;
use MongoDB\BSON\Serializable;
use MongoDB\Driver\ClientEncryption;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function array_key_exists;
use function is_array;
use function is_object;
use function MongoDB\document_to_array;
use function MongoDB\is_document;
use function MongoDB\server_supports_feature;
















class CreateEncryptedCollection implements Executable
{
private const WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2 = 21;

private CreateCollection $createCollection;


private array $createMetadataCollections;

private CreateIndexes $createSafeContentIndex;

private string $databaseName;

private string $collectionName;

private array $options;








public function __construct(string $databaseName, string $collectionName, array $options)
{
if (! isset($options['encryptedFields'])) {
throw new InvalidArgumentException('"encryptedFields" option is required');
}

if (! is_document($options['encryptedFields'])) {
throw InvalidArgumentException::expectedDocumentType('"encryptedFields" option', $options['encryptedFields']);
}

$this->createCollection = new CreateCollection($databaseName, $collectionName, $options);


$encryptedFields = document_to_array($options['encryptedFields']);
$enxcolOptions = ['clusteredIndex' => ['key' => ['_id' => 1], 'unique' => true]];

$this->createMetadataCollections = [
new CreateCollection($databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc', $enxcolOptions),
new CreateCollection($databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc', $enxcolOptions),
];

$this->createSafeContentIndex = new CreateIndexes($databaseName, $collectionName, [['key' => ['__safeContent__' => 1]]]);

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}















public function createDataKeys(ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, ?array &$encryptedFields = null): void
{

$encryptedFields = document_to_array($this->options['encryptedFields']);


if (! isset($encryptedFields['fields'])) {
return;
}


if ($encryptedFields['fields'] instanceof PackedArray) {

$encryptedFields['fields'] = $encryptedFields['fields']->toPHP([
'array' => 'array',
'document' => 'object',
'root' => 'array',
]);
} elseif ($encryptedFields['fields'] instanceof Serializable) {
$encryptedFields['fields'] = $encryptedFields['fields']->bsonSerialize();
}


if (! is_array($encryptedFields['fields'])) {
return;
}

$createDataKeyArgs = [
$kmsProvider,
$masterKey !== null ? ['masterKey' => $masterKey] : [],
];

foreach ($encryptedFields['fields'] as $i => $field) {

if (! is_array($field) && ! is_object($field)) {
continue;
}

$field = document_to_array($field);

if (array_key_exists('keyId', $field) && $field['keyId'] === null) {
$field['keyId'] = $clientEncryption->createDataKey(...$createDataKeyArgs);
$encryptedFields['fields'][$i] = $field;
}
}

$this->options['encryptedFields'] = $encryptedFields;
$this->createCollection = new CreateCollection($this->databaseName, $this->collectionName, $this->options);
}







public function execute(Server $server)
{
if (! server_supports_feature($server, self::WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2)) {
throw new UnsupportedException('Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption.');
}

foreach ($this->createMetadataCollections as $createMetadataCollection) {
$createMetadataCollection->execute($server);
}

$result = $this->createCollection->execute($server);

$this->createSafeContentIndex->execute($server);

return $result;
}
}
<?php

namespace MongoDB\Operation;

use Exception;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Session;
use Throwable;

use function call_user_func;
use function time;


class WithTransaction
{

private $callback;

private array $transactionOptions;







public function __construct(callable $callback, array $transactionOptions = [])
{
$this->callback = $callback;
$this->transactionOptions = $transactionOptions;
}























public function execute(Session $session): void
{
$startTime = time();

while (true) {
$session->startTransaction($this->transactionOptions);

try {
call_user_func($this->callback, $session);
} catch (Throwable $e) {
if ($session->isInTransaction()) {
$session->abortTransaction();
}

if (
$e instanceof RuntimeException &&
$e->hasErrorLabel('TransientTransactionError') &&
! $this->isTransactionTimeLimitExceeded($startTime)
) {
continue;
}

throw $e;
}

if (! $session->isInTransaction()) {

return;
}

while (true) {
try {
$session->commitTransaction();
} catch (RuntimeException $e) {
if (
$e->getCode() !== 50  &&
$e->hasErrorLabel('UnknownTransactionCommitResult') &&
! $this->isTransactionTimeLimitExceeded($startTime)
) {

continue;
}

if (
$e->hasErrorLabel('TransientTransactionError') &&
! $this->isTransactionTimeLimitExceeded($startTime)
) {

continue 2;
}

throw $e;
}


break;
}


break;
}
}






private function isTransactionTimeLimitExceeded(int $startTime): bool
{
return time() - $startTime >= 120;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Server;









interface Executable
{





public function execute(Server $server);
}
<?php
















namespace MongoDB\Operation;

use MongoDB\DeleteResult;
use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function is_array;
use function is_object;
use function is_string;
use function MongoDB\is_document;
use function MongoDB\is_write_concern_acknowledged;
use function MongoDB\server_supports_feature;










class Delete implements Executable, Explainable
{
private const WIRE_VERSION_FOR_HINT = 9;

private string $databaseName;

private string $collectionName;


private $filter;

private int $limit;

private array $options;





































public function __construct(string $databaseName, string $collectionName, $filter, int $limit, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if ($limit !== 0 && $limit !== 1) {
throw new InvalidArgumentException('$limit must be 0 or 1');
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['let']) && ! is_document($options['let'])) {
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->filter = $filter;
$this->limit = $limit;
$this->options = $options;
}









public function execute(Server $server)
{


if (
isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) &&
isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT)
) {
throw UnsupportedException::hintNotSupported();
}

$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$bulk = new Bulk($this->createBulkWriteOptions());
$bulk->delete($this->filter, $this->createDeleteOptions());

$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());

return new DeleteResult($writeResult);
}







public function getCommandDocument()
{
$cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

if (isset($this->options['let'])) {
$cmd['let'] = (object) $this->options['let'];
}

return $cmd;
}






private function createBulkWriteOptions(): array
{
$options = [];

if (isset($this->options['comment'])) {
$options['comment'] = $this->options['comment'];
}

if (isset($this->options['let'])) {
$options['let'] = (object) $this->options['let'];
}

return $options;
}







private function createDeleteOptions(): array
{
$deleteOptions = ['limit' => $this->limit];

if (isset($this->options['collation'])) {
$deleteOptions['collation'] = (object) $this->options['collation'];
}

if (isset($this->options['hint'])) {
$deleteOptions['hint'] = $this->options['hint'];
}

return $deleteOptions;
}






private function createExecuteOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use EmptyIterator;
use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\CommandException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Model\CachingIterator;
use MongoDB\Model\IndexInfoIterator;
use MongoDB\Model\IndexInfoIteratorIterator;

use function is_integer;







class ListIndexes implements Executable
{
private const ERROR_CODE_DATABASE_NOT_FOUND = 60;
private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26;

private string $databaseName;

private string $collectionName;

private array $options;




















public function __construct(string $databaseName, string $collectionName, array $options = [])
{
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}








public function execute(Server $server)
{
return $this->executeCommand($server);
}









private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}







private function executeCommand(Server $server): IndexInfoIteratorIterator
{
$cmd = ['listIndexes' => $this->collectionName];

foreach (['comment', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

try {
$cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions());
} catch (CommandException $e) {




if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND || $e->getCode() === self::ERROR_CODE_DATABASE_NOT_FOUND) {
return new IndexInfoIteratorIterator(new EmptyIterator());
}

throw $e;
}

$cursor->setTypeMap(['root' => 'array', 'document' => 'array']);


$iterator = new CachingIterator($cursor);

return new IndexInfoIteratorIterator($iterator, $this->databaseName . '.' . $this->collectionName);
}
}
<?php
















namespace MongoDB\Operation;

use ArrayIterator;
use Iterator;
use MongoDB\Command\ListDatabases as ListDatabasesCommand;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;

use function array_column;







class ListDatabaseNames implements Executable
{
private ListDatabasesCommand $listDatabases;

























public function __construct(array $options = [])
{
$this->listDatabases = new ListDatabasesCommand(['nameOnly' => true] + $options);
}








public function execute(Server $server): Iterator
{
$result = $this->listDatabases->execute($server);

return new ArrayIterator(array_column($result, 'name'));
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;

use function current;
use function is_array;







class ModifyCollection implements Executable
{
private string $databaseName;

private string $collectionName;

private array $collectionOptions;

private array $options;























public function __construct(string $databaseName, string $collectionName, array $collectionOptions, array $options = [])
{
if (empty($collectionOptions)) {
throw new InvalidArgumentException('$collectionOptions is empty');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->collectionOptions = $collectionOptions;
$this->options = $options;
}








public function execute(Server $server)
{
$cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}

private function createCommand(): Command
{
$cmd = ['collMod' => $this->collectionName] + $this->collectionOptions;

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;







interface Explainable extends Executable
{





public function getCommandDocument();
}
<?php
















namespace MongoDB\Operation;

use Iterator;
use MongoDB\Command\ListCollections as ListCollectionsCommand;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Model\CallbackIterator;







class ListCollectionNames implements Executable
{
private ListCollectionsCommand $listCollections;


























public function __construct(string $databaseName, array $options = [])
{
$this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => true] + $options);
}








public function execute(Server $server): Iterator
{
return new CallbackIterator(
$this->listCollections->execute($server),
fn (array $collectionInfo): string => (string) $collectionInfo['name'],
);
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;

use function array_intersect_key;
use function is_integer;







class EstimatedDocumentCount implements Executable, Explainable
{
private string $databaseName;

private string $collectionName;

private array $options;

























public function __construct(string $databaseName, string $collectionName, array $options = [])
{
$this->databaseName = $databaseName;
$this->collectionName = $collectionName;

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

$this->options = array_intersect_key($options, ['comment' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]);
}










public function execute(Server $server)
{
return $this->createCount()->execute($server);
}







public function getCommandDocument()
{
return $this->createCount()->getCommandDocument();
}

private function createCount(): Count
{
return new Count($this->databaseName, $this->collectionName, [], $this->options);
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\IndexInput;

use function array_is_list;
use function array_map;
use function is_array;
use function is_integer;
use function is_string;
use function MongoDB\server_supports_feature;
use function sprintf;








class CreateIndexes implements Executable
{
private const WIRE_VERSION_FOR_COMMIT_QUORUM = 9;

private string $databaseName;

private string $collectionName;


private array $indexes = [];

private array $options = [];



























public function __construct(string $databaseName, string $collectionName, array $indexes, array $options = [])
{
if (empty($indexes)) {
throw new InvalidArgumentException('$indexes is empty');
}

if (! array_is_list($indexes)) {
throw new InvalidArgumentException('$indexes is not a list');
}

foreach ($indexes as $i => $index) {
if (! is_array($index)) {
throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array');
}

$this->indexes[] = new IndexInput($index);
}

if (isset($options['commitQuorum']) && ! is_string($options['commitQuorum']) && ! is_integer($options['commitQuorum'])) {
throw InvalidArgumentException::invalidType('"commitQuorum" option', $options['commitQuorum'], ['integer', 'string']);
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$this->executeCommand($server);

return array_map(
'strval',
$this->indexes,
);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}







private function executeCommand(Server $server): void
{
$cmd = [
'createIndexes' => $this->collectionName,
'indexes' => $this->indexes,
];

if (isset($this->options['commitQuorum'])) {


if (! server_supports_feature($server, self::WIRE_VERSION_FOR_COMMIT_QUORUM)) {
throw UnsupportedException::commitQuorumNotSupported();
}

$cmd['commitQuorum'] = $this->options['commitQuorum'];
}

foreach (['comment', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

$server->executeWriteCommand($this->databaseName, new Command($cmd), $this->createOptions());
}
}
<?php
















namespace MongoDB\Operation;

use Countable;
use Iterator;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\CachingIterator;

use function array_intersect_key;
use function is_string;







class ListSearchIndexes implements Executable
{
private string $databaseName;
private string $collectionName;
private array $listSearchIndexesOptions;
private array $aggregateOptions;
private Aggregate $aggregate;








public function __construct(string $databaseName, string $collectionName, array $options = [])
{
if (isset($options['name']) && ! is_string($options['name'])) {
throw InvalidArgumentException::invalidType('"name" option', $options['name'], 'string');
}

if (isset($options['name']) && $options['name'] === '') {
throw new InvalidArgumentException('"name" option cannot be empty');
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->listSearchIndexesOptions = array_intersect_key($options, ['name' => 1]);
$this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'codec' => 1, 'collation' => 1, 'comment' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]);

$this->aggregate = $this->createAggregate();
}










public function execute(Server $server): Iterator
{
$cursor = $this->aggregate->execute($server);

return new CachingIterator($cursor);
}

private function createAggregate(): Aggregate
{
$pipeline = [
['$listSearchIndexes' => (object) $this->listSearchIndexesOptions],
];

return new Aggregate($this->databaseName, $this->collectionName, $pipeline, $this->aggregateOptions);
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\BSON\Document;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;

use function array_key_exists;
use function assert;
use function current;
use function is_array;
use function is_bool;
use function is_integer;
use function is_object;
use function is_string;
use function MongoDB\create_field_path_type_map;
use function MongoDB\is_document;
use function MongoDB\is_pipeline;
use function MongoDB\is_write_concern_acknowledged;
use function MongoDB\server_supports_feature;










class FindAndModify implements Executable, Explainable
{
private const WIRE_VERSION_FOR_HINT = 9;

private const WIRE_VERSION_FOR_UNSUPPORTED_OPTION_SERVER_SIDE_ERROR = 8;

private string $databaseName;

private string $collectionName;

private array $options;





































































public function __construct(string $databaseName, string $collectionName, array $options)
{
$options += ['remove' => false];

if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) {
throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array');
}

if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['fields']) && ! is_document($options['fields'])) {
throw InvalidArgumentException::expectedDocumentType('"fields" option', $options['fields']);
}

if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']);
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (array_key_exists('new', $options) && ! is_bool($options['new'])) {
throw InvalidArgumentException::invalidType('"new" option', $options['new'], 'boolean');
}

if (isset($options['query']) && ! is_document($options['query'])) {
throw InvalidArgumentException::expectedDocumentType('"query" option', $options['query']);
}

if (! is_bool($options['remove'])) {
throw InvalidArgumentException::invalidType('"remove" option', $options['remove'], 'boolean');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['sort']) && ! is_document($options['sort'])) {
throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['update']) && ! is_array($options['update']) && ! is_object($options['update'])) {
throw InvalidArgumentException::invalidType('"update" option', $options['update'], 'array or object');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (array_key_exists('upsert', $options) && ! is_bool($options['upsert'])) {
throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean');
}

if (isset($options['let']) && ! is_document($options['let'])) {
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (! (isset($options['update']) xor $options['remove'])) {
throw new InvalidArgumentException('The "remove" option must be true or an "update" document must be specified, but not both');
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

if (isset($options['codec']) && isset($options['typeMap'])) {
throw InvalidArgumentException::cannotCombineCodecAndTypeMap();
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}










public function execute(Server $server)
{


if (isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_UNSUPPORTED_OPTION_SERVER_SIDE_ERROR)) {
throw UnsupportedException::hintNotSupported();
}



if (
isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) &&
isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT)
) {
throw UnsupportedException::hintNotSupported();
}

$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$cursor = $server->executeWriteCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions());

if (isset($this->options['codec'])) {
$cursor->setTypeMap(['root' => 'bson']);
$result = current($cursor->toArray());
assert($result instanceof Document);

$value = $result->get('value');

return $value === null ? $value : $this->options['codec']->decode($value);
}

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'value'));
}

$result = current($cursor->toArray());

return is_object($result) ? ($result->value ?? null) : null;
}







public function getCommandDocument()
{
return $this->createCommandDocument();
}




private function createCommandDocument(): array
{
$cmd = ['findAndModify' => $this->collectionName];

if ($this->options['remove']) {
$cmd['remove'] = true;
} else {
if (isset($this->options['new'])) {
$cmd['new'] = $this->options['new'];
}

if (isset($this->options['upsert'])) {
$cmd['upsert'] = $this->options['upsert'];
}
}

foreach (['collation', 'fields', 'let', 'query', 'sort'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option];
}
}

if (isset($this->options['update'])) {

$update = $this->options['update'];








$cmd['update'] = is_pipeline($update) ? $update : (object) $update;
}

foreach (['arrayFilters', 'bypassDocumentValidation', 'comment', 'hint', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return $cmd;
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use Iterator;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Query;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\CodecCursor;

use function is_array;
use function is_bool;
use function is_integer;
use function is_object;
use function is_string;
use function MongoDB\document_to_array;
use function MongoDB\is_document;
use function trigger_error;

use const E_USER_DEPRECATED;








class Find implements Executable, Explainable
{
public const NON_TAILABLE = 1;
public const TAILABLE = 2;
public const TAILABLE_AWAIT = 3;

private string $databaseName;

private string $collectionName;


private $filter;

private array $options;






































































































public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) {
throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean');
}

if (isset($options['allowPartialResults']) && ! is_bool($options['allowPartialResults'])) {
throw InvalidArgumentException::invalidType('"allowPartialResults" option', $options['allowPartialResults'], 'boolean');
}

if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) {
throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer');
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['cursorType'])) {
if (! is_integer($options['cursorType'])) {
throw InvalidArgumentException::invalidType('"cursorType" option', $options['cursorType'], 'integer');
}

if (
$options['cursorType'] !== self::NON_TAILABLE &&
$options['cursorType'] !== self::TAILABLE &&
$options['cursorType'] !== self::TAILABLE_AWAIT
) {
throw new InvalidArgumentException('Invalid value for "cursorType" option: ' . $options['cursorType']);
}
}

if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
}

if (isset($options['limit']) && ! is_integer($options['limit'])) {
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
}

if (isset($options['max']) && ! is_document($options['max'])) {
throw InvalidArgumentException::expectedDocumentType('"max" option', $options['max']);
}

if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer');
}

if (isset($options['maxScan']) && ! is_integer($options['maxScan'])) {
throw InvalidArgumentException::invalidType('"maxScan" option', $options['maxScan'], 'integer');
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['min']) && ! is_document($options['min'])) {
throw InvalidArgumentException::expectedDocumentType('"min" option', $options['min']);
}

if (isset($options['modifiers']) && ! is_document($options['modifiers'])) {
throw InvalidArgumentException::expectedDocumentType('"modifiers" option', $options['modifiers']);
}

if (isset($options['noCursorTimeout']) && ! is_bool($options['noCursorTimeout'])) {
throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $options['noCursorTimeout'], 'boolean');
}

if (isset($options['oplogReplay']) && ! is_bool($options['oplogReplay'])) {
throw InvalidArgumentException::invalidType('"oplogReplay" option', $options['oplogReplay'], 'boolean');
}

if (isset($options['projection']) && ! is_document($options['projection'])) {
throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']);
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['returnKey']) && ! is_bool($options['returnKey'])) {
throw InvalidArgumentException::invalidType('"returnKey" option', $options['returnKey'], 'boolean');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['showRecordId']) && ! is_bool($options['showRecordId'])) {
throw InvalidArgumentException::invalidType('"showRecordId" option', $options['showRecordId'], 'boolean');
}

if (isset($options['skip']) && ! is_integer($options['skip'])) {
throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer');
}

if (isset($options['snapshot']) && ! is_bool($options['snapshot'])) {
throw InvalidArgumentException::invalidType('"snapshot" option', $options['snapshot'], 'boolean');
}

if (isset($options['sort']) && ! is_document($options['sort'])) {
throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['let']) && ! is_document($options['let'])) {
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
}

if (isset($options['readConcern']) && $options['readConcern']->isDefault()) {
unset($options['readConcern']);
}

if (isset($options['snapshot'])) {
trigger_error('The "snapshot" option is deprecated and will be removed in a future release', E_USER_DEPRECATED);
}

if (isset($options['maxScan'])) {
trigger_error('The "maxScan" option is deprecated and will be removed in a future release', E_USER_DEPRECATED);
}

if (isset($options['codec']) && isset($options['typeMap'])) {
throw InvalidArgumentException::cannotCombineCodecAndTypeMap();
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->filter = $filter;
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['readConcern'])) {
throw UnsupportedException::readConcernNotSupportedInTransaction();
}

$cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, new Query($this->filter, $this->createQueryOptions()), $this->createExecuteOptions());

if (isset($this->options['codec'])) {
return CodecCursor::fromCursor($cursor, $this->options['codec']);
}

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return $cursor;
}







public function getCommandDocument()
{
$cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter];

$options = $this->createQueryOptions();

if (empty($options)) {
return $cmd;
}


unset($options['maxAwaitTimeMS']);

$modifierFallback = [
['allowPartialResults', 'partial'],
['comment', '$comment'],
['hint', '$hint'],
['maxScan', '$maxScan'],
['max', '$max'],
['maxTimeMS', '$maxTimeMS'],
['min', '$min'],
['returnKey', '$returnKey'],
['showRecordId', '$showDiskLoc'],
['sort', '$orderby'],
['snapshot', '$snapshot'],
];

foreach ($modifierFallback as $modifier) {
if (! isset($options[$modifier[0]]) && isset($options['modifiers'][$modifier[1]])) {
$options[$modifier[0]] = $options['modifiers'][$modifier[1]];
}
}

unset($options['modifiers']);

return $cmd + $options;
}






private function createExecuteOptions(): array
{
$options = [];

if (isset($this->options['readPreference'])) {
$options['readPreference'] = $this->options['readPreference'];
}

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}







private function createQueryOptions(): array
{
$options = [];

if (isset($this->options['cursorType'])) {
if ($this->options['cursorType'] === self::TAILABLE) {
$options['tailable'] = true;
}

if ($this->options['cursorType'] === self::TAILABLE_AWAIT) {
$options['tailable'] = true;
$options['awaitData'] = true;
}
}

foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
}

foreach (['collation', 'let', 'max', 'min'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = (object) $this->options[$option];
}
}

if (! empty($this->options['modifiers'])) {

$modifiers = $this->options['modifiers'];
$options['modifiers'] = is_object($modifiers) ? document_to_array($modifiers) : $modifiers;
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\BulkWriteResult;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function array_is_list;
use function array_key_exists;
use function count;
use function current;
use function is_array;
use function is_bool;
use function key;
use function MongoDB\is_document;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;
use function sprintf;






class BulkWrite implements Executable
{
public const DELETE_MANY = 'deleteMany';
public const DELETE_ONE = 'deleteOne';
public const INSERT_ONE = 'insertOne';
public const REPLACE_ONE = 'replaceOne';
public const UPDATE_MANY = 'updateMany';
public const UPDATE_ONE = 'updateOne';

private string $databaseName;

private string $collectionName;


private array $operations;

private array $options;




































































public function __construct(string $databaseName, string $collectionName, array $operations, array $options = [])
{
if (empty($operations)) {
throw new InvalidArgumentException('$operations is empty');
}

if (! array_is_list($operations)) {
throw new InvalidArgumentException('$operations is not a list');
}

$options += ['ordered' => true];

if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (! is_bool($options['ordered'])) {
throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['let']) && ! is_document($options['let'])) {
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->operations = $this->validateOperations($operations, $options['codec'] ?? null);
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$bulk = new Bulk($this->createBulkWriteOptions());
$insertedIds = [];

foreach ($this->operations as $i => $operation) {
$type = key($operation);
$args = current($operation);

switch ($type) {
case self::DELETE_MANY:
case self::DELETE_ONE:
$bulk->delete($args[0], $args[1]);
break;

case self::INSERT_ONE:
$insertedIds[$i] = $bulk->insert($args[0]);
break;

case self::UPDATE_MANY:
case self::UPDATE_ONE:
case self::REPLACE_ONE:
$bulk->update($args[0], $args[1], $args[2]);
break;
}
}

$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());

return new BulkWriteResult($writeResult, $insertedIds);
}






private function createBulkWriteOptions(): array
{
$options = ['ordered' => $this->options['ordered']];

foreach (['bypassDocumentValidation', 'comment'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
}

if (isset($this->options['let'])) {
$options['let'] = (object) $this->options['let'];
}

return $options;
}






private function createExecuteOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}





private function validateOperations(array $operations, ?DocumentCodec $codec): array
{
foreach ($operations as $i => $operation) {
if (! is_array($operation)) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]', $i), $operation, 'array');
}

if (count($operation) !== 1) {
throw new InvalidArgumentException(sprintf('Expected one element in $operation[%d], actually: %d', $i, count($operation)));
}

$type = key($operation);
$args = current($operation);

if (! isset($args[0]) && ! array_key_exists(0, $args)) {
throw new InvalidArgumentException(sprintf('Missing first argument for $operations[%d]["%s"]', $i, $type));
}

if (! is_document($args[0])) {
throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][0]', $i, $type), $args[0]);
}

switch ($type) {
case self::INSERT_ONE:


if ($codec) {
$operations[$i][$type][0] = $codec->encode($args[0]);
}

break;

case self::DELETE_MANY:
case self::DELETE_ONE:
if (! isset($args[1])) {
$args[1] = [];
}

if (! is_array($args[1])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array');
}

$args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0);

if (isset($args[1]['collation']) && ! is_document($args[1]['collation'])) {
throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation']);
}

$operations[$i][$type][1] = $args[1];

break;

case self::REPLACE_ONE:
if (! isset($args[1]) && ! array_key_exists(1, $args)) {
throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type));
}

if ($codec) {
$operations[$i][$type][1] = $codec->encode($args[1]);
}

if (! is_document($args[1])) {
throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1]);
}


if ($args[1] === []) {
$args[1] = (object) $args[1];
}

if (is_first_key_operator($args[1])) {
throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is an update operator', $i, $type));
}

if (is_pipeline($args[1], true )) {
throw new InvalidArgumentException(sprintf('$operations[%d]["%s"][1] is an update pipeline', $i, $type));
}

if (! isset($args[2])) {
$args[2] = [];
}

if (! is_array($args[2])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array');
}

$args[2]['multi'] = false;
$args[2] += ['upsert' => false];

if (isset($args[2]['collation']) && ! is_document($args[2]['collation'])) {
throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation']);
}

if (! is_bool($args[2]['upsert'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean');
}

$operations[$i][$type][2] = $args[2];

break;

case self::UPDATE_MANY:
case self::UPDATE_ONE:
if (! isset($args[1]) && ! array_key_exists(1, $args)) {
throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type));
}

if ((! is_document($args[1]) || ! is_first_key_operator($args[1])) && ! is_pipeline($args[1])) {
throw new InvalidArgumentException(sprintf('Expected update operator(s) or non-empty pipeline for $operations[%d]["%s"][1]', $i, $type));
}

if (! isset($args[2])) {
$args[2] = [];
}

if (! is_array($args[2])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array');
}

$args[2]['multi'] = ($type === self::UPDATE_MANY);
$args[2] += ['upsert' => false];

if (isset($args[2]['arrayFilters']) && ! is_array($args[2]['arrayFilters'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array');
}

if (isset($args[2]['collation']) && ! is_document($args[2]['collation'])) {
throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation']);
}

if (! is_bool($args[2]['upsert'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean');
}

$operations[$i][$type][2] = $args[2];

break;

default:
throw new InvalidArgumentException(sprintf('Unknown operation type "%s" in $operations[%d]', $type, $i));
}
}

return $operations;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function array_key_exists;
use function is_integer;
use function MongoDB\is_document;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;







class FindOneAndReplace implements Executable, Explainable
{
public const RETURN_DOCUMENT_BEFORE = 1;
public const RETURN_DOCUMENT_AFTER = 2;

private FindAndModify $findAndModify;





























































public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['projection']) && ! is_document($options['projection'])) {
throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']);
}

if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) {
throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer');
}

if (
isset($options['returnDocument']) &&
$options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER &&
$options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE
) {
throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']);
}

if (isset($options['projection'])) {
$options['fields'] = $options['projection'];
}

if (isset($options['returnDocument'])) {
$options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER;
}

unset($options['projection'], $options['returnDocument']);

$replacement = $this->validateReplacement($replacement, $options['codec'] ?? null);

$this->findAndModify = new FindAndModify(
$databaseName,
$collectionName,
['query' => $filter, 'update' => $replacement] + $options,
);
}









public function execute(Server $server)
{
return $this->findAndModify->execute($server);
}







public function getCommandDocument()
{
return $this->findAndModify->getCommandDocument();
}





private function validateReplacement($replacement, ?DocumentCodec $codec)
{
if (isset($codec)) {
$replacement = $codec->encode($replacement);
}

if (! is_document($replacement)) {
throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement);
}


if ($replacement === []) {
$replacement = (object) $replacement;
}

if (is_first_key_operator($replacement)) {
throw new InvalidArgumentException('First key in $replacement is an update operator');
}

if (is_pipeline($replacement, true )) {
throw new InvalidArgumentException('$replacement is an update pipeline');
}

return $replacement;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function MongoDB\is_document;







class FindOneAndDelete implements Executable, Explainable
{
private FindAndModify $findAndModify;
















































public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (isset($options['projection']) && ! is_document($options['projection'])) {
throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']);
}

if (isset($options['projection'])) {
$options['fields'] = $options['projection'];
}

unset($options['projection']);

$this->findAndModify = new FindAndModify(
$databaseName,
$collectionName,
['query' => $filter, 'remove' => true] + $options,
);
}









public function execute(Server $server)
{
return $this->findAndModify->execute($server);
}







public function getCommandDocument()
{
return $this->findAndModify->getCommandDocument();
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\DeleteResult;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;







class DeleteOne implements Executable, Explainable
{
private Delete $delete;


































public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
{
$this->delete = new Delete($databaseName, $collectionName, $filter, 1, $options);
}









public function execute(Server $server)
{
return $this->delete->execute($server);
}







public function getCommandDocument()
{
return $this->delete->getCommandDocument();
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;

use function current;
use function is_array;
use function is_bool;
use function is_integer;
use function is_string;
use function MongoDB\is_document;
use function MongoDB\is_pipeline;
use function trigger_error;

use const E_USER_DEPRECATED;







class CreateCollection implements Executable
{
public const USE_POWER_OF_2_SIZES = 1;
public const NO_PADDING = 2;

private string $databaseName;

private string $collectionName;

private array $options = [];
























































































public function __construct(string $databaseName, string $collectionName, array $options = [])
{
if (isset($options['autoIndexId']) && ! is_bool($options['autoIndexId'])) {
throw InvalidArgumentException::invalidType('"autoIndexId" option', $options['autoIndexId'], 'boolean');
}

if (isset($options['capped']) && ! is_bool($options['capped'])) {
throw InvalidArgumentException::invalidType('"capped" option', $options['capped'], 'boolean');
}

if (isset($options['changeStreamPreAndPostImages']) && ! is_document($options['changeStreamPreAndPostImages'])) {
throw InvalidArgumentException::expectedDocumentType('"changeStreamPreAndPostImages" option', $options['changeStreamPreAndPostImages']);
}

if (isset($options['clusteredIndex']) && ! is_document($options['clusteredIndex'])) {
throw InvalidArgumentException::expectedDocumentType('"clusteredIndex" option', $options['clusteredIndex']);
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['encryptedFields']) && ! is_document($options['encryptedFields'])) {
throw InvalidArgumentException::expectedDocumentType('"encryptedFields" option', $options['encryptedFields']);
}

if (isset($options['expireAfterSeconds']) && ! is_integer($options['expireAfterSeconds'])) {
throw InvalidArgumentException::invalidType('"expireAfterSeconds" option', $options['expireAfterSeconds'], 'integer');
}

if (isset($options['flags']) && ! is_integer($options['flags'])) {
throw InvalidArgumentException::invalidType('"flags" option', $options['flags'], 'integer');
}

if (isset($options['indexOptionDefaults']) && ! is_document($options['indexOptionDefaults'])) {
throw InvalidArgumentException::expectedDocumentType('"indexOptionDefaults" option', $options['indexOptionDefaults']);
}

if (isset($options['max']) && ! is_integer($options['max'])) {
throw InvalidArgumentException::invalidType('"max" option', $options['max'], 'integer');
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['pipeline']) && ! is_array($options['pipeline'])) {
throw InvalidArgumentException::invalidType('"pipeline" option', $options['pipeline'], 'array');
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['size']) && ! is_integer($options['size'])) {
throw InvalidArgumentException::invalidType('"size" option', $options['size'], 'integer');
}

if (isset($options['storageEngine']) && ! is_document($options['storageEngine'])) {
throw InvalidArgumentException::expectedDocumentType('"storageEngine" option', $options['storageEngine']);
}

if (isset($options['timeseries']) && ! is_document($options['timeseries'])) {
throw InvalidArgumentException::expectedDocumentType('"timeseries" option', $options['timeseries']);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['validationAction']) && ! is_string($options['validationAction'])) {
throw InvalidArgumentException::invalidType('"validationAction" option', $options['validationAction'], 'string');
}

if (isset($options['validationLevel']) && ! is_string($options['validationLevel'])) {
throw InvalidArgumentException::invalidType('"validationLevel" option', $options['validationLevel'], 'string');
}

if (isset($options['validator']) && ! is_document($options['validator'])) {
throw InvalidArgumentException::expectedDocumentType('"validator" option', $options['validator']);
}

if (isset($options['viewOn']) && ! is_string($options['viewOn'])) {
throw InvalidArgumentException::invalidType('"viewOn" option', $options['viewOn'], 'string');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

if (isset($options['autoIndexId'])) {
trigger_error('The "autoIndexId" option is deprecated and will be removed in a future release', E_USER_DEPRECATED);
}

if (isset($options['pipeline']) && ! is_pipeline($options['pipeline'], true )) {
throw new InvalidArgumentException('"pipeline" option is not a valid aggregation pipeline');
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}








public function execute(Server $server)
{
$cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}




private function createCommand(): Command
{
$cmd = ['create' => $this->collectionName];

foreach (['autoIndexId', 'capped', 'comment', 'expireAfterSeconds', 'flags', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

foreach (['changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'encryptedFields', 'indexOptionDefaults', 'storageEngine', 'timeseries', 'validator'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option];
}
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\UpdateResult;

use function is_array;
use function is_object;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;







class UpdateMany implements Executable, Explainable
{
private Update $update;












































public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
{
if (! is_array($update) && ! is_object($update)) {
throw InvalidArgumentException::invalidType('$update', $update, 'array or object');
}

if (! is_first_key_operator($update) && ! is_pipeline($update)) {
throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update');
}

$this->update = new Update(
$databaseName,
$collectionName,
$filter,
$update,
['multi' => true] + $options,
);
}









public function execute(Server $server)
{
return $this->update->execute($server);
}







public function getCommandDocument()
{
return $this->update->getCommandDocument();
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function current;
use function is_array;
use function is_bool;








class RenameCollection implements Executable
{
private string $fromNamespace;

private string $toNamespace;

private array $options;



























public function __construct(string $fromDatabaseName, string $fromCollectionName, string $toDatabaseName, string $toCollectionName, array $options = [])
{
if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

if (isset($options['dropTarget']) && ! is_bool($options['dropTarget'])) {
throw InvalidArgumentException::invalidType('"dropTarget" option', $options['dropTarget'], 'boolean');
}

$this->fromNamespace = $fromDatabaseName . '.' . $fromCollectionName;
$this->toNamespace = $toDatabaseName . '.' . $toCollectionName;
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

$cursor = $server->executeWriteCommand('admin', $this->createCommand(), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}




private function createCommand(): Command
{
$cmd = [
'renameCollection' => $this->fromNamespace,
'to' => $this->toNamespace,
];

foreach (['comment', 'dropTarget'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\SearchIndexInput;

use function array_column;
use function array_is_list;
use function current;
use function is_array;
use function sprintf;








class CreateSearchIndexes implements Executable
{
private string $databaseName;
private string $collectionName;
private array $indexes = [];
private array $options;










public function __construct(string $databaseName, string $collectionName, array $indexes, array $options)
{
if (! array_is_list($indexes)) {
throw new InvalidArgumentException('$indexes is not a list');
}

foreach ($indexes as $i => $index) {
if (! is_array($index)) {
throw InvalidArgumentException::invalidType(sprintf('$indexes[%d]', $i), $index, 'array');
}

$this->indexes[] = new SearchIndexInput($index);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}









public function execute(Server $server): array
{
$cmd = [
'createSearchIndexes' => $this->collectionName,
'indexes' => $this->indexes,
];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

$cursor = $server->executeCommand($this->databaseName, new Command($cmd));


$result = current($cursor->toArray());

return array_column($result->indexesCreated, 'name');
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;

use function MongoDB\document_to_array;
use function MongoDB\is_document;














class DropEncryptedCollection implements Executable
{
private DropCollection $dropCollection;


private array $dropMetadataCollections;

















public function __construct(string $databaseName, string $collectionName, array $options)
{
if (! isset($options['encryptedFields'])) {
throw new InvalidArgumentException('"encryptedFields" option is required');
}

if (! is_document($options['encryptedFields'])) {
throw InvalidArgumentException::expectedDocumentType('"encryptedFields" option', $options['encryptedFields']);
}


$encryptedFields = document_to_array($options['encryptedFields']);

$this->dropMetadataCollections = [
new DropCollection($databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'),
new DropCollection($databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'),
];


unset($options['encryptedFields']);

$this->dropCollection = new DropCollection($databaseName, $collectionName, $options);
}






public function execute(Server $server)
{
foreach ($this->dropMetadataCollections as $dropMetadataCollection) {
$dropMetadataCollection->execute($server);
}

return $this->dropCollection->execute($server);
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;

use function array_intersect_key;
use function count;
use function current;
use function is_float;
use function is_integer;
use function is_object;
use function MongoDB\is_document;







class CountDocuments implements Executable
{
private string $databaseName;

private string $collectionName;


private $filter;

private array $aggregateOptions;

private array $countOptions;

private Aggregate $aggregate;




































public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (isset($options['limit']) && ! is_integer($options['limit'])) {
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
}

if (isset($options['skip']) && ! is_integer($options['skip'])) {
throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer');
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->filter = $filter;

$this->aggregateOptions = array_intersect_key($options, ['collation' => 1, 'comment' => 1, 'hint' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]);
$this->countOptions = array_intersect_key($options, ['limit' => 1, 'skip' => 1]);

$this->aggregate = $this->createAggregate();
}










public function execute(Server $server)
{
$cursor = $this->aggregate->execute($server);

$allResults = $cursor->toArray();



if (count($allResults) == 0) {
return 0;
}

$result = current($allResults);
if (! is_object($result) || ! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) {
throw new UnexpectedValueException('count command did not return a numeric "n" value');
}

return (integer) $result->n;
}

private function createAggregate(): Aggregate
{
$pipeline = [
['$match' => (object) $this->filter],
];

if (isset($this->countOptions['skip'])) {
$pipeline[] = ['$skip' => $this->countOptions['skip']];
}

if (isset($this->countOptions['limit'])) {
$pipeline[] = ['$limit' => $this->countOptions['limit']];
}

$pipeline[] = ['$group' => ['_id' => 1, 'n' => ['$sum' => 1]]];

return new Aggregate($this->databaseName, $this->collectionName, $pipeline, $this->aggregateOptions);
}
}
<?php
















namespace MongoDB\Operation;

use Iterator;
use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\CodecCursor;
use stdClass;

use function is_array;
use function is_bool;
use function is_integer;
use function is_object;
use function is_string;
use function MongoDB\is_document;
use function MongoDB\is_last_pipeline_operator_write;
use function MongoDB\is_pipeline;







class Aggregate implements Executable, Explainable
{
private string $databaseName;

private ?string $collectionName = null;

private array $pipeline;

private array $options;

private bool $isWrite;


































































public function __construct(string $databaseName, ?string $collectionName, array $pipeline, array $options = [])
{
if (! is_pipeline($pipeline, true )) {
throw new InvalidArgumentException('$pipeline is not a valid aggregation pipeline');
}

if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) {
throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean');
}

if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) {
throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer');
}

if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}

if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['explain']) && ! is_bool($options['explain'])) {
throw InvalidArgumentException::invalidType('"explain" option', $options['explain'], 'boolean');
}

if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
}

if (isset($options['let']) && ! is_document($options['let'])) {
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
}

if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer');
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
unset($options['bypassDocumentValidation']);
}

if (isset($options['readConcern']) && $options['readConcern']->isDefault()) {
unset($options['readConcern']);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

if (isset($options['codec']) && isset($options['typeMap'])) {
throw InvalidArgumentException::cannotCombineCodecAndTypeMap();
}

$this->isWrite = is_last_pipeline_operator_write($pipeline) && ! ($options['explain'] ?? false);

if ($this->isWrite) {


unset($options['batchSize']);
} else {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->pipeline = $pipeline;
$this->options = $options;
}










public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction) {
if (isset($this->options['readConcern'])) {
throw UnsupportedException::readConcernNotSupportedInTransaction();
}

if (isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}
}

$command = new Command(
$this->createCommandDocument(),
$this->createCommandOptions(),
);

$cursor = $this->executeCommand($server, $command);

if (isset($this->options['codec'])) {
return CodecCursor::fromCursor($cursor, $this->options['codec']);
}

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return $cursor;
}







public function getCommandDocument()
{
$cmd = $this->createCommandDocument();


if (isset($this->options['readConcern'])) {
$cmd['readConcern'] = $this->options['readConcern'];
}

return $cmd;
}




private function createCommandDocument(): array
{
$cmd = [
'aggregate' => $this->collectionName ?? 1,
'pipeline' => $this->pipeline,
];

foreach (['allowDiskUse', 'bypassDocumentValidation', 'comment', 'explain', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

foreach (['collation', 'let'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option];
}
}

if (isset($this->options['hint'])) {
$cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint'];
}

$cmd['cursor'] = isset($this->options['batchSize'])
? ['batchSize' => $this->options['batchSize']]
: new stdClass();

return $cmd;
}

private function createCommandOptions(): array
{
$cmdOptions = [];

if (isset($this->options['maxAwaitTimeMS'])) {
$cmdOptions['maxAwaitTimeMS'] = $this->options['maxAwaitTimeMS'];
}

return $cmdOptions;
}








private function executeCommand(Server $server, Command $command): Cursor
{
$options = [];

foreach (['readConcern', 'readPreference', 'session', 'writeConcern'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
}

if (! $this->isWrite) {
return $server->executeReadCommand($this->databaseName, $command, $options);
}





if (isset($options['readPreference'])) {
return $server->executeCommand($this->databaseName, $command, $options);
}

return $server->executeReadWriteCommand($this->databaseName, $command, $options);
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;

use function current;
use function is_array;
use function is_float;
use function is_integer;
use function is_object;
use function is_string;
use function MongoDB\is_document;







class Count implements Executable, Explainable
{
private string $databaseName;

private string $collectionName;


private $filter;

private array $options;




































public function __construct(string $databaseName, string $collectionName, $filter = [], array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
}

if (isset($options['limit']) && ! is_integer($options['limit'])) {
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['skip']) && ! is_integer($options['skip'])) {
throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer');
}

if (isset($options['readConcern']) && $options['readConcern']->isDefault()) {
unset($options['readConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->filter = $filter;
$this->options = $options;
}










public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['readConcern'])) {
throw UnsupportedException::readConcernNotSupportedInTransaction();
}

$cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions());
$result = current($cursor->toArray());


if (! is_object($result) || ! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) {
throw new UnexpectedValueException('count command did not return a numeric "n" value');
}

return (integer) $result->n;
}







public function getCommandDocument()
{
$cmd = $this->createCommandDocument();


if (isset($this->options['readConcern'])) {
$cmd['readConcern'] = $this->options['readConcern'];
}

return $cmd;
}




private function createCommandDocument(): array
{
$cmd = ['count' => $this->collectionName];

if (! empty($this->filter)) {
$cmd['query'] = (object) $this->filter;
}

if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}

if (isset($this->options['hint'])) {
$cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint'];
}

foreach (['comment', 'limit', 'maxTimeMS', 'skip'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return $cmd;
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['readConcern'])) {
$options['readConcern'] = $this->options['readConcern'];
}

if (isset($this->options['readPreference'])) {
$options['readPreference'] = $this->options['readPreference'];
}

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;

use function current;
use function is_array;
use function is_integer;
use function is_object;
use function MongoDB\create_field_path_type_map;
use function MongoDB\is_document;







class Distinct implements Executable, Explainable
{
private string $databaseName;

private string $collectionName;

private string $fieldName;


private $filter;

private array $options;






























public function __construct(string $databaseName, string $collectionName, string $fieldName, $filter = [], array $options = [])
{
if (! is_document($filter)) {
throw InvalidArgumentException::expectedDocumentType('$filter', $filter);
}

if (isset($options['collation']) && ! is_document($options['collation'])) {
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
}

if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}

if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
}

if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
}

if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['readConcern']) && $options['readConcern']->isDefault()) {
unset($options['readConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->fieldName = $fieldName;
$this->filter = $filter;
$this->options = $options;
}










public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['readConcern'])) {
throw UnsupportedException::readConcernNotSupportedInTransaction();
}

$cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions());

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'values.$'));
}

$result = current($cursor->toArray());

if (! is_object($result) || ! isset($result->values) || ! is_array($result->values)) {
throw new UnexpectedValueException('distinct command did not return a "values" array');
}

return $result->values;
}







public function getCommandDocument()
{
$cmd = $this->createCommandDocument();


if (isset($this->options['readConcern'])) {
$cmd['readConcern'] = $this->options['readConcern'];
}

return $cmd;
}




private function createCommandDocument(): array
{
$cmd = [
'distinct' => $this->collectionName,
'key' => $this->fieldName,
];

if (! empty($this->filter)) {
$cmd['query'] = (object) $this->filter;
}

if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}

foreach (['comment', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
}

return $cmd;
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['readConcern'])) {
$options['readConcern'] = $this->options['readConcern'];
}

if (isset($this->options['readPreference'])) {
$options['readPreference'] = $this->options['readPreference'];
}

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Codec\DocumentCodec;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\UpdateResult;

use function MongoDB\is_document;
use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline;







class ReplaceOne implements Executable
{
private Update $update;












































public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = [])
{
if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
}

if (isset($options['codec'], $options['typeMap'])) {
throw InvalidArgumentException::cannotCombineCodecAndTypeMap();
}

$this->update = new Update(
$databaseName,
$collectionName,
$filter,
$this->validateReplacement($replacement, $options['codec'] ?? null),
['multi' => false] + $options,
);
}









public function execute(Server $server)
{
return $this->update->execute($server);
}





private function validateReplacement($replacement, ?DocumentCodec $codec)
{
if ($codec) {
$replacement = $codec->encode($replacement);
}

if (! is_document($replacement)) {
throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement);
}


if ($replacement === []) {
$replacement = (object) $replacement;
}

if (is_first_key_operator($replacement)) {
throw new InvalidArgumentException('First key in $replacement is an update operator');
}

if (is_pipeline($replacement, true )) {
throw new InvalidArgumentException('$replacement is an update pipeline');
}

return $replacement;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\CommandException;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;

use function current;
use function is_array;








class DropCollection implements Executable
{
private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26;

private string $databaseName;

private string $collectionName;

private array $options;






















public function __construct(string $databaseName, string $collectionName, array $options = [])
{
if (isset($options['session']) && ! $options['session'] instanceof Session) {
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
}

if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
unset($options['writeConcern']);
}

$this->databaseName = $databaseName;
$this->collectionName = $collectionName;
$this->options = $options;
}









public function execute(Server $server)
{
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction();
}

try {
$cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions());
} catch (CommandException $e) {



if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND) {
return $e->getResultDocument();
}

throw $e;
}

if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}

return current($cursor->toArray());
}




private function createCommand(): Command
{
$cmd = ['drop' => $this->collectionName];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
}

return new Command($cmd);
}






private function createOptions(): array
{
$options = [];

if (isset($this->options['session'])) {
$options['session'] = $this->options['session'];
}

if (isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}

return $options;
}
}
<?php
















namespace MongoDB\Operation;

use MongoDB\DeleteResult;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;







class DeleteMany implements Executable, Explainable
{
private Delete $delete;


































public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
{
$this->delete = new Delete($databaseName, $collectionName, $filter, 0, $options);
}









public function execute(Server $server)
{
return $this->delete->execute($server);
}







public function getCommandDocument()
{
return $this->delete->getCommandDocument();
}
}
<?php

use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector;
use Rector\Php70\Rector\StmtsAwareInterface\IfIssetToCoalescingRector;
use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector;
use Rector\Set\ValueObject\LevelSetList;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/examples',
__DIR__ . '/src',
__DIR__ . '/tests',
__DIR__ . '/tools',
]);


$rectorConfig->sets([LevelSetList::UP_TO_PHP_74]);


$rectorConfig->skip([

IfIssetToCoalescingRector::class,

JsonThrowOnErrorRector::class => [
__DIR__ . '/tests/DocumentationExamplesTest.php',
],
]);



$rectorConfig->ruleWithConfiguration(RemoveAnnotationRector::class, ['api']);
};
<?php



if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}

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

return ComposerAutoloaderInit6df945c87d8c91a0cdc33b5e2833ee2d::getLoader();
<?php










namespace Psy;







class Context
{
private static $specialNames = ['_', '_e', '__out', '__psysh__', 'this'];



private static $commandScopeNames = [
'__function', '__method', '__class', '__namespace', '__file', '__line', '__dir',
];

private $scopeVariables = [];
private $commandScopeVariables = [];
private $returnValue;
private $lastException;
private $lastStdout;
private $boundObject;
private $boundClass;










public function get(string $name)
{
switch ($name) {
case '_':
return $this->returnValue;

case '_e':
if (isset($this->lastException)) {
return $this->lastException;
}
break;

case '__out':
if (isset($this->lastStdout)) {
return $this->lastStdout;
}
break;

case 'this':
if (isset($this->boundObject)) {
return $this->boundObject;
}
break;

case '__function':
case '__method':
case '__class':
case '__namespace':
case '__file':
case '__line':
case '__dir':
if (\array_key_exists($name, $this->commandScopeVariables)) {
return $this->commandScopeVariables[$name];
}
break;

default:
if (\array_key_exists($name, $this->scopeVariables)) {
return $this->scopeVariables[$name];
}
break;
}

throw new \InvalidArgumentException('Unknown variable: $'.$name);
}




public function getAll(): array
{
return \array_merge($this->scopeVariables, $this->getSpecialVariables());
}




public function getSpecialVariables(): array
{
$vars = [
'_' => $this->returnValue,
];

if (isset($this->lastException)) {
$vars['_e'] = $this->lastException;
}

if (isset($this->lastStdout)) {
$vars['__out'] = $this->lastStdout;
}

if (isset($this->boundObject)) {
$vars['this'] = $this->boundObject;
}

return \array_merge($vars, $this->commandScopeVariables);
}









public function setAll(array $vars)
{
foreach (self::$specialNames as $key) {
unset($vars[$key]);
}

foreach (self::$commandScopeNames as $key) {
unset($vars[$key]);
}

$this->scopeVariables = $vars;
}






public function setReturnValue($value)
{
$this->returnValue = $value;
}






public function getReturnValue()
{
return $this->returnValue;
}






public function setLastException(\Throwable $e)
{
$this->lastException = $e;
}








public function getLastException()
{
if (!isset($this->lastException)) {
throw new \InvalidArgumentException('No most-recent exception');
}

return $this->lastException;
}






public function setLastStdout(string $lastStdout)
{
$this->lastStdout = $lastStdout;
}








public function getLastStdout()
{
if (!isset($this->lastStdout)) {
throw new \InvalidArgumentException('No most-recent output');
}

return $this->lastStdout;
}








public function setBoundObject($boundObject)
{
$this->boundObject = \is_object($boundObject) ? $boundObject : null;
$this->boundClass = null;
}






public function getBoundObject()
{
return $this->boundObject;
}








public function setBoundClass($boundClass)
{
$this->boundClass = (\is_string($boundClass) && $boundClass !== '') ? $boundClass : null;
$this->boundObject = null;
}






public function getBoundClass()
{
return $this->boundClass;
}






public function setCommandScopeVariables(array $commandScopeVariables)
{
$vars = [];
foreach ($commandScopeVariables as $key => $value) {

if (\is_scalar($value) && \in_array($key, self::$commandScopeNames)) {
$vars[$key] = $value;
}
}

$this->commandScopeVariables = $vars;
}




public function getCommandScopeVariables(): array
{
return $this->commandScopeVariables;
}









public function getUnusedCommandScopeVariableNames(): array
{
return \array_diff(self::$commandScopeNames, \array_keys($this->commandScopeVariables));
}






public static function isSpecialVariableName(string $name): bool
{
return \in_array($name, self::$specialNames) || \in_array($name, self::$commandScopeNames);
}
}
<?php










namespace Psy;




class SystemEnv implements EnvInterface
{





public function get(string $key)
{
if (isset($_SERVER[$key]) && $_SERVER[$key]) {
return $_SERVER[$key];
}

$result = \getenv($key);

return $result === false ? null : $result;
}
}
<?php










namespace Psy\Sudo;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Scalar\String_;
use PhpParser\NodeVisitorAbstract;
use Psy\Sudo;







class SudoVisitor extends NodeVisitorAbstract
{
const PROPERTY_FETCH = 'fetchProperty';
const PROPERTY_ASSIGN = 'assignProperty';
const METHOD_CALL = 'callMethod';
const STATIC_PROPERTY_FETCH = 'fetchStaticProperty';
const STATIC_PROPERTY_ASSIGN = 'assignStaticProperty';
const STATIC_CALL = 'callStatic';
const CLASS_CONST_FETCH = 'fetchClassConst';
const NEW_INSTANCE = 'newInstance';






public function enterNode(Node $node)
{
if ($node instanceof PropertyFetch) {
$name = $node->name instanceof Identifier ? $node->name->toString() : $node->name;
$args = [
$node->var,
\is_string($name) ? new String_($name) : $name,
];

return $this->prepareCall(self::PROPERTY_FETCH, $args);
} elseif ($node instanceof Assign && $node->var instanceof PropertyFetch) {
$target = $node->var;
$name = $target->name instanceof Identifier ? $target->name->toString() : $target->name;
$args = [
$target->var,
\is_string($name) ? new String_($name) : $name,
$node->expr,
];

return $this->prepareCall(self::PROPERTY_ASSIGN, $args);
} elseif ($node instanceof MethodCall) {
$name = $node->name instanceof Identifier ? $node->name->toString() : $node->name;
$args = $node->args;
\array_unshift($args, new Arg(\is_string($name) ? new String_($name) : $name));
\array_unshift($args, new Arg($node->var));


return new StaticCall(new FullyQualifiedName(Sudo::class), self::METHOD_CALL, $args);
} elseif ($node instanceof StaticPropertyFetch) {
$class = $node->class instanceof Name ? $node->class->toString() : $node->class;
$name = $node->name instanceof Identifier ? $node->name->toString() : $node->name;
$args = [
\is_string($class) ? new String_($class) : $class,
\is_string($name) ? new String_($name) : $name,
];

return $this->prepareCall(self::STATIC_PROPERTY_FETCH, $args);
} elseif ($node instanceof Assign && $node->var instanceof StaticPropertyFetch) {
$target = $node->var;
$class = $target->class instanceof Name ? $target->class->toString() : $target->class;
$name = $target->name instanceof Identifier ? $target->name->toString() : $target->name;
$args = [
\is_string($class) ? new String_($class) : $class,
\is_string($name) ? new String_($name) : $name,
$node->expr,
];

return $this->prepareCall(self::STATIC_PROPERTY_ASSIGN, $args);
} elseif ($node instanceof StaticCall) {
$args = $node->args;
$class = $node->class instanceof Name ? $node->class->toString() : $node->class;
$name = $node->name instanceof Identifier ? $node->name->toString() : $node->name;
\array_unshift($args, new Arg(\is_string($name) ? new String_($name) : $name));
\array_unshift($args, new Arg(\is_string($class) ? new String_($class) : $class));


return new StaticCall(new FullyQualifiedName(Sudo::class), self::STATIC_CALL, $args);
} elseif ($node instanceof ClassConstFetch) {
$class = $node->class instanceof Name ? $node->class->toString() : $node->class;
$name = $node->name instanceof Identifier ? $node->name->toString() : $node->name;
$args = [
\is_string($class) ? new String_($class) : $class,
\is_string($name) ? new String_($name) : $name,
];

return $this->prepareCall(self::CLASS_CONST_FETCH, $args);
} elseif ($node instanceof New_) {
$args = $node->args;
$class = $node->class instanceof Name ? $node->class->toString() : $node->class;
\array_unshift($args, new Arg(\is_string($class) ? new String_($class) : $class));


return new StaticCall(new FullyQualifiedName(Sudo::class), self::NEW_INSTANCE, $args);
}
}

private function prepareCall(string $method, array $args): StaticCall
{
return new StaticCall(new FullyQualifiedName(Sudo::class), $method, \array_map(function ($arg) {
return new Arg($arg);
}, $args));
}
}
<?php










namespace Psy;




class ExecutionClosure
{
const NOOP_INPUT = 'return null;';

private $closure;




public function __construct(Shell $__psysh__)
{
$this->setClosure($__psysh__, function () use ($__psysh__) {
try {

\extract($__psysh__->getScopeVariables(false));


\ob_start([$__psysh__, 'writeStdout'], 1);


\set_error_handler([$__psysh__, 'handleError']);


$_ = eval($__psysh__->onExecute($__psysh__->flushCode() ?: self::NOOP_INPUT));
} catch (\Throwable $_e) {

if (\ob_get_level() > 0) {
\ob_end_clean();
}

throw $_e;
} finally {

\restore_error_handler();
}


\ob_end_flush();


$__psysh__->setScopeVariables(\get_defined_vars());

return $_;
});
}







protected function setClosure(Shell $shell, \Closure $closure)
{
$that = $shell->getBoundObject();

if (\is_object($that)) {
$this->closure = $closure->bindTo($that, \get_class($that));
} else {
$this->closure = $closure->bindTo(null, $shell->getBoundClass());
}
}






public function execute()
{
$closure = $this->closure;

return $closure();
}
}
<?php










namespace Psy;

use PhpParser\NodeTraverser;
use PhpParser\Parser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\CodeCleaner\AbstractClassPass;
use Psy\CodeCleaner\AssignThisVariablePass;
use Psy\CodeCleaner\CalledClassPass;
use Psy\CodeCleaner\CallTimePassByReferencePass;
use Psy\CodeCleaner\CodeCleanerPass;
use Psy\CodeCleaner\EmptyArrayDimFetchPass;
use Psy\CodeCleaner\ExitPass;
use Psy\CodeCleaner\FinalClassPass;
use Psy\CodeCleaner\FunctionContextPass;
use Psy\CodeCleaner\FunctionReturnInWriteContextPass;
use Psy\CodeCleaner\ImplicitReturnPass;
use Psy\CodeCleaner\IssetPass;
use Psy\CodeCleaner\LabelContextPass;
use Psy\CodeCleaner\LeavePsyshAlonePass;
use Psy\CodeCleaner\ListPass;
use Psy\CodeCleaner\LoopContextPass;
use Psy\CodeCleaner\MagicConstantsPass;
use Psy\CodeCleaner\NamespacePass;
use Psy\CodeCleaner\PassableByReferencePass;
use Psy\CodeCleaner\RequirePass;
use Psy\CodeCleaner\ReturnTypePass;
use Psy\CodeCleaner\StrictTypesPass;
use Psy\CodeCleaner\UseStatementPass;
use Psy\CodeCleaner\ValidClassNamePass;
use Psy\CodeCleaner\ValidConstructorPass;
use Psy\CodeCleaner\ValidFunctionNamePass;
use Psy\Exception\ParseErrorException;





class CodeCleaner
{
private $yolo = false;
private $strictTypes = false;

private $parser;
private $printer;
private $traverser;
private $namespace;










public function __construct(Parser $parser = null, Printer $printer = null, NodeTraverser $traverser = null, bool $yolo = false, bool $strictTypes = false)
{
$this->yolo = $yolo;
$this->strictTypes = $strictTypes;

$this->parser = $parser ?? (new ParserFactory())->createParser();
$this->printer = $printer ?: new Printer();
$this->traverser = $traverser ?: new NodeTraverser();

foreach ($this->getDefaultPasses() as $pass) {
$this->traverser->addVisitor($pass);
}
}




public function yolo(): bool
{
return $this->yolo;
}






private function getDefaultPasses(): array
{
if ($this->yolo) {
return $this->getYoloPasses();
}

$useStatementPass = new UseStatementPass();
$namespacePass = new NamespacePass($this);



$this->addImplicitDebugContext([$useStatementPass, $namespacePass]);

return [

new AbstractClassPass(),
new AssignThisVariablePass(),
new CalledClassPass(),
new CallTimePassByReferencePass(),
new FinalClassPass(),
new FunctionContextPass(),
new FunctionReturnInWriteContextPass(),
new IssetPass(),
new LabelContextPass(),
new LeavePsyshAlonePass(),
new ListPass(),
new LoopContextPass(),
new PassableByReferencePass(),
new ReturnTypePass(),
new EmptyArrayDimFetchPass(),
new ValidConstructorPass(),


$useStatementPass, 
new ExitPass(),
new ImplicitReturnPass(),
new MagicConstantsPass(),
$namespacePass, 
new RequirePass(),
new StrictTypesPass($this->strictTypes),


new ValidClassNamePass(),
new ValidFunctionNamePass(),
];
}










private function getYoloPasses(): array
{
$useStatementPass = new UseStatementPass();
$namespacePass = new NamespacePass($this);



$this->addImplicitDebugContext([$useStatementPass, $namespacePass]);

return [
new LeavePsyshAlonePass(),
$useStatementPass, 
new ExitPass(),
new ImplicitReturnPass(),
new MagicConstantsPass(),
$namespacePass, 
new RequirePass(),
new StrictTypesPass($this->strictTypes),
];
}










private function addImplicitDebugContext(array $passes)
{
$file = $this->getDebugFile();
if ($file === null) {
return;
}

try {
$code = @\file_get_contents($file);
if (!$code) {
return;
}

$stmts = $this->parse($code, true);
if ($stmts === false) {
return;
}


$traverser = new NodeTraverser();
foreach ($passes as $pass) {
$traverser->addVisitor($pass);
}

$traverser->traverse($stmts);
} catch (\Throwable $e) {

}
}






private static function getDebugFile()
{
$trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);

foreach (\array_reverse($trace) as $stackFrame) {
if (!self::isDebugCall($stackFrame)) {
continue;
}

if (\preg_match('/eval\(/', $stackFrame['file'])) {
\preg_match_all('/([^\(]+)\((\d+)/', $stackFrame['file'], $matches);

return $matches[1][0];
}

return $stackFrame['file'];
}
}






private static function isDebugCall(array $stackFrame): bool
{
$class = isset($stackFrame['class']) ? $stackFrame['class'] : null;
$function = isset($stackFrame['function']) ? $stackFrame['function'] : null;

return ($class === null && $function === 'Psy\\debug') ||
($class === Shell::class && $function === 'debug');
}











public function clean(array $codeLines, bool $requireSemicolons = false)
{
$stmts = $this->parse('<?php '.\implode(\PHP_EOL, $codeLines).\PHP_EOL, $requireSemicolons);
if ($stmts === false) {
return false;
}


$stmts = $this->traverser->traverse($stmts);


$oldLocale = \setlocale(\LC_NUMERIC, 0);
\setlocale(\LC_NUMERIC, 'C');

$code = $this->printer->prettyPrint($stmts);


\setlocale(\LC_NUMERIC, $oldLocale);

return $code;
}






public function setNamespace(array $namespace = null)
{
$this->namespace = $namespace;
}






public function getNamespace()
{
return $this->namespace;
}














protected function parse(string $code, bool $requireSemicolons = false)
{
try {
return $this->parser->parse($code);
} catch (\PhpParser\Error $e) {
if ($this->parseErrorIsUnclosedString($e, $code)) {
return false;
}

if ($this->parseErrorIsUnterminatedComment($e, $code)) {
return false;
}

if ($this->parseErrorIsTrailingComma($e, $code)) {
return false;
}

if (!$this->parseErrorIsEOF($e)) {
throw ParseErrorException::fromParseError($e);
}

if ($requireSemicolons) {
return false;
}

try {

return $this->parser->parse($code.';');
} catch (\PhpParser\Error $e) {
return false;
}
}
}

private function parseErrorIsEOF(\PhpParser\Error $e): bool
{
$msg = $e->getRawMessage();

return ($msg === 'Unexpected token EOF') || (\strpos($msg, 'Syntax error, unexpected EOF') !== false);
}











private function parseErrorIsUnclosedString(\PhpParser\Error $e, string $code): bool
{
if ($e->getRawMessage() !== 'Syntax error, unexpected T_ENCAPSED_AND_WHITESPACE') {
return false;
}

try {
$this->parser->parse($code."';");
} catch (\Throwable $e) {
return false;
}

return true;
}

private function parseErrorIsUnterminatedComment(\PhpParser\Error $e, $code): bool
{
return $e->getRawMessage() === 'Unterminated comment';
}

private function parseErrorIsTrailingComma(\PhpParser\Error $e, $code): bool
{
return ($e->getRawMessage() === 'A trailing comma is not allowed here') && (\substr(\rtrim($code), -1) === ',');
}
}
<?php










namespace Psy;

use Psy\ExecutionLoop\ProcessForker;
use Psy\VersionUpdater\GitHubChecker;
use Psy\VersionUpdater\Installer;
use Psy\VersionUpdater\SelfUpdate;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

if (!\function_exists('Psy\\sh')) {





function sh(): string
{
if (\version_compare(\PHP_VERSION, '8.0', '<')) {
return '\extract(\Psy\debug(\get_defined_vars(), isset($this) ? $this : @\get_called_class()));';
}

return <<<'EOS'
if (isset($this)) {
    \extract(\Psy\debug(\get_defined_vars(), $this));
} else {
    try {
        static::class;
        \extract(\Psy\debug(\get_defined_vars(), static::class));
    } catch (\Error $e) {
        \extract(\Psy\debug(\get_defined_vars()));
    }
}
EOS;
}
}

if (!\function_exists('Psy\\debug')) {










































function debug(array $vars = [], $bindTo = null): array
{
echo \PHP_EOL;

$sh = new Shell();
$sh->setScopeVariables($vars);




if ($sh->has('whereami')) {
$sh->addInput('whereami -n2', true);
}

if (\is_string($bindTo)) {
$sh->setBoundClass($bindTo);
} elseif ($bindTo !== null) {
$sh->setBoundObject($bindTo);
}

$sh->run();

return $sh->getScopeVariables(false);
}
}

if (!\function_exists('Psy\\info')) {











function info(Configuration $config = null)
{
static $lastConfig;
if ($config !== null) {
$lastConfig = $config;

return;
}

$prettyPath = function ($path) {
return $path;
};

$homeDir = (new ConfigPaths())->homeDir();
if ($homeDir && $homeDir = \rtrim($homeDir, '/')) {
$homePattern = '#^'.\preg_quote($homeDir, '#').'/#';
$prettyPath = function ($path) use ($homePattern) {
if (\is_string($path)) {
return \preg_replace($homePattern, '~/', $path);
} else {
return $path;
}
};
}

$config = $lastConfig ?: new Configuration();
$configEnv = (isset($_SERVER['PSYSH_CONFIG']) && $_SERVER['PSYSH_CONFIG']) ? $_SERVER['PSYSH_CONFIG'] : false;
if ($configEnv === false && \PHP_SAPI === 'cli-server') {
$configEnv = \getenv('PSYSH_CONFIG');
}

$shellInfo = [
'PsySH version' => Shell::VERSION,
];

$core = [
'PHP version' => \PHP_VERSION,
'OS' => \PHP_OS,
'default includes' => $config->getDefaultIncludes(),
'require semicolons' => $config->requireSemicolons(),
'strict types' => $config->strictTypes(),
'error logging level' => $config->errorLoggingLevel(),
'config file' => [
'default config file' => $prettyPath($config->getConfigFile()),
'local config file' => $prettyPath($config->getLocalConfigFile()),
'PSYSH_CONFIG env' => $prettyPath($configEnv),
],



];


$checker = new GitHubChecker();
$updateAvailable = null;
$latest = null;
try {
$updateAvailable = !$checker->isLatest();
$latest = $checker->getLatest();
} catch (\Throwable $e) {
}

$updates = [
'update available' => $updateAvailable,
'latest release version' => $latest,
'update check interval' => $config->getUpdateCheck(),
'update cache file' => $prettyPath($config->getUpdateCheckCacheFile()),
];

$input = [
'interactive mode' => $config->interactiveMode(),
'input interactive' => $config->getInputInteractive(),
'yolo' => $config->yolo(),
];

if ($config->hasReadline()) {
$info = \readline_info();

$readline = [
'readline available' => true,
'readline enabled' => $config->useReadline(),
'readline service' => \get_class($config->getReadline()),
];

if (isset($info['library_version'])) {
$readline['readline library'] = $info['library_version'];
}

if (isset($info['readline_name']) && $info['readline_name'] !== '') {
$readline['readline name'] = $info['readline_name'];
}
} else {
$readline = [
'readline available' => false,
];
}

$output = [
'color mode' => $config->colorMode(),
'output decorated' => $config->getOutputDecorated(),
'output verbosity' => $config->verbosity(),
'output pager' => $config->getPager(),
];

$theme = $config->theme();

$output['theme'] = [
'compact' => $theme->compact(),
'prompt' => $theme->prompt(),
'bufferPrompt' => $theme->bufferPrompt(),
'replayPrompt' => $theme->replayPrompt(),
'returnValue' => $theme->returnValue(),
];

$pcntl = [
'pcntl available' => ProcessForker::isPcntlSupported(),
'posix available' => ProcessForker::isPosixSupported(),
];

if ($disabledPcntl = ProcessForker::disabledPcntlFunctions()) {
$pcntl['disabled pcntl functions'] = $disabledPcntl;
}

if ($disabledPosix = ProcessForker::disabledPosixFunctions()) {
$pcntl['disabled posix functions'] = $disabledPosix;
}

$pcntl['use pcntl'] = $config->usePcntl();

$history = [
'history file' => $prettyPath($config->getHistoryFile()),
'history size' => $config->getHistorySize(),
'erase duplicates' => $config->getEraseDuplicates(),
];

$docs = [
'manual db file' => $prettyPath($config->getManualDbFile()),
'sqlite available' => true,
];

try {
if ($db = $config->getManualDb()) {
if ($q = $db->query('SELECT * FROM meta;')) {
$q->setFetchMode(\PDO::FETCH_KEY_PAIR);
$meta = $q->fetchAll();

foreach ($meta as $key => $val) {
switch ($key) {
case 'built_at':
$d = new \DateTime('@'.$val);
$val = $d->format(\DateTime::RFC2822);
break;
}
$key = 'db '.\str_replace('_', ' ', $key);
$docs[$key] = $val;
}
} else {
$docs['db schema'] = '0.1.0';
}
}
} catch (Exception\RuntimeException $e) {
if ($e->getMessage() === 'SQLite PDO driver not found') {
$docs['sqlite available'] = false;
} else {
throw $e;
}
}

$autocomplete = [
'tab completion enabled' => $config->useTabCompletion(),
'bracketed paste' => $config->useBracketedPaste(),
];


try {
if ($shell = Sudo::fetchProperty($config, 'shell')) {
$shellClass = \get_class($shell);
if ($shellClass !== 'Psy\\Shell') {
$shellInfo = [
'PsySH version' => $shell::VERSION,
'Shell class' => $shellClass,
];
}

try {
$core['loop listeners'] = \array_map('get_class', Sudo::fetchProperty($shell, 'loopListeners'));
} catch (\ReflectionException $e) {

}

$core['commands'] = \array_map('get_class', $shell->all());

try {
$autocomplete['custom matchers'] = \array_map('get_class', Sudo::fetchProperty($shell, 'matchers'));
} catch (\ReflectionException $e) {

}
}
} catch (\ReflectionException $e) {

}



return \array_merge($shellInfo, $core, \compact('updates', 'pcntl', 'input', 'readline', 'output', 'history', 'docs', 'autocomplete'));
}
}

if (!\function_exists('Psy\\bin')) {





function bin(): \Closure
{
return function () {
if (!isset($_SERVER['PSYSH_IGNORE_ENV']) || !$_SERVER['PSYSH_IGNORE_ENV']) {
if (\defined('HHVM_VERSION_ID')) {
\fwrite(\STDERR, 'PsySH v0.11 and higher does not support HHVM. Install an older version, or set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
exit(1);
}

if (\PHP_VERSION_ID < 70400) {
\fwrite(\STDERR, 'PHP 7.4.0 or higher is required. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
exit(1);
}

if (\PHP_VERSION_ID > 89999) {
\fwrite(\STDERR, 'PHP 9 or higher is not supported. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
exit(1);
}

if (!\function_exists('json_encode')) {
\fwrite(\STDERR, 'The JSON extension is required. Please install it. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
exit(1);
}

if (!\function_exists('token_get_all')) {
\fwrite(\STDERR, 'The Tokenizer extension is required. Please install it. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
exit(1);
}
}

$usageException = null;
$shellIsPhar = Shell::isPhar();

$input = new ArgvInput();
try {
$input->bind(new InputDefinition(\array_merge(Configuration::getInputOptions(), [
new InputOption('help', 'h', InputOption::VALUE_NONE),
new InputOption('version', 'V', InputOption::VALUE_NONE),
new InputOption('self-update', 'u', InputOption::VALUE_NONE),

new InputArgument('include', InputArgument::IS_ARRAY),
])));
} catch (\RuntimeException $e) {
$usageException = $e;
}

try {
$config = Configuration::fromInput($input);
} catch (\InvalidArgumentException $e) {
$usageException = $e;
}


if (!isset($config) || $usageException !== null || $input->getOption('help')) {
if ($usageException !== null) {
echo $usageException->getMessage().\PHP_EOL.\PHP_EOL;
}

$version = Shell::getVersionHeader(false);
$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : [];
$name = $argv ? \basename(\reset($argv)) : 'psysh';

echo <<<EOL
$version

Usage:
  $name [--version] [--help] [files...]

Options:
  -h, --help            Display this help message.
  -c, --config FILE     Use an alternate PsySH config file location.
      --cwd PATH        Use an alternate working directory.
  -V, --version         Display the PsySH version.

EOL;
if ($shellIsPhar) {
echo <<<EOL
  -u, --self-update     Install a newer version if available.

EOL;
}
echo <<<EOL
      --color           Force colors in output.
      --no-color        Disable colors in output.
  -i, --interactive     Force PsySH to run in interactive mode.
  -n, --no-interactive  Run PsySH without interactive input. Requires input from stdin.
  -r, --raw-output      Print var_export-style return values (for non-interactive input)
      --compact         Run PsySH with compact output.
  -q, --quiet           Shhhhhh.
  -v|vv|vvv, --verbose  Increase the verbosity of messages.
      --yolo            Run PsySH without input validation. You don't want this.

EOL;

exit($usageException === null ? 0 : 1);
}


if ($input->getOption('version')) {
echo Shell::getVersionHeader($config->useUnicode()).\PHP_EOL;
exit(0);
}


if ($input->getOption('self-update')) {
if (!$shellIsPhar) {
\fwrite(\STDERR, 'The --self-update option can only be used with with a phar based install.'.\PHP_EOL);
exit(1);
}
$selfUpdate = new SelfUpdate(new GitHubChecker(), new Installer());
$result = $selfUpdate->run($input, $config->getOutput());
exit($result);
}

$shell = new Shell($config);


$shell->setIncludes($input->getArgument('include'));

try {

$shell->run();
} catch (\Throwable $e) {
\fwrite(\STDERR, $e->getMessage().\PHP_EOL);






}
};
}
}
<?php










namespace Psy\Readline;








class GNUReadline implements Readline
{

protected $historyFile;

protected $historySize;

protected $eraseDups;









public static function isSupported(): bool
{
return \function_exists('readline') && \function_exists('readline_list_history');
}






public static function supportsBracketedPaste(): bool
{
return self::isSupported() && \stripos(\readline_info('library_version') ?: '', 'editline') === false;
}

public function __construct($historyFile = null, $historySize = 0, $eraseDups = false)
{
$this->historyFile = ($historyFile !== null) ? $historyFile : false;
$this->historySize = $historySize;
$this->eraseDups = $eraseDups;

\readline_info('readline_name', 'psysh');
}




public function addHistory(string $line): bool
{
if ($res = \readline_add_history($line)) {
$this->writeHistory();
}

return $res;
}




public function clearHistory(): bool
{
if ($res = \readline_clear_history()) {
$this->writeHistory();
}

return $res;
}




public function listHistory(): array
{
return \readline_list_history();
}




public function readHistory(): bool
{
\readline_read_history();
\readline_clear_history();

return \readline_read_history($this->historyFile);
}




public function readline(string $prompt = null)
{
return \readline($prompt);
}




public function redisplay()
{
\readline_redisplay();
}




public function writeHistory(): bool
{


if ($this->historyFile !== false) {
$res = \readline_write_history($this->historyFile);
} else {
$res = true;
}

if (!$res || !$this->eraseDups && !$this->historySize > 0) {
return $res;
}

$hist = $this->listHistory();
if (!$hist) {
return true;
}

if ($this->eraseDups) {

$hist = \array_flip(\array_flip($hist));

\ksort($hist);
}

if ($this->historySize > 0) {
$histsize = \count($hist);
if ($histsize > $this->historySize) {
$hist = \array_slice($hist, $histsize - $this->historySize);
}
}

\readline_clear_history();
foreach ($hist as $line) {
\readline_add_history($line);
}

if ($this->historyFile !== false) {
return \readline_write_history($this->historyFile);
}

return true;
}
}
<?php










namespace Psy\Readline;

use Psy\Exception\BreakException;
use Psy\Readline\Hoa\Console as HoaConsole;
use Psy\Readline\Hoa\ConsoleCursor as HoaConsoleCursor;
use Psy\Readline\Hoa\ConsoleInput as HoaConsoleInput;
use Psy\Readline\Hoa\ConsoleOutput as HoaConsoleOutput;
use Psy\Readline\Hoa\ConsoleTput as HoaConsoleTput;
use Psy\Readline\Hoa\Readline as HoaReadline;
use Psy\Readline\Hoa\Ustring as HoaUstring;




class Userland implements Readline
{

private $hoaReadline;


private $lastPrompt;

private $tput;
private $input;
private $output;

public static function isSupported(): bool
{
static::bootstrapHoa();

return HoaUstring::checkMbString() && HoaConsoleTput::isSupported();
}




public static function supportsBracketedPaste(): bool
{
return false;
}




public function __construct($historyFile = null, $historySize = 0, $eraseDups = false)
{
static::bootstrapHoa(true);

$this->hoaReadline = new HoaReadline();
$this->hoaReadline->addMapping('\C-l', function () {
$this->redisplay();

return HoaReadline::STATE_NO_ECHO;
});

$this->tput = new HoaConsoleTput();
HoaConsole::setTput($this->tput);

$this->input = new HoaConsoleInput();
HoaConsole::setInput($this->input);

$this->output = new HoaConsoleOutput();
HoaConsole::setOutput($this->output);
}




public static function bootstrapHoa(bool $withTerminalResize = false)
{

\class_exists('Psy\Readline\Hoa\ProtocolWrapper');


\class_exists('Psy\Readline\Hoa\Stream');


$withTerminalResize && \class_exists('Psy\Readline\Hoa\ConsoleWindow');
}




public function addHistory(string $line): bool
{
$this->hoaReadline->addHistory($line);

return true;
}




public function clearHistory(): bool
{
$this->hoaReadline->clearHistory();

return true;
}




public function listHistory(): array
{
$i = 0;
$list = [];
while (($item = $this->hoaReadline->getHistory($i++)) !== null) {
$list[] = $item;
}

return $list;
}




public function readHistory(): bool
{
return true;
}








public function readline(string $prompt = null)
{
$this->lastPrompt = $prompt;

return $this->hoaReadline->readLine($prompt);
}




public function redisplay()
{
$currentLine = $this->hoaReadline->getLine();
HoaConsoleCursor::clear('all');
echo $this->lastPrompt, $currentLine;
}




public function writeHistory(): bool
{
return true;
}
}
<?php










namespace Psy\Readline;




interface Readline
{





public function __construct($historyFile = null, $historySize = 0, $eraseDups = false);




public static function isSupported(): bool;




public static function supportsBracketedPaste(): bool;








public function addHistory(string $line): bool;






public function clearHistory(): bool;






public function listHistory(): array;






public function readHistory(): bool;








public function readline(string $prompt = null);




public function redisplay();






public function writeHistory(): bool;
}
<?php










namespace Psy\Readline;

use Psy\Util\Str;











class Libedit extends GNUReadline
{
private $hasWarnedOwnership = false;




public static function isSupported(): bool
{
return \function_exists('readline') && !\function_exists('readline_list_history');
}




public static function supportsBracketedPaste(): bool
{
return false;
}




public function listHistory(): array
{
$history = \file_get_contents($this->historyFile);
if (!$history) {
return [];
}


$history = \explode("\n", $history);


if ($history[0] === '_HiStOrY_V2_') {
\array_shift($history);
}


$history = \array_map([$this, 'parseHistoryLine'], $history);

return \array_values(\array_filter($history));
}




public function writeHistory(): bool
{
$res = parent::writeHistory();





if ($res === false && !$this->hasWarnedOwnership) {
if (\is_file($this->historyFile) && \is_writable($this->historyFile)) {
$this->hasWarnedOwnership = true;
$msg = \sprintf('Error writing history file, check file ownership: %s', $this->historyFile);
\trigger_error($msg, \E_USER_NOTICE);
}
}

return $res;
}











protected function parseHistoryLine(string $line)
{

if (!$line || $line[0] === "\0") {
return;
}


if (($pos = \strpos($line, "\0")) !== false) {
$line = \substr($line, 0, $pos);
}

return ($line !== '') ? Str::unvis($line) : null;
}
}
<?php










namespace Psy\Readline;

use Psy\Exception\BreakException;




class Transient implements Readline
{
private $history;
private $historySize;
private $eraseDups;
private $stdin;






public static function isSupported(): bool
{
return true;
}




public static function supportsBracketedPaste(): bool
{
return false;
}




public function __construct($historyFile = null, $historySize = 0, $eraseDups = false)
{

$this->history = [];
$this->historySize = $historySize;
$this->eraseDups = $eraseDups;
}




public function addHistory(string $line): bool
{
if ($this->eraseDups) {
if (($key = \array_search($line, $this->history)) !== false) {
unset($this->history[$key]);
}
}

$this->history[] = $line;

if ($this->historySize > 0) {
$histsize = \count($this->history);
if ($histsize > $this->historySize) {
$this->history = \array_slice($this->history, $histsize - $this->historySize);
}
}

$this->history = \array_values($this->history);

return true;
}




public function clearHistory(): bool
{
$this->history = [];

return true;
}




public function listHistory(): array
{
return $this->history;
}




public function readHistory(): bool
{
return true;
}








public function readline(string $prompt = null)
{
echo $prompt;

return \rtrim(\fgets($this->getStdin()), "\n\r");
}




public function redisplay()
{

}




public function writeHistory(): bool
{
return true;
}








private function getStdin()
{
if (!isset($this->stdin)) {
$this->stdin = \fopen('php://stdin', 'r');
}

if (\feof($this->stdin)) {
throw new BreakException('Ctrl+D');
}

return $this->stdin;
}
}
<?php



































namespace Psy\Readline\Hoa;




class Protocol extends ProtocolNode
{





const NO_RESOLUTION = '/hoa/flatland';




private static $_instance = null;




private static $_cache = [];




public function __construct()
{
$this->initialize();

return;
}





public static function getInstance(): self
{
if (null === static::$_instance) {
static::$_instance = new self();
}

return static::$_instance;
}




protected function initialize()
{
$root = \dirname(__DIR__, 3);
$argv0 = isset($_SERVER['argv'][0]) ? \realpath($_SERVER['argv'][0]) : false;

$cwd =
'cli' === \PHP_SAPI
? false !== $argv0 ? \dirname($argv0) : ''
: \getcwd();

$this[] = new ProtocolNode(
'Application',
$cwd.\DIRECTORY_SEPARATOR,
[
new ProtocolNode('Public', 'Public'.\DIRECTORY_SEPARATOR),
]
);

$this[] = new ProtocolNode(
'Data',
\dirname($cwd).\DIRECTORY_SEPARATOR,
[
new ProtocolNode(
'Etc',
'Etc'.\DIRECTORY_SEPARATOR,
[
new ProtocolNode('Configuration', 'Configuration'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Locale', 'Locale'.\DIRECTORY_SEPARATOR),
]
),
new ProtocolNode('Lost+found', 'Lost+found'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Temporary', 'Temporary'.\DIRECTORY_SEPARATOR),
new ProtocolNode(
'Variable',
'Variable'.\DIRECTORY_SEPARATOR,
[
new ProtocolNode('Cache', 'Cache'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Database', 'Database'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Log', 'Log'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Private', 'Private'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Run', 'Run'.\DIRECTORY_SEPARATOR),
new ProtocolNode('Test', 'Test'.\DIRECTORY_SEPARATOR),
]
),
]
);

$this[] = new ProtocolNodeLibrary(
'Library',
$root.\DIRECTORY_SEPARATOR.'Hoathis'.\DIRECTORY_SEPARATOR.';'.
$root.\DIRECTORY_SEPARATOR.'Hoa'.\DIRECTORY_SEPARATOR
);
}








public function resolve(string $path, bool $exists = true, bool $unfold = false)
{
if (\substr($path, 0, 6) !== 'hoa://') {
if (true === \is_dir($path)) {
$path = \rtrim($path, '/\\');

if ('' === $path) {
$path = '/';
}
}

return $path;
}

if (isset(self::$_cache[$path])) {
$handle = self::$_cache[$path];
} else {
$out = $this->_resolve($path, $handle);


if (!\is_array($handle)) {
return $out;
}

$handle = \array_values(\array_unique($handle, \SORT_REGULAR));

foreach ($handle as &$entry) {
if (true === \is_dir($entry)) {
$entry = \rtrim($entry, '/\\');

if ('' === $entry) {
$entry = '/';
}
}
}

self::$_cache[$path] = $handle;
}

if (true === $unfold) {
if (true !== $exists) {
return $handle;
}

$out = [];

foreach ($handle as $solution) {
if (\file_exists($solution)) {
$out[] = $solution;
}
}

return $out;
}

if (true !== $exists) {
return $handle[0];
}

foreach ($handle as $solution) {
if (\file_exists($solution)) {
return $solution;
}
}

return static::NO_RESOLUTION;
}




public static function clearCache()
{
self::$_cache = [];
}
}
<?php



































namespace Psy\Readline\Hoa;








class FileDoesNotExistException extends FileException
{
}
<?php



































namespace Psy\Readline\Hoa;






class IteratorRecursiveDirectory extends \RecursiveDirectoryIterator
{



protected $_splFileInfoClass = null;




protected $_relativePath = null;






public function __construct(string $path, int $flags = null, string $splFileInfoClass = null)
{
if (null === $flags) {
parent::__construct($path);
} else {
parent::__construct($path, $flags);
}

$this->_relativePath = $path;
$this->setSplFileInfoClass($splFileInfoClass);

return;
}





#[\ReturnTypeWillChange]
public function current()
{
$out = parent::current();

if (null !== $this->_splFileInfoClass &&
$out instanceof \SplFileInfo) {
$out->setInfoClass($this->_splFileInfoClass);
$out = $out->getFileInfo();

if ($out instanceof IteratorSplFileInfo) {
$out->setRelativePath($this->getRelativePath());
}
}

return $out;
}





#[\ReturnTypeWillChange]
public function getChildren()
{
$out = parent::getChildren();
$out->_relativePath = $this->getRelativePath();
$out->setSplFileInfoClass($this->_splFileInfoClass);

return $out;
}




public function setSplFileInfoClass($splFileInfoClass)
{
$this->_splFileInfoClass = $splFileInfoClass;
}




public function getRelativePath(): string
{
return $this->_relativePath;
}
}
<?php



































namespace Psy\Readline\Hoa;






class ExceptionIdle extends \Exception
{



protected $_tmpArguments = null;




protected $_arguments = null;




protected $_trace = null;




protected $_previous = null;




protected $_rawMessage = null;








public function __construct(
string $message,
int $code = 0,
$arguments = [],
\Exception $previous = null
) {
$this->_tmpArguments = $arguments;
parent::__construct($message, $code, $previous);
$this->_rawMessage = $message;
$this->message = @\vsprintf($message, $this->getArguments());

return;
}






public function getBacktrace()
{
if (null === $this->_trace) {
$this->_trace = $this->getTrace();
}

return $this->_trace;
}






public function getPreviousThrow()
{
if (null === $this->_previous) {
$this->_previous = $this->getPrevious();
}

return $this->_previous;
}




public function getArguments()
{
if (null === $this->_arguments) {
$arguments = $this->_tmpArguments;

if (!\is_array($arguments)) {
$arguments = [$arguments];
}

foreach ($arguments as &$value) {
if (null === $value) {
$value = '(null)';
}
}

$this->_arguments = $arguments;
unset($this->_tmpArguments);
}

return $this->_arguments;
}




public function getRawMessage(): string
{
return $this->_rawMessage;
}




public function getFormattedMessage(): string
{
return $this->getMessage();
}




public function getFrom(): string
{
$trace = $this->getBacktrace();
$from = '{main}';

if (!empty($trace)) {
$t = $trace[0];
$from = '';

if (isset($t['class'])) {
$from .= $t['class'].'::';
}

if (isset($t['function'])) {
$from .= $t['function'].'()';
}
}

return $from;
}




public function raise(bool $includePrevious = false): string
{
$message = $this->getFormattedMessage();
$trace = $this->getBacktrace();
$file = '/dev/null';
$line = -1;
$pre = $this->getFrom();

if (!empty($trace)) {
$file = $trace['file'] ?? null;
$line = $trace['line'] ?? null;
}

$pre .= ': ';

try {
$out =
$pre.'('.$this->getCode().') '.$message."\n".
'in '.$this->getFile().' at line '.
$this->getLine().'.';
} catch (\Exception $e) {
$out =
$pre.'('.$this->getCode().') '.$message."\n".
'in '.$file.' around line '.$line.'.';
}

if (true === $includePrevious &&
null !== $previous = $this->getPreviousThrow()) {
$out .=
"\n\n".'    ⬇'."\n\n".
'Nested exception ('.\get_class($previous).'):'."\n".
($previous instanceof self
? $previous->raise(true)
: $previous->getMessage());
}

return $out;
}




public static function uncaught(\Throwable $exception)
{
if (!($exception instanceof self)) {
throw $exception;
}

while (0 < \ob_get_level()) {
\ob_end_flush();
}

echo 'Uncaught exception ('.\get_class($exception).'):'."\n".
$exception->raise(true);
}




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






public static function enableUncaughtHandler(bool $enable = true)
{
if (false === $enable) {
return \restore_exception_handler();
}

return \set_exception_handler(function ($exception) {
return self::uncaught($exception);
});
}
}
<?php



































namespace Psy\Readline\Hoa;






class FileDirectory extends FileGeneric
{



const MODE_READ = 'rb';





const MODE_CREATE = 'xb';





const MODE_CREATE_RECURSIVE = 'xrb';




public function __construct(
string $streamName,
string $mode = self::MODE_READ,
string $context = null,
bool $wait = false
) {
$this->setMode($mode);
parent::__construct($streamName, $context, $wait);

return;
}




protected function &_open(string $streamName, StreamContext $context = null)
{
if (false === \is_dir($streamName)) {
if ($this->getMode() === self::MODE_READ) {
throw new FileDoesNotExistException('Directory %s does not exist.', 0, $streamName);
} else {
self::create(
$streamName,
$this->getMode(),
null !== $context
? $context->getContext()
: null
);
}
}

$out = null;

return $out;
}




protected function _close(): bool
{
return true;
}




public function copy(string $to, bool $force = StreamTouchable::DO_NOT_OVERWRITE): bool
{
if (empty($to)) {
throw new FileException('The destination path (to copy) is empty.', 1);
}

$from = $this->getStreamName();
$fromLength = \strlen($from) + 1;
$finder = new FileFinder();
$finder->in($from);

self::create($to, self::MODE_CREATE_RECURSIVE);

foreach ($finder as $file) {
$relative = \substr($file->getPathname(), $fromLength);
$_to = $to.\DIRECTORY_SEPARATOR.$relative;

if (true === $file->isDir()) {
self::create($_to, self::MODE_CREATE);

continue;
}






$handle = null;

if (true === $file->isFile()) {
$handle = new FileRead($file->getPathname());
} elseif (true === $file->isDir()) {
$handle = new self($file->getPathName());
} elseif (true === $file->isLink()) {
$handle = new FileLinkRead($file->getPathName());
}

if (null !== $handle) {
$handle->copy($_to, $force);
$handle->close();
}
}

return true;
}




public function delete(): bool
{
$from = $this->getStreamName();
$finder = new FileFinder();
$finder->in($from)
->childFirst();

foreach ($finder as $file) {
$file->open()->delete();
$file->close();
}

if (null === $this->getStreamContext()) {
return @\rmdir($from);
}

return @\rmdir($from, $this->getStreamContext()->getContext());
}




public static function create(
string $name,
string $mode = self::MODE_CREATE_RECURSIVE,
string $context = null
): bool {
if (true === \is_dir($name)) {
return true;
}

if (empty($name)) {
return false;
}

if (null !== $context) {
if (false === StreamContext::contextExists($context)) {
throw new FileException('Context %s was not previously declared, cannot retrieve '.'this context.', 2, $context);
} else {
$context = StreamContext::getInstance($context);
}
}

if (null === $context) {
return @\mkdir(
$name,
0755,
self::MODE_CREATE_RECURSIVE === $mode
);
}

return @\mkdir(
$name,
0755,
self::MODE_CREATE_RECURSIVE === $mode,
$context->getContext()
);
}
}
<?php



































namespace Psy\Readline\Hoa;






class StreamContext
{



protected $_id = null;




protected $_context;




protected static $_instances = [];




protected function __construct($id)
{
$this->_id = $id;
$this->_context = \stream_context_create();

return;
}




public static function getInstance(string $id): self
{
if (false === static::contextExists($id)) {
static::$_instances[$id] = new self($id);
}

return static::$_instances[$id];
}




public function getId(): string
{
return $this->_id;
}




public static function contextExists(string $id): bool
{
return \array_key_exists($id, static::$_instances);
}





public function setOptions(array $options): bool
{
return \stream_context_set_option($this->getContext(), $options);
}





public function setParameters(array $parameters): bool
{
return \stream_context_set_params($this->getContext(), $parameters);
}




public function getOptions(): array
{
return \stream_context_get_options($this->getContext());
}




public function getParameters(): array
{
return \stream_context_get_params($this->getContext());
}




public function getContext()
{
return $this->_context;
}
}
<?php



































namespace Psy\Readline\Hoa;






interface IStream
{



public function getStream();
}
<?php



































namespace Psy\Readline\Hoa;






abstract class Stream implements IStream, EventListenable
{
use EventListens;




const NAME = 0;




const HANDLER = 1;




const RESOURCE = 2;




const CONTEXT = 3;




const DEFAULT_BUFFER_SIZE = 8192;




protected $_bucket = [];




private static $_register = [];




protected $_bufferSize = self::DEFAULT_BUFFER_SIZE;




protected $_streamName = null;




protected $_context = null;




protected $_hasBeenDeferred = false;




protected $_borrowing = false;






public function __construct(string $streamName, string $context = null, bool $wait = false)
{
$this->_streamName = $streamName;
$this->_context = $context;
$this->_hasBeenDeferred = $wait;
$this->setListener(
new EventListener(
$this,
[
'authrequire',
'authresult',
'complete',
'connect',
'failure',
'mimetype',
'progress',
'redirect',
'resolve',
'size',
]
)
);

if (true === $wait) {
return;
}

$this->open();

return;
}






private static function &_getStream(
string $streamName,
self $handler,
string $context = null
): array {
$name = \md5($streamName);

if (null !== $context) {
if (false === StreamContext::contextExists($context)) {
throw new StreamException('Context %s was not previously declared, cannot retrieve '.'this context.', 0, $context);
}

$context = StreamContext::getInstance($context);
}

if (!isset(self::$_register[$name])) {
self::$_register[$name] = [
self::NAME => $streamName,
self::HANDLER => $handler,
self::RESOURCE => $handler->_open($streamName, $context),
self::CONTEXT => $context,
];
Event::register(
'hoa://Event/Stream/'.$streamName,
$handler
);

Event::register(
'hoa://Event/Stream/'.$streamName.':close-before',
$handler
);
} else {
$handler->_borrowing = true;
}

if (null === self::$_register[$name][self::RESOURCE]) {
self::$_register[$name][self::RESOURCE]
= $handler->_open($streamName, $context);
}

return self::$_register[$name];
}






abstract protected function &_open(string $streamName, StreamContext $context = null);






abstract protected function _close(): bool;




final public function open(): self
{
$context = $this->_context;

if (true === $this->hasBeenDeferred()) {
if (null === $context) {
$handle = StreamContext::getInstance(\uniqid());
$handle->setParameters([
'notification' => [$this, '_notify'],
]);
$context = $handle->getId();
} elseif (true === StreamContext::contextExists($context)) {
$handle = StreamContext::getInstance($context);
$parameters = $handle->getParameters();

if (!isset($parameters['notification'])) {
$handle->setParameters([
'notification' => [$this, '_notify'],
]);
}
}
}

$this->_bufferSize = self::DEFAULT_BUFFER_SIZE;
$this->_bucket = self::_getStream(
$this->_streamName,
$this,
$context
);

return $this;
}




final public function close()
{
$streamName = $this->getStreamName();

if (null === $streamName) {
return;
}

$name = \md5($streamName);

if (!isset(self::$_register[$name])) {
return;
}

Event::notify(
'hoa://Event/Stream/'.$streamName.':close-before',
$this,
new EventBucket()
);

if (false === $this->_close()) {
return;
}

unset(self::$_register[$name]);
$this->_bucket[self::HANDLER] = null;
Event::unregister(
'hoa://Event/Stream/'.$streamName
);
Event::unregister(
'hoa://Event/Stream/'.$streamName.':close-before'
);

return;
}




public function getStreamName()
{
if (empty($this->_bucket)) {
return null;
}

return $this->_bucket[self::NAME];
}




public function getStream()
{
if (empty($this->_bucket)) {
return null;
}

return $this->_bucket[self::RESOURCE];
}




public function getStreamContext()
{
if (empty($this->_bucket)) {
return null;
}

return $this->_bucket[self::CONTEXT];
}




public static function getStreamHandler(string $streamName)
{
$name = \md5($streamName);

if (!isset(self::$_register[$name])) {
return null;
}

return self::$_register[$name][self::HANDLER];
}







public function _setStream($stream)
{
if (false === \is_resource($stream) &&
('resource' !== \gettype($stream) ||
'Unknown' !== \get_resource_type($stream))) {
throw new StreamException('Try to change the stream resource with an invalid one; '.'given %s.', 1, \gettype($stream));
}

$old = $this->_bucket[self::RESOURCE];
$this->_bucket[self::RESOURCE] = $stream;

return $old;
}




public function isOpened(): bool
{
return \is_resource($this->getStream());
}




public function setStreamTimeout(int $seconds, int $microseconds = 0): bool
{
return \stream_set_timeout($this->getStream(), $seconds, $microseconds);
}




protected function hasBeenDeferred()
{
return $this->_hasBeenDeferred;
}






public function hasTimedOut(): bool
{
$metaData = $this->getStreamMetaData();

return true === $metaData['timed_out'];
}




public function setStreamBlocking(bool $mode): bool
{
return \stream_set_blocking($this->getStream(), $mode);
}








public function setStreamBuffer(int $buffer): bool
{

$out = 0 === \stream_set_write_buffer($this->getStream(), $buffer);

if (true === $out) {
$this->_bufferSize = $buffer;
}

return $out;
}





public function disableStreamBuffer(): bool
{
return $this->setStreamBuffer(0);
}




public function getStreamBufferSize(): int
{
return $this->_bufferSize;
}




public function getStreamWrapperName(): string
{
if (false === $pos = \strpos($this->getStreamName(), '://')) {
return 'file';
}

return \substr($this->getStreamName(), 0, $pos);
}




public function getStreamMetaData(): array
{
return \stream_get_meta_data($this->getStream());
}




public function isBorrowing(): bool
{
return $this->_borrowing;
}




public function _notify(
int $ncode,
int $severity,
$message,
$code,
$transferred,
$max
) {
static $_map = [
\STREAM_NOTIFY_AUTH_REQUIRED => 'authrequire',
\STREAM_NOTIFY_AUTH_RESULT => 'authresult',
\STREAM_NOTIFY_COMPLETED => 'complete',
\STREAM_NOTIFY_CONNECT => 'connect',
\STREAM_NOTIFY_FAILURE => 'failure',
\STREAM_NOTIFY_MIME_TYPE_IS => 'mimetype',
\STREAM_NOTIFY_PROGRESS => 'progress',
\STREAM_NOTIFY_REDIRECTED => 'redirect',
\STREAM_NOTIFY_RESOLVE => 'resolve',
\STREAM_NOTIFY_FILE_SIZE_IS => 'size',
];

$this->getListener()->fire($_map[$ncode], new EventBucket([
'code' => $code,
'severity' => $severity,
'message' => $message,
'transferred' => $transferred,
'max' => $max,
]));
}








final public static function _Hoa_Stream()
{
foreach (self::$_register as $entry) {
$entry[self::HANDLER]->close();
}

return;
}




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




public function __destruct()
{
if (false === $this->isOpened()) {
return;
}

$this->close();

return;
}
}








class _Protocol extends ProtocolNode
{





protected $_name = 'Stream';








public function reachId(string $id)
{
return Stream::getStreamHandler($id);
}
}




\register_shutdown_function([Stream::class, '_Hoa_Stream']);





$protocol = Protocol::getInstance();
$protocol['Library'][] = new _Protocol();
<?php



































namespace Psy\Readline\Hoa;




class ConsoleProcessus extends Stream implements StreamIn, StreamOut, StreamPathable
{



const SIGHUP = 1;




const SIGINT = 2;




const SIGQUIT = 3;




const SIGILL = 4;




const SIGTRAP = 5;




const SIGABRT = 6;




const SIGEMT = 7;




const SIGFPE = 8;




const SIGKILL = 9;




const SIGBUS = 10;




const SIGSEGV = 11;




const SIGSYS = 12;




const SIGPIPE = 13;




const SIGALRM = 14;




const SIGTERM = 15;




const SIGURG = 16;




const SIGSTOP = 17;




const SIGTSTP = 18;




const SIGCONT = 19;




const SIGCHLD = 20;




const SIGTTIN = 21;




const SIGTTOU = 22;




const SIGIO = 23;




const SIGXCPU = 24;




const SIGXFSZ = 25;




const SIGVTALRM = 26;




const SIGPROF = 27;




const SIGWINCH = 28;




const SIGINFO = 29;




const SIGUSR1 = 30;




const SIGUSR2 = 31;




protected $_command = null;




protected $_options = [];




protected $_cwd = null;




protected $_environment = null;




protected $_timeout = 30;




protected $_descriptors = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];




protected $_pipes = null;




protected $_seekable = [];




public function __construct(
string $command,
array $options = null,
array $descriptors = null,
string $cwd = null,
array $environment = null,
int $timeout = 30
) {
$this->setCommand($command);

if (null !== $options) {
$this->setOptions($options);
}

if (null !== $descriptors) {
$this->_descriptors = [];

foreach ($descriptors as $descriptor => $nature) {
if (isset($this->_descriptors[$descriptor])) {
throw new ConsoleException('Pipe descriptor %d already exists, cannot '.'redefine it.', 0, $descriptor);
}

$this->_descriptors[$descriptor] = $nature;
}
}

$this->setCwd($cwd ?: \getcwd());

if (null !== $environment) {
$this->setEnvironment($environment);
}

$this->setTimeout($timeout);
parent::__construct($this->getCommandLine(), null, true);
$this->getListener()->addIds(['input', 'output', 'timeout', 'start', 'stop']);

return;
}




protected function &_open(string $streamName, StreamContext $context = null)
{
$out = @\proc_open(
$streamName,
$this->_descriptors,
$this->_pipes,
$this->getCwd(),
$this->getEnvironment()
);

if (false === $out) {
throw new ConsoleException('Something wrong happen when running %s.', 1, $streamName);
}

return $out;
}




protected function _close(): bool
{
foreach ($this->_pipes as $pipe) {
@\fclose($pipe);
}

return (bool) @\proc_close($this->getStream());
}







public function run()
{
if (false === $this->isOpened()) {
$this->open();
} else {
$this->_close();
$this->_setStream($this->_open(
$this->getStreamName(),
$this->getStreamContext()
));
}

$this->getListener()->fire('start', new EventBucket());

$_read = [];
$_write = [];
$_except = [];

foreach ($this->_pipes as $p => $pipe) {
switch ($this->_descriptors[$p][1]) {
case 'r':
\stream_set_blocking($pipe, false);
$_write[] = $pipe;

break;

case 'w':
case 'a':
\stream_set_blocking($pipe, true);
$_read[] = $pipe;

break;
}
}

while (true) {
foreach ($_read as $i => $r) {
if (false === \is_resource($r)) {
unset($_read[$i]);
}
}

foreach ($_write as $i => $w) {
if (false === \is_resource($w)) {
unset($_write[$i]);
}
}

foreach ($_except as $i => $e) {
if (false === \is_resource($e)) {
unset($_except[$i]);
}
}

if (empty($_read) && empty($_write) && empty($_except)) {
break;
}

$read = $_read;
$write = $_write;
$except = $_except;
$select = \stream_select($read, $write, $except, $this->getTimeout());

if (0 === $select) {
$this->getListener()->fire('timeout', new EventBucket());

break;
}

foreach ($read as $i => $_r) {
$pipe = \array_search($_r, $this->_pipes);
$line = $this->readLine($pipe);

if (false === $line) {
$result = [false];
} else {
$result = $this->getListener()->fire(
'output',
new EventBucket([
'pipe' => $pipe,
'line' => $line,
])
);
}

if (true === \feof($_r) || \in_array(false, $result, true)) {
\fclose($_r);
unset($_read[$i]);

break;
}
}

foreach ($write as $j => $_w) {
$result = $this->getListener()->fire(
'input',
new EventBucket([
'pipe' => \array_search($_w, $this->_pipes),
])
);

if (true === \feof($_w) || \in_array(false, $result, true)) {
\fclose($_w);
unset($_write[$j]);
}
}

if (empty($_read)) {
break;
}
}

$this->getListener()->fire('stop', new EventBucket());

return;
}




protected function getPipe(int $pipe)
{
if (!isset($this->_pipes[$pipe])) {
throw new ConsoleException('Pipe descriptor %d does not exist, cannot read from it.', 2, $pipe);
}

return $this->_pipes[$pipe];
}




protected function isPipeSeekable(int $pipe): bool
{
if (!isset($this->_seekable[$pipe])) {
$_pipe = $this->getPipe($pipe);
$data = \stream_get_meta_data($_pipe);
$this->_seekable[$pipe] = $data['seekable'];
}

return $this->_seekable[$pipe];
}




public function eof(int $pipe = 1): bool
{
return \feof($this->getPipe($pipe));
}




public function read(int $length, int $pipe = 1)
{
if (0 > $length) {
throw new ConsoleException('Length must be greater than 0, given %d.', 3, $length);
}

return \fread($this->getPipe($pipe), $length);
}




public function readString(int $length, int $pipe = 1)
{
return $this->read($length, $pipe);
}




public function readCharacter(int $pipe = 1)
{
return \fgetc($this->getPipe($pipe));
}




public function readBoolean(int $pipe = 1)
{
return (bool) $this->read(1, $pipe);
}




public function readInteger(int $length = 1, int $pipe = 1)
{
return (int) $this->read($length, $pipe);
}




public function readFloat(int $length = 1, int $pipe = 1)
{
return (float) $this->read($length, $pipe);
}





public function readArray(string $format = null, int $pipe = 1)
{
return $this->scanf($format, $pipe);
}




public function readLine(int $pipe = 1)
{
return \stream_get_line($this->getPipe($pipe), 1 << 15, "\n");
}




public function readAll(int $offset = -1, int $pipe = 1)
{
$_pipe = $this->getPipe($pipe);

if (true === $this->isPipeSeekable($pipe)) {
$offset += \ftell($_pipe);
} else {
$offset = -1;
}

return \stream_get_contents($_pipe, -1, $offset);
}




public function scanf(string $format, int $pipe = 1): array
{
return \fscanf($this->getPipe($pipe), $format);
}




public function write(string $string, int $length, int $pipe = 0)
{
if (0 > $length) {
throw new ConsoleException('Length must be greater than 0, given %d.', 4, $length);
}

return \fwrite($this->getPipe($pipe), $string, $length);
}




public function writeString(string $string, int $pipe = 0)
{
$string = (string) $string;

return $this->write($string, \strlen($string), $pipe);
}




public function writeCharacter(string $char, int $pipe = 0)
{
return $this->write((string) $char[0], 1, $pipe);
}




public function writeBoolean(bool $boolean, int $pipe = 0)
{
return $this->write((string) (bool) $boolean, 1, $pipe);
}




public function writeInteger(int $integer, int $pipe = 0)
{
$integer = (string) (int) $integer;

return $this->write($integer, \strlen($integer), $pipe);
}




public function writeFloat(float $float, int $pipe = 0)
{
$float = (string) (float) $float;

return $this->write($float, \strlen($float), $pipe);
}




public function writeArray(array $array, int $pipe = 0)
{
$array = \var_export($array, true);

return $this->write($array, \strlen($array), $pipe);
}




public function writeLine(string $line, int $pipe = 0)
{
if (false === $n = \strpos($line, "\n")) {
return $this->write($line."\n", \strlen($line) + 1, $pipe);
}

++$n;

return $this->write(\substr($line, 0, $n), $n, $pipe);
}




public function writeAll(string $string, int $pipe = 0)
{
return $this->write($string, \strlen($string), $pipe);
}




public function truncate(int $size, int $pipe = 0): bool
{
return \ftruncate($this->getPipe($pipe), $size);
}




public function getBasename(): string
{
return \basename($this->getCommand());
}




public function getDirname(): string
{
return \dirname($this->getCommand());
}




public function getStatus(): array
{
return \proc_get_status($this->getStream());
}




public function getExitCode(): int
{
$handle = $this->getStatus();

return $handle['exitcode'];
}






public function isSuccessful(): bool
{
return 0 === $this->getExitCode();
}







public function terminate(int $signal = self::SIGTERM): bool
{
return \proc_terminate($this->getStream(), $signal);
}




protected function setCommand(string $command)
{
$old = $this->_command;
$this->_command = \escapeshellcmd($command);

return $old;
}




public function getCommand()
{
return $this->_command;
}




protected function setOptions(array $options): array
{
foreach ($options as &$option) {
$option = \escapeshellarg($option);
}

$old = $this->_options;
$this->_options = $options;

return $old;
}




public function getOptions(): array
{
return $this->_options;
}




public function getCommandLine(): string
{
$out = $this->getCommand();

foreach ($this->getOptions() as $key => $value) {
if (!\is_int($key)) {
$out .= ' '.$key.'='.$value;
} else {
$out .= ' '.$value;
}
}

return $out;
}




protected function setCwd(string $cwd)
{
$old = $this->_cwd;
$this->_cwd = $cwd;

return $old;
}




public function getCwd(): string
{
return $this->_cwd;
}




protected function setEnvironment(array $environment)
{
$old = $this->_environment;
$this->_environment = $environment;

return $old;
}




public function getEnvironment()
{
return $this->_environment;
}




public function setTimeout(int $timeout)
{
$old = $this->_timeout;
$this->_timeout = $timeout;

return $old;
}




public function getTimeout(): int
{
return $this->_timeout;
}




public static function setTitle(string $title)
{
\cli_set_process_title($title);
}




public static function getTitle()
{
return \cli_get_process_title();
}




public static function locate(string $binary)
{
if (isset($_ENV['PATH'])) {
$separator = ':';
$path = &$_ENV['PATH'];
} elseif (isset($_SERVER['PATH'])) {
$separator = ':';
$path = &$_SERVER['PATH'];
} elseif (isset($_SERVER['Path'])) {
$separator = ';';
$path = &$_SERVER['Path'];
} else {
return null;
}

foreach (\explode($separator, $path) as $directory) {
if (true === \file_exists($out = $directory.\DIRECTORY_SEPARATOR.$binary)) {
return $out;
}
}

return null;
}





public static function execute(string $commandLine, bool $escape = true): string
{
if (true === $escape) {
$commandLine = \escapeshellcmd($commandLine);
}

return \rtrim(\shell_exec($commandLine) ?? '');
}
}
<?php



































namespace Psy\Readline\Hoa;








interface StreamLockable extends IStream
{





const LOCK_SHARED = \LOCK_SH;






const LOCK_EXCLUSIVE = \LOCK_EX;






const LOCK_RELEASE = \LOCK_UN;






const LOCK_NO_BLOCK = \LOCK_NB;









public function lock(int $operation): bool;
}
<?php



































namespace Psy\Readline\Hoa;






class FileLinkReadWrite extends FileLink implements StreamIn, StreamOut
{



public function __construct(
string $streamName,
string $mode = parent::MODE_APPEND_READ_WRITE,
string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);

return;
}




protected function &_open(string $streamName, StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ_WRITE,
parent::MODE_TRUNCATE_READ_WRITE,
parent::MODE_APPEND_READ_WRITE,
parent::MODE_CREATE_READ_WRITE,
];

if (!\in_array($this->getMode(), $createModes)) {
throw new FileException('Open mode are not supported; given %d. Only %s are supported.', 0, [$this->getMode(), \implode(', ', $createModes)]);
}

\preg_match('#^(\w+)://#', $streamName, $match);

if (((isset($match[1]) && $match[1] === 'file') || !isset($match[1])) &&
!\file_exists($streamName) &&
parent::MODE_READ_WRITE === $this->getMode()) {
throw new FileDoesNotExistException('File %s does not exist.', 1, $streamName);
}

$out = parent::_open($streamName, $context);

return $out;
}




public function eof(): bool
{
return \feof($this->getStream());
}




public function read(int $length)
{
if (0 > $length) {
throw new FileException('Length must be greater than 0, given %d.', 2, $length);
}

return \fread($this->getStream(), $length);
}




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




public function readCharacter()
{
return \fgetc($this->getStream());
}




public function readBoolean()
{
return (bool) $this->read(1);
}




public function readInteger(int $length = 1)
{
return (int) $this->read($length);
}




public function readFloat(int $length = 1)
{
return (float) $this->read($length);
}





public function readArray(string $format = null)
{
return $this->scanf($format);
}




public function readLine()
{
return \fgets($this->getStream());
}




public function readAll(int $offset = 0)
{
return \stream_get_contents($this->getStream(), -1, $offset);
}




public function scanf(string $format): array
{
return \fscanf($this->getStream(), $format);
}




public function write(string $string, int $length)
{
if (0 > $length) {
throw new FileException('Length must be greater than 0, given %d.', 3, $length);
}

return \fwrite($this->getStream(), $string, $length);
}




public function writeString(string $string)
{
$string = (string) $string;

return $this->write($string, \strlen($string));
}




public function writeCharacter(string $char)
{
return $this->write((string) $char[0], 1);
}




public function writeBoolean(bool $boolean)
{
return $this->write((string) (bool) $boolean, 1);
}




public function writeInteger(int $integer)
{
$integer = (string) (int) $integer;

return $this->write($integer, \strlen($integer));
}




public function writeFloat(float $float)
{
$float = (string) (float) $float;

return $this->write($float, \strlen($float));
}




public function writeArray(array $array)
{
$array = \var_export($array, true);

return $this->write($array, \strlen($array));
}




public function writeLine(string $line)
{
if (false === $n = \strpos($line, "\n")) {
return $this->write($line."\n", \strlen($line) + 1);
}

++$n;

return $this->write(\substr($line, 0, $n), $n);
}




public function writeAll(string $string)
{
return $this->write($string, \strlen($string));
}




public function truncate(int $size): bool
{
return \ftruncate($this->getStream(), $size);
}
}
<?php



































namespace Psy\Readline\Hoa;








class FileException extends Exception
{
}
<?php



































namespace Psy\Readline\Hoa;




class ProtocolException extends Exception
{
}
<?php



































namespace Psy\Readline\Hoa;








class ConsoleInput implements StreamIn
{



protected $_input = null;




public function __construct(StreamIn $input = null)
{
if (null === $input) {
if (\defined('STDIN') &&
false !== @\stream_get_meta_data(\STDIN)) {
$input = new FileRead('php://stdin');
} else {
$input = new FileRead('/dev/tty');
}
}

$this->_input = $input;

return;
}




public function getStream(): StreamIn
{
return $this->_input;
}




public function eof(): bool
{
return $this->_input->eof();
}




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




public function readString(int $length)
{
return $this->_input->readString($length);
}




public function readCharacter()
{
return $this->_input->readCharacter();
}




public function readBoolean()
{
return $this->_input->readBoolean();
}




public function readInteger(int $length = 1)
{
return $this->_input->readInteger($length);
}




public function readFloat(int $length = 1)
{
return $this->_input->readFloat($length);
}





public function readArray($argument = null)
{
return $this->_input->readArray($argument);
}




public function readLine()
{
return $this->_input->readLine();
}




public function readAll(int $offset = 0)
{
return $this->_input->readAll($offset);
}




public function scanf(string $format): array
{
return $this->_input->scanf($format);
}
}
<?php



































namespace Psy\Readline\Hoa;






class Readline
{



const STATE_CONTINUE = 1;




const STATE_BREAK = 2;




const STATE_NO_ECHO = 4;




protected $_line = null;




protected $_lineCurrent = 0;




protected $_lineLength = 0;




protected $_buffer = null;




protected $_mapping = [];




protected $_history = [];




protected $_historyCurrent = 0;




protected $_historySize = 0;




protected $_prefix = null;




protected $_autocompleter = null;




public function __construct()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

$this->_mapping["\033[A"] = [$this, '_bindArrowUp'];
$this->_mapping["\033[B"] = [$this, '_bindArrowDown'];
$this->_mapping["\033[C"] = [$this, '_bindArrowRight'];
$this->_mapping["\033[D"] = [$this, '_bindArrowLeft'];
$this->_mapping["\001"] = [$this, '_bindControlA'];
$this->_mapping["\002"] = [$this, '_bindControlB'];
$this->_mapping["\005"] = [$this, '_bindControlE'];
$this->_mapping["\006"] = [$this, '_bindControlF'];
$this->_mapping["\010"] =
$this->_mapping["\177"] = [$this, '_bindBackspace'];
$this->_mapping["\027"] = [$this, '_bindControlW'];
$this->_mapping["\n"] = [$this, '_bindNewline'];
$this->_mapping["\t"] = [$this, '_bindTab'];

return;
}




public function readLine(string $prefix = null)
{
$input = Console::getInput();

if (true === $input->eof()) {
return false;
}

$direct = Console::isDirect($input->getStream()->getStream());
$output = Console::getOutput();

if (false === $direct || \defined('PHP_WINDOWS_VERSION_PLATFORM')) {
$out = $input->readLine();

if (false === $out) {
return false;
}

$out = \substr($out, 0, -1);

if (true === $direct) {
$output->writeAll($prefix);
} else {
$output->writeAll($prefix.$out."\n");
}

return $out;
}

$this->resetLine();
$this->setPrefix($prefix);
$read = [$input->getStream()->getStream()];
$write = $except = [];
$output->writeAll($prefix);

while (true) {
@\stream_select($read, $write, $except, 30, 0);

if (empty($read)) {
$read = [$input->getStream()->getStream()];

continue;
}

$char = $this->_read();
$this->_buffer = $char;
$return = $this->_readLine($char);

if (0 === ($return & self::STATE_NO_ECHO)) {
$output->writeAll($this->_buffer);
}

if (0 !== ($return & self::STATE_BREAK)) {
break;
}
}

return $this->getLine();
}




public function _readLine(string $char)
{
if (isset($this->_mapping[$char]) &&
\is_callable($this->_mapping[$char])) {
$mapping = $this->_mapping[$char];

return $mapping($this);
}

if (isset($this->_mapping[$char])) {
$this->_buffer = $this->_mapping[$char];
} elseif (false === Ustring::isCharPrintable($char)) {
ConsoleCursor::bip();

return static::STATE_CONTINUE | static::STATE_NO_ECHO;
}

if ($this->getLineLength() === $this->getLineCurrent()) {
$this->appendLine($this->_buffer);

return static::STATE_CONTINUE;
}

$this->insertLine($this->_buffer);
$tail = \mb_substr(
$this->getLine(),
$this->getLineCurrent() - 1
);
$this->_buffer = "\033[K".$tail.\str_repeat(
"\033[D",
\mb_strlen($tail) - 1
);

return static::STATE_CONTINUE;
}




public function addMappings(array $mappings)
{
foreach ($mappings as $key => $mapping) {
$this->addMapping($key, $mapping);
}
}










public function addMapping(string $key, $mapping)
{
if ('\e[' === \substr($key, 0, 3)) {
$this->_mapping["\033[".\substr($key, 3)] = $mapping;
} elseif ('\C-' === \substr($key, 0, 3)) {
$_key = \ord(\strtolower(\substr($key, 3))) - 96;
$this->_mapping[\chr($_key)] = $mapping;
} else {
$this->_mapping[$key] = $mapping;
}
}




public function addHistory(string $line = null)
{
if (empty($line)) {
return;
}

$this->_history[] = $line;
$this->_historyCurrent = $this->_historySize++;
}




public function clearHistory()
{
unset($this->_history);
$this->_history = [];
$this->_historyCurrent = 0;
$this->_historySize = 1;
}




public function getHistory(int $i = null)
{
if (null === $i) {
$i = $this->_historyCurrent;
}

if (!isset($this->_history[$i])) {
return null;
}

return $this->_history[$i];
}




public function previousHistory()
{
if (0 >= $this->_historyCurrent) {
return $this->getHistory(0);
}

return $this->getHistory($this->_historyCurrent--);
}




public function nextHistory()
{
if ($this->_historyCurrent + 1 >= $this->_historySize) {
return $this->getLine();
}

return $this->getHistory(++$this->_historyCurrent);
}




public function getLine()
{
return $this->_line;
}




public function appendLine(string $append)
{
$this->_line .= $append;
$this->_lineLength = \mb_strlen($this->_line);
$this->_lineCurrent = $this->_lineLength;
}




public function insertLine(string $insert)
{
if ($this->_lineLength === $this->_lineCurrent) {
return $this->appendLine($insert);
}

$this->_line = \mb_substr($this->_line, 0, $this->_lineCurrent).
$insert.
\mb_substr($this->_line, $this->_lineCurrent);
$this->_lineLength = \mb_strlen($this->_line);
$this->_lineCurrent += \mb_strlen($insert);

return;
}




protected function resetLine()
{
$this->_line = null;
$this->_lineCurrent = 0;
$this->_lineLength = 0;
}




public function getLineCurrent(): int
{
return $this->_lineCurrent;
}






public function getLineLength(): int
{
return $this->_lineLength;
}




public function setPrefix(string $prefix)
{
$this->_prefix = $prefix;
}




public function getPrefix()
{
return $this->_prefix;
}




public function getBuffer()
{
return $this->_buffer;
}




public function setAutocompleter(Autocompleter $autocompleter)
{
$old = $this->_autocompleter;
$this->_autocompleter = $autocompleter;

return $old;
}






public function getAutocompleter()
{
return $this->_autocompleter;
}




public function _read(int $length = 512): string
{
return Console::getInput()->read($length);
}




public function setLine(string $line)
{
$this->_line = $line;
$this->_lineLength = \mb_strlen($this->_line ?: '');
$this->_lineCurrent = $this->_lineLength;
}




public function setLineCurrent(int $current)
{
$this->_lineCurrent = $current;
}




public function setLineLength(int $length)
{
$this->_lineLength = $length;
}




public function setBuffer(string $buffer)
{
$this->_buffer = $buffer;
}





public function _bindArrowUp(self $self): int
{
if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) {
ConsoleCursor::clear('↔');
Console::getOutput()->writeAll($self->getPrefix());
}
$buffer = $self->previousHistory() ?? '';
$self->setBuffer($buffer);
$self->setLine($buffer);

return static::STATE_CONTINUE;
}





public function _bindArrowDown(self $self): int
{
if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) {
ConsoleCursor::clear('↔');
Console::getOutput()->writeAll($self->getPrefix());
}

$self->setBuffer($buffer = $self->nextHistory());
$self->setLine($buffer);

return static::STATE_CONTINUE;
}





public function _bindArrowRight(self $self): int
{
if ($self->getLineLength() > $self->getLineCurrent()) {
if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) {
ConsoleCursor::move('→');
}

$self->setLineCurrent($self->getLineCurrent() + 1);
}

$self->setBuffer('');

return static::STATE_CONTINUE;
}





public function _bindArrowLeft(self $self): int
{
if (0 < $self->getLineCurrent()) {
if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) {
ConsoleCursor::move('←');
}

$self->setLineCurrent($self->getLineCurrent() - 1);
}

$self->setBuffer('');

return static::STATE_CONTINUE;
}





public function _bindBackspace(self $self): int
{
$buffer = '';

if (0 < $self->getLineCurrent()) {
if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) {
ConsoleCursor::move('←');
ConsoleCursor::clear('→');
}

if ($self->getLineLength() === $current = $self->getLineCurrent()) {
$self->setLine(\mb_substr($self->getLine(), 0, -1));
} else {
$line = $self->getLine();
$current = $self->getLineCurrent();
$tail = \mb_substr($line, $current);
$buffer = $tail.\str_repeat("\033[D", \mb_strlen($tail));
$self->setLine(\mb_substr($line, 0, $current - 1).$tail);
$self->setLineCurrent($current - 1);
}
}

$self->setBuffer($buffer);

return static::STATE_CONTINUE;
}





public function _bindControlA(self $self): int
{
for ($i = $self->getLineCurrent() - 1; 0 <= $i; --$i) {
$self->_bindArrowLeft($self);
}

return static::STATE_CONTINUE;
}





public function _bindControlB(self $self): int
{
$current = $self->getLineCurrent();

if (0 === $current) {
return static::STATE_CONTINUE;
}

$words = \preg_split(
'#\b#u',
$self->getLine(),
-1,
\PREG_SPLIT_OFFSET_CAPTURE | \PREG_SPLIT_NO_EMPTY
);

for (
$i = 0, $max = \count($words) - 1;
$i < $max && $words[$i + 1][1] < $current;
++$i
) {
}

for ($j = $words[$i][1] + 1; $current >= $j; ++$j) {
$self->_bindArrowLeft($self);
}

return static::STATE_CONTINUE;
}





public function _bindControlE(self $self): int
{
for (
$i = $self->getLineCurrent(), $max = $self->getLineLength();
$i < $max;
++$i
) {
$self->_bindArrowRight($self);
}

return static::STATE_CONTINUE;
}





public function _bindControlF(self $self): int
{
$current = $self->getLineCurrent();

if ($self->getLineLength() === $current) {
return static::STATE_CONTINUE;
}

$words = \preg_split(
'#\b#u',
$self->getLine(),
-1,
\PREG_SPLIT_OFFSET_CAPTURE | \PREG_SPLIT_NO_EMPTY
);

for (
$i = 0, $max = \count($words) - 1;
$i < $max && $words[$i][1] < $current;
++$i
) {
}

if (!isset($words[$i + 1])) {
$words[$i + 1] = [1 => $self->getLineLength()];
}

for ($j = $words[$i + 1][1]; $j > $current; --$j) {
$self->_bindArrowRight($self);
}

return static::STATE_CONTINUE;
}





public function _bindControlW(self $self): int
{
$current = $self->getLineCurrent();

if (0 === $current) {
return static::STATE_CONTINUE;
}

$words = \preg_split(
'#\b#u',
$self->getLine(),
-1,
\PREG_SPLIT_OFFSET_CAPTURE | \PREG_SPLIT_NO_EMPTY
);

for (
$i = 0, $max = \count($words) - 1;
$i < $max && $words[$i + 1][1] < $current;
++$i
) {
}

for ($j = $words[$i][1] + 1; $current >= $j; ++$j) {
$self->_bindBackspace($self);
}

return static::STATE_CONTINUE;
}




public function _bindNewline(self $self): int
{
$self->addHistory($self->getLine());

return static::STATE_BREAK;
}




public function _bindTab(self $self): int
{
$output = Console::getOutput();
$autocompleter = $self->getAutocompleter();
$state = static::STATE_CONTINUE | static::STATE_NO_ECHO;

if (null === $autocompleter) {
return $state;
}

$current = $self->getLineCurrent();
$line = $self->getLine();

if (0 === $current) {
return $state;
}

$matches = \preg_match_all(
'#'.$autocompleter->getWordDefinition().'$#u',
\mb_substr($line, 0, $current),
$words
);

if (0 === $matches) {
return $state;
}

$word = $words[0][0];

if ('' === \trim($word)) {
return $state;
}

$solution = $autocompleter->complete($word);
$length = \mb_strlen($word);

if (null === $solution) {
return $state;
}

if (\is_array($solution)) {
$_solution = $solution;
$count = \count($_solution) - 1;
$cWidth = 0;
$window = ConsoleWindow::getSize();
$wWidth = $window['x'];
$cursor = ConsoleCursor::getPosition();

\array_walk($_solution, function (&$value) use (&$cWidth) {
$handle = \mb_strlen($value);

if ($handle > $cWidth) {
$cWidth = $handle;
}

return;
});
\array_walk($_solution, function (&$value) use (&$cWidth) {
$handle = \mb_strlen($value);

if ($handle >= $cWidth) {
return;
}

$value .= \str_repeat(' ', $cWidth - $handle);

return;
});

$mColumns = (int) \floor($wWidth / ($cWidth + 2));
$mLines = (int) \ceil(($count + 1) / $mColumns);
--$mColumns;
$i = 0;

if (0 > $window['y'] - $cursor['y'] - $mLines) {
ConsoleWindow::scroll('↑', $mLines);
ConsoleCursor::move('↑', $mLines);
}

ConsoleCursor::save();
ConsoleCursor::hide();
ConsoleCursor::move('↓ LEFT');
ConsoleCursor::clear('↓');

foreach ($_solution as $j => $s) {
$output->writeAll("\033[0m".$s."\033[0m");

if ($i++ < $mColumns) {
$output->writeAll('  ');
} else {
$i = 0;

if (isset($_solution[$j + 1])) {
$output->writeAll("\n");
}
}
}

ConsoleCursor::restore();
ConsoleCursor::show();

++$mColumns;
$input = Console::getInput();
$read = [$input->getStream()->getStream()];
$write = $except = [];
$mColumn = -1;
$mLine = -1;
$coord = -1;
$unselect = function () use (
&$mColumn,
&$mLine,
&$coord,
&$_solution,
&$cWidth,
$output
) {
ConsoleCursor::save();
ConsoleCursor::hide();
ConsoleCursor::move('↓ LEFT');
ConsoleCursor::move('→', $mColumn * ($cWidth + 2));
ConsoleCursor::move('↓', $mLine);
$output->writeAll("\033[0m".$_solution[$coord]."\033[0m");
ConsoleCursor::restore();
ConsoleCursor::show();

return;
};
$select = function () use (
&$mColumn,
&$mLine,
&$coord,
&$_solution,
&$cWidth,
$output
) {
ConsoleCursor::save();
ConsoleCursor::hide();
ConsoleCursor::move('↓ LEFT');
ConsoleCursor::move('→', $mColumn * ($cWidth + 2));
ConsoleCursor::move('↓', $mLine);
$output->writeAll("\033[7m".$_solution[$coord]."\033[0m");
ConsoleCursor::restore();
ConsoleCursor::show();

return;
};
$init = function () use (
&$mColumn,
&$mLine,
&$coord,
&$select
) {
$mColumn = 0;
$mLine = 0;
$coord = 0;
$select();

return;
};

while (true) {
@\stream_select($read, $write, $except, 30, 0);

if (empty($read)) {
$read = [$input->getStream()->getStream()];

continue;
}

switch ($char = $self->_read()) {
case "\033[A":
if (-1 === $mColumn && -1 === $mLine) {
$init();

break;
}

$unselect();
$coord = \max(0, $coord - $mColumns);
$mLine = (int) \floor($coord / $mColumns);
$mColumn = $coord % $mColumns;
$select();

break;

case "\033[B":
if (-1 === $mColumn && -1 === $mLine) {
$init();

break;
}

$unselect();
$coord = \min($count, $coord + $mColumns);
$mLine = (int) \floor($coord / $mColumns);
$mColumn = $coord % $mColumns;
$select();

break;

case "\t":
case "\033[C":
if (-1 === $mColumn && -1 === $mLine) {
$init();

break;
}

$unselect();
$coord = \min($count, $coord + 1);
$mLine = (int) \floor($coord / $mColumns);
$mColumn = $coord % $mColumns;
$select();

break;

case "\033[D":
if (-1 === $mColumn && -1 === $mLine) {
$init();

break;
}

$unselect();
$coord = \max(0, $coord - 1);
$mLine = (int) \floor($coord / $mColumns);
$mColumn = $coord % $mColumns;
$select();

break;

case "\n":
if (-1 !== $mColumn && -1 !== $mLine) {
$tail = \mb_substr($line, $current);
$current -= $length;
$self->setLine(
\mb_substr($line, 0, $current).
$solution[$coord].
$tail
);
$self->setLineCurrent(
$current + \mb_strlen($solution[$coord])
);

ConsoleCursor::move('←', $length);
$output->writeAll($solution[$coord]);
ConsoleCursor::clear('→');
$output->writeAll($tail);
ConsoleCursor::move('←', \mb_strlen($tail));
}


default:
$mColumn = -1;
$mLine = -1;
$coord = -1;
ConsoleCursor::save();
ConsoleCursor::move('↓ LEFT');
ConsoleCursor::clear('↓');
ConsoleCursor::restore();

if ("\033" !== $char && "\n" !== $char) {
$self->setBuffer($char);

return $self->_readLine($char);
}

break 2;
}
}

return $state;
}

$tail = \mb_substr($line, $current);
$current -= $length;
$self->setLine(
\mb_substr($line, 0, $current).
$solution.
$tail
);
$self->setLineCurrent(
$current + \mb_strlen($solution)
);

ConsoleCursor::move('←', $length);
$output->writeAll($solution);
ConsoleCursor::clear('→');
$output->writeAll($tail);
ConsoleCursor::move('←', \mb_strlen($tail));

return $state;
}
}




Console::advancedInteraction();
<?php



































namespace Psy\Readline\Hoa;






class FileLink extends File
{



public function __construct(
string $streamName,
string $mode,
string $context = null,
bool $wait = false
) {
if (!\is_link($streamName)) {
throw new FileException('File %s is not a link.', 0, $streamName);
}

parent::__construct($streamName, $mode, $context, $wait);

return;
}




public function getStatistic(): array
{
return \lstat($this->getStreamName());
}




public function changeGroup($group): bool
{
return \lchgrp($this->getStreamName(), $group);
}




public function changeOwner($user): bool
{
return \lchown($this->getStreamName(), $user);
}




public function getPermissions(): int
{
return 41453; 
}




public function getTarget(): FileGeneric
{
$target = \dirname($this->getStreamName()).\DIRECTORY_SEPARATOR.
$this->getTargetName();
$context = null !== $this->getStreamContext()
? $this->getStreamContext()->getCurrentId()
: null;

if (true === \is_link($target)) {
return new FileLinkReadWrite(
$target,
File::MODE_APPEND_READ_WRITE,
$context
);
} elseif (true === \is_file($target)) {
return new FileReadWrite(
$target,
File::MODE_APPEND_READ_WRITE,
$context
);
} elseif (true === \is_dir($target)) {
return new FileDirectory(
$target,
File::MODE_READ,
$context
);
}

throw new FileException('Cannot find an appropriated object that matches with '.'path %s when defining it.', 1, $target);
}




public function getTargetName(): string
{
return \readlink($this->getStreamName());
}




public static function create(string $name, string $target): bool
{
if (false !== \linkinfo($name)) {
return true;
}

return \symlink($target, $name);
}
}
<?php



































namespace Psy\Readline\Hoa;






class ConsoleCursor
{













public static function move(string $steps, int $repeat = 1)
{
if (1 > $repeat) {
return;
} elseif (1 === $repeat) {
$handle = \explode(' ', $steps);
} else {
$handle = \explode(' ', $steps, 1);
}

$tput = Console::getTput();
$output = Console::getOutput();

foreach ($handle as $step) {
switch ($step) {
case 'u':
case 'up':
case '↑':
$output->writeAll(
\str_replace(
'%p1%d',
$repeat,
$tput->get('parm_up_cursor')
)
);

break;

case 'U':
case 'UP':
static::moveTo(null, 1);

break;

case 'r':
case 'right':
case '→':
$output->writeAll(
\str_replace(
'%p1%d',
$repeat,
$tput->get('parm_right_cursor')
)
);

break;

case 'R':
case 'RIGHT':
static::moveTo(9999);

break;

case 'd':
case 'down':
case '↓':
$output->writeAll(
\str_replace(
'%p1%d',
$repeat,
$tput->get('parm_down_cursor')
)
);

break;

case 'D':
case 'DOWN':
static::moveTo(null, 9999);

break;

case 'l':
case 'left':
case '←':
$output->writeAll(
\str_replace(
'%p1%d',
$repeat,
$tput->get('parm_left_cursor')
)
);

break;

case 'L':
case 'LEFT':
static::moveTo(1);

break;
}
}
}





public static function moveTo(int $x = null, int $y = null)
{
if (null === $x || null === $y) {
$position = static::getPosition();

if (null === $x) {
$x = $position['x'];
}

if (null === $y) {
$y = $position['y'];
}
}

Console::getOutput()->writeAll(
\str_replace(
['%i%p1%d', '%p2%d'],
[$y, $x],
Console::getTput()->get('cursor_address')
)
);
}




public static function getPosition(): array
{
$tput = Console::getTput();
$user7 = $tput->get('user7');

if (null === $user7) {
return [
'x' => 0,
'y' => 0,
];
}

Console::getOutput()->writeAll($user7);

$input = Console::getInput();


$input->read(2); 

$x = null;
$y = null;
$handle = &$y;

while (true) {
$char = $input->readCharacter();

switch ($char) {
case ';':
$handle = &$x;

break;

case 'R':
break 2;

default:
$handle .= $char;
}
}

return [
'x' => (int) $x,
'y' => (int) $y,
];
}




public static function save()
{
Console::getOutput()->writeAll(
Console::getTput()->get('save_cursor')
);
}




public static function restore()
{
Console::getOutput()->writeAll(
Console::getTput()->get('restore_cursor')
);
}












public static function clear(string $parts = 'all')
{
$tput = Console::getTput();
$output = Console::getOutput();

foreach (\explode(' ', $parts) as $part) {
switch ($part) {
case 'a':
case 'all':
case '↕':
$output->writeAll($tput->get('clear_screen'));
static::moveTo(1, 1);

break;

case 'u':
case 'up':
case '↑':
$output->writeAll("\033[1J");

break;

case 'r':
case 'right':
case '→':
$output->writeAll($tput->get('clr_eol'));

break;

case 'd':
case 'down':
case '↓':
$output->writeAll($tput->get('clr_eos'));

break;

case 'l':
case 'left':
case '←':
$output->writeAll($tput->get('clr_bol'));

break;

case 'line':
case '↔':
$output->writeAll("\r".$tput->get('clr_eol'));

break;
}
}
}




public static function hide()
{
Console::getOutput()->writeAll(
Console::getTput()->get('cursor_invisible')
);
}




public static function show()
{
Console::getOutput()->writeAll(
Console::getTput()->get('cursor_visible')
);
}





























public static function colorize(string $attributes)
{
static $_rgbTo256 = null;

if (null === $_rgbTo256) {
$_rgbTo256 = [
'000000', '800000', '008000', '808000', '000080', '800080',
'008080', 'c0c0c0', '808080', 'ff0000', '00ff00', 'ffff00',
'0000ff', 'ff00ff', '00ffff', 'ffffff', '000000', '00005f',
'000087', '0000af', '0000d7', '0000ff', '005f00', '005f5f',
'005f87', '005faf', '005fd7', '005fff', '008700', '00875f',
'008787', '0087af', '0087d7', '0087ff', '00af00', '00af5f',
'00af87', '00afaf', '00afd7', '00afff', '00d700', '00d75f',
'00d787', '00d7af', '00d7d7', '00d7ff', '00ff00', '00ff5f',
'00ff87', '00ffaf', '00ffd7', '00ffff', '5f0000', '5f005f',
'5f0087', '5f00af', '5f00d7', '5f00ff', '5f5f00', '5f5f5f',
'5f5f87', '5f5faf', '5f5fd7', '5f5fff', '5f8700', '5f875f',
'5f8787', '5f87af', '5f87d7', '5f87ff', '5faf00', '5faf5f',
'5faf87', '5fafaf', '5fafd7', '5fafff', '5fd700', '5fd75f',
'5fd787', '5fd7af', '5fd7d7', '5fd7ff', '5fff00', '5fff5f',
'5fff87', '5fffaf', '5fffd7', '5fffff', '870000', '87005f',
'870087', '8700af', '8700d7', '8700ff', '875f00', '875f5f',
'875f87', '875faf', '875fd7', '875fff', '878700', '87875f',
'878787', '8787af', '8787d7', '8787ff', '87af00', '87af5f',
'87af87', '87afaf', '87afd7', '87afff', '87d700', '87d75f',
'87d787', '87d7af', '87d7d7', '87d7ff', '87ff00', '87ff5f',
'87ff87', '87ffaf', '87ffd7', '87ffff', 'af0000', 'af005f',
'af0087', 'af00af', 'af00d7', 'af00ff', 'af5f00', 'af5f5f',
'af5f87', 'af5faf', 'af5fd7', 'af5fff', 'af8700', 'af875f',
'af8787', 'af87af', 'af87d7', 'af87ff', 'afaf00', 'afaf5f',
'afaf87', 'afafaf', 'afafd7', 'afafff', 'afd700', 'afd75f',
'afd787', 'afd7af', 'afd7d7', 'afd7ff', 'afff00', 'afff5f',
'afff87', 'afffaf', 'afffd7', 'afffff', 'd70000', 'd7005f',
'd70087', 'd700af', 'd700d7', 'd700ff', 'd75f00', 'd75f5f',
'd75f87', 'd75faf', 'd75fd7', 'd75fff', 'd78700', 'd7875f',
'd78787', 'd787af', 'd787d7', 'd787ff', 'd7af00', 'd7af5f',
'd7af87', 'd7afaf', 'd7afd7', 'd7afff', 'd7d700', 'd7d75f',
'd7d787', 'd7d7af', 'd7d7d7', 'd7d7ff', 'd7ff00', 'd7ff5f',
'd7ff87', 'd7ffaf', 'd7ffd7', 'd7ffff', 'ff0000', 'ff005f',
'ff0087', 'ff00af', 'ff00d7', 'ff00ff', 'ff5f00', 'ff5f5f',
'ff5f87', 'ff5faf', 'ff5fd7', 'ff5fff', 'ff8700', 'ff875f',
'ff8787', 'ff87af', 'ff87d7', 'ff87ff', 'ffaf00', 'ffaf5f',
'ffaf87', 'ffafaf', 'ffafd7', 'ffafff', 'ffd700', 'ffd75f',
'ffd787', 'ffd7af', 'ffd7d7', 'ffd7ff', 'ffff00', 'ffff5f',
'ffff87', 'ffffaf', 'ffffd7', 'ffffff', '080808', '121212',
'1c1c1c', '262626', '303030', '3a3a3a', '444444', '4e4e4e',
'585858', '606060', '666666', '767676', '808080', '8a8a8a',
'949494', '9e9e9e', 'a8a8a8', 'b2b2b2', 'bcbcbc', 'c6c6c6',
'd0d0d0', 'dadada', 'e4e4e4', 'eeeeee',
];
}

$tput = Console::getTput();

if (1 >= $tput->count('max_colors')) {
return;
}

$handle = [];

foreach (\explode(' ', $attributes) as $attribute) {
switch ($attribute) {
case 'n':
case 'normal':
$handle[] = 0;

break;

case 'b':
case 'bold':
$handle[] = 1;

break;

case 'u':
case 'underlined':
$handle[] = 4;

break;

case 'bl':
case 'blink':
$handle[] = 5;

break;

case 'i':
case 'inverse':
$handle[] = 7;

break;

case '!b':
case '!bold':
$handle[] = 22;

break;

case '!u':
case '!underlined':
$handle[] = 24;

break;

case '!bl':
case '!blink':
$handle[] = 25;

break;

case '!i':
case '!inverse':
$handle[] = 27;

break;

default:
if (0 === \preg_match('#^([^\(]+)\(([^\)]+)\)$#', $attribute, $m)) {
break;
}

$shift = 0;

switch ($m[1]) {
case 'fg':
case 'foreground':
$shift = 0;

break;

case 'bg':
case 'background':
$shift = 10;

break;

default:
break 2;
}

$_handle = 0;
$_keyword = true;

switch ($m[2]) {
case 'black':
$_handle = 30;

break;

case 'red':
$_handle = 31;

break;

case 'green':
$_handle = 32;

break;

case 'yellow':
$_handle = 33;

break;

case 'blue':
$_handle = 34;

break;

case 'magenta':
$_handle = 35;

break;

case 'cyan':
$_handle = 36;

break;

case 'white':
$_handle = 37;

break;

case 'default':
$_handle = 39;

break;

default:
$_keyword = false;

if (256 <= $tput->count('max_colors') &&
'#' === $m[2][0]) {
$rgb = \hexdec(\substr($m[2], 1));
$r = ($rgb >> 16) & 255;
$g = ($rgb >> 8) & 255;
$b = $rgb & 255;
$distance = null;

foreach ($_rgbTo256 as $i => $_rgb) {
$_rgb = \hexdec($_rgb);
$_r = ($_rgb >> 16) & 255;
$_g = ($_rgb >> 8) & 255;
$_b = $_rgb & 255;

$d = \sqrt(
($_r - $r) ** 2
+ ($_g - $g) ** 2
+ ($_b - $b) ** 2
);

if (null === $distance ||
$d <= $distance) {
$distance = $d;
$_handle = $i;
}
}
} else {
$_handle = (int) ($m[2]);
}
}

if (true === $_keyword) {
$handle[] = $_handle + $shift;
} else {
$handle[] = (38 + $shift).';5;'.$_handle;
}
}
}

Console::getOutput()->writeAll("\033[".\implode(';', $handle).'m');

return;
}




public static function changeColor(int $fromCode, int $toColor)
{
$tput = Console::getTput();

if (true !== $tput->has('can_change')) {
return;
}

$r = ($toColor >> 16) & 255;
$g = ($toColor >> 8) & 255;
$b = $toColor & 255;

Console::getOutput()->writeAll(
\str_replace(
[
'%p1%d',
'rgb:',
'%p2%{255}%*%{1000}%/%2.2X/',
'%p3%{255}%*%{1000}%/%2.2X/',
'%p4%{255}%*%{1000}%/%2.2X',
],
[
$fromCode,
'',
\sprintf('%02x', $r),
\sprintf('%02x', $g),
\sprintf('%02x', $b),
],
$tput->get('initialize_color')
)
);

return;
}








public static function setStyle(string $style, bool $blink = true)
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

switch ($style) {
case 'u':
case 'underline':
case '_':
$_style = 2;

break;

case 'v':
case 'vertical':
case '|':
$_style = 5;

break;

case 'b':
case 'block':
case '▋':
default:
$_style = 1;

break;
}

if (false === $blink) {
++$_style;
}


Console::getOutput()->writeAll("\033[".$_style.' q');

return;
}




public static function bip()
{
Console::getOutput()->writeAll(
Console::getTput()->get('bell')
);
}
}




Console::advancedInteraction();
<?php



































namespace Psy\Readline\Hoa;








class ConsoleWindow implements EventSource
{



private static $_instance = null;






private function __construct()
{
Event::register(
'hoa://Event/Console/Window:resize',
$this
);

return;
}




public static function getInstance(): self
{
if (null === static::$_instance) {
static::$_instance = new self();
}

return static::$_instance;
}




public static function setSize(int $x, int $y)
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

Console::getOutput()->writeAll("\033[8;".$y.';'.$x.'t');

return;
}




public static function getSize(): array
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
$modecon = \explode("\n", \ltrim(ConsoleProcessus::execute('mode con')));

$_y = \trim($modecon[2]);
\preg_match('#[^:]+:\s*([0-9]+)#', $_y, $matches);
$y = (int) $matches[1];

$_x = \trim($modecon[3]);
\preg_match('#[^:]+:\s*([0-9]+)#', $_x, $matches);
$x = (int) $matches[1];

return [
'x' => $x,
'y' => $y,
];
}

$term = '';

if (isset($_SERVER['TERM'])) {
$term = 'TERM="'.$_SERVER['TERM'].'" ';
}

$command = $term.'tput cols && '.$term.'tput lines';
$tput = ConsoleProcessus::execute($command, false);

if (!empty($tput)) {
list($x, $y) = \explode("\n", $tput);

return [
'x' => (int) $x,
'y' => (int) $y,
];
}


Console::getOutput()->writeAll("\033[18t");

$input = Console::getInput();


$input->read(4); 

$x = null;
$y = null;
$handle = &$y;

while (true) {
$char = $input->readCharacter();

switch ($char) {
case ';':
$handle = &$x;

break;

case 't':
break 2;

default:
if (false === \ctype_digit($char)) {
break 2;
}

$handle .= $char;
}
}

if (null === $x || null === $y) {
return [
'x' => 0,
'y' => 0,
];
}

return [
'x' => (int) $x,
'y' => (int) $y,
];
}




public static function moveTo(int $x, int $y)
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}


Console::getOutput()->writeAll("\033[3;".$x.';'.$y.'t');

return;
}




public static function getPosition(): array
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return ['x' => 0, 'y' => 0];
}


Console::getOutput()->writeAll("\033[13t");

$input = Console::getInput();


$input->read(4); 

$x = null;
$y = null;
$handle = &$x;

while (true) {
$char = $input->readCharacter();

switch ($char) {
case ';':
$handle = &$y;

break;

case 't':
break 2;

default:
$handle .= $char;
}
}

return [
'x' => (int) $x,
'y' => (int) $y,
];
}








public static function scroll(string $directions, int $repeat = 1)
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

if (1 > $repeat) {
return;
} elseif (1 === $repeat) {
$handle = \explode(' ', $directions);
} else {
$handle = \explode(' ', $directions, 1);
}

$tput = Console::getTput();
$count = ['up' => 0, 'down' => 0];

foreach ($handle as $direction) {
switch ($direction) {
case 'u':
case 'up':
case '↑':
++$count['up'];

break;

case 'd':
case 'down':
case '↓':
++$count['down'];

break;
}
}

$output = Console::getOutput();

if (0 < $count['up']) {
$output->writeAll(
\str_replace(
'%p1%d',
$count['up'] * $repeat,
$tput->get('parm_index')
)
);
}

if (0 < $count['down']) {
$output->writeAll(
\str_replace(
'%p1%d',
$count['down'] * $repeat,
$tput->get('parm_rindex')
)
);
}

return;
}




public static function minimize()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}


Console::getOutput()->writeAll("\033[2t");

return;
}




public static function restore()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

Console::getOutput()->writeAll("\033[1t");

return;
}




public static function raise()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

Console::getOutput()->writeAll("\033[5t");

return;
}




public static function lower()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

Console::getOutput()->writeAll("\033[6t");

return;
}




public static function setTitle(string $title)
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}


Console::getOutput()->writeAll("\033]0;".$title."\033\\");

return;
}




public static function getTitle()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return null;
}


Console::getOutput()->writeAll("\033[21t");

$input = Console::getInput();
$read = [$input->getStream()->getStream()];
$write = [];
$except = [];
$out = null;

if (0 === \stream_select($read, $write, $except, 0, 50000)) {
return $out;
}


$input->read(3); 

while (true) {
$char = $input->readCharacter();

if ("\033" === $char) {
$chaar = $input->readCharacter();

if ('\\' === $chaar) {
break;
}

$char .= $chaar;
}

$out .= $char;
}

return $out;
}




public static function getLabel()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return null;
}


Console::getOutput()->writeAll("\033[20t");

$input = Console::getInput();
$read = [$input->getStream()->getStream()];
$write = [];
$except = [];
$out = null;

if (0 === \stream_select($read, $write, $except, 0, 50000)) {
return $out;
}


$input->read(3); 

while (true) {
$char = $input->readCharacter();

if ("\033" === $char) {
$chaar = $input->readCharacter();

if ('\\' === $chaar) {
break;
}

$char .= $chaar;
}

$out .= $char;
}

return $out;
}




public static function refresh()
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}


Console::getOutput()->writeAll("\033[7t");

return;
}




public static function copy(string $data)
{
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return;
}

$out = "\033]52;;".\base64_encode($data)."\033\\";
$output = Console::getOutput();
$considerMultiplexer = $output->considerMultiplexer(true);

$output->writeAll($out);
$output->considerMultiplexer($considerMultiplexer);

return;
}
}




Console::advancedInteraction();




if (\function_exists('pcntl_signal')) {
ConsoleWindow::getInstance();
\pcntl_signal(
\SIGWINCH,
function () {
static $_window = null;

if (null === $_window) {
$_window = ConsoleWindow::getInstance();
}

Event::notify(
'hoa://Event/Console/Window:resize',
$_window,
new EventBucket([
'size' => ConsoleWindow::getSize(),
])
);
}
);
}
<?php



































namespace Psy\Readline\Hoa;






interface Autocompleter
{




public function complete(&$prefix);





public function getWordDefinition(): string;
}
<?php



































namespace Psy\Readline\Hoa;






class AutocompleterWord implements Autocompleter
{



protected $_words = null;




public function __construct(array $words)
{
$this->setWords($words);
}









public function complete(&$prefix)
{
$out = [];
$length = \mb_strlen($prefix);

foreach ($this->getWords() as $word) {
if (\mb_substr($word, 0, $length) === $prefix) {
$out[] = $word;
}
}

if (empty($out)) {
return null;
}

if (1 === \count($out)) {
return $out[0];
}

return $out;
}




public function getWordDefinition(): string
{
return '\b\w+';
}








public function setWords(array $words)
{
$old = $this->_words;
$this->_words = $words;

return $old;
}




public function getWords(): array
{
return $this->_words;
}
}
<?php



































namespace Psy\Readline\Hoa;






class FileFinder implements \IteratorAggregate
{



protected $_splFileInfo = \SplFileInfo::class;




protected $_paths = [];




protected $_maxDepth = -1;




protected $_filters = [];




protected $_flags = -1;




protected $_types = [];




protected $_first = -1;




protected $_sorts = [];




public function __construct()
{
$this->_flags = IteratorFileSystem::KEY_AS_PATHNAME
| IteratorFileSystem::CURRENT_AS_FILEINFO
| IteratorFileSystem::SKIP_DOTS;
$this->_first = \RecursiveIteratorIterator::SELF_FIRST;

return;
}




public function in($paths): self
{
if (!\is_array($paths)) {
$paths = [$paths];
}

foreach ($paths as $path) {
if (1 === \preg_match('/[\*\?\[\]]/', $path)) {
$iterator = new \CallbackFilterIterator(
new \GlobIterator(\rtrim($path, \DIRECTORY_SEPARATOR)),
function ($current) {
return $current->isDir();
}
);

foreach ($iterator as $fileInfo) {
$this->_paths[] = $fileInfo->getPathname();
}
} else {
$this->_paths[] = $path;
}
}

return $this;
}




public function maxDepth(int $depth): self
{
$this->_maxDepth = $depth;

return $this;
}




public function files(): self
{
$this->_types[] = 'file';

return $this;
}




public function directories(): self
{
$this->_types[] = 'dir';

return $this;
}




public function links(): self
{
$this->_types[] = 'link';

return $this;
}




public function followSymlinks(bool $flag = true): self
{
if (true === $flag) {
$this->_flags ^= IteratorFileSystem::FOLLOW_SYMLINKS;
} else {
$this->_flags |= IteratorFileSystem::FOLLOW_SYMLINKS;
}

return $this;
}






public function name(string $regex): self
{
$this->_filters[] = function (\SplFileInfo $current) use ($regex) {
return 0 !== \preg_match($regex, $current->getBasename());
};

return $this;
}






public function notIn(string $regex): self
{
$this->_filters[] = function (\SplFileInfo $current) use ($regex) {
foreach (\explode(\DIRECTORY_SEPARATOR, $current->getPathname()) as $part) {
if (0 !== \preg_match($regex, $part)) {
return false;
}
}

return true;
};

return $this;
}












public function size(string $size): self
{
if (0 === \preg_match('#^(<|<=|>|>=|=)\s*(\d+)\s*((?:[KMGTPEZY])b)?$#', $size, $matches)) {
return $this;
}

$number = (float) ($matches[2]);
$unit = $matches[3] ?? 'b';
$operator = $matches[1];

switch ($unit) {
case 'b':
break;


case 'Kb':
$number <<= 10;

break;


case 'Mb':
$number <<= 20;

break;


case 'Gb':
$number <<= 30;

break;


case 'Tb':
$number *= 1099511627776;

break;


case 'Pb':
$number *= 1024 ** 5;

break;


case 'Eb':
$number *= 1024 ** 6;

break;


case 'Zb':
$number *= 1024 ** 7;

break;


case 'Yb':
$number *= 1024 ** 8;

break;
}

$filter = null;

switch ($operator) {
case '<':
$filter = function (\SplFileInfo $current) use ($number) {
return $current->getSize() < $number;
};

break;

case '<=':
$filter = function (\SplFileInfo $current) use ($number) {
return $current->getSize() <= $number;
};

break;

case '>':
$filter = function (\SplFileInfo $current) use ($number) {
return $current->getSize() > $number;
};

break;

case '>=':
$filter = function (\SplFileInfo $current) use ($number) {
return $current->getSize() >= $number;
};

break;

case '=':
$filter = function (\SplFileInfo $current) use ($number) {
return $current->getSize() === $number;
};

break;
}

$this->_filters[] = $filter;

return $this;
}




public function dots(bool $flag = true): self
{
if (true === $flag) {
$this->_flags ^= IteratorFileSystem::SKIP_DOTS;
} else {
$this->_flags |= IteratorFileSystem::SKIP_DOTS;
}

return $this;
}




public function owner(int $owner): self
{
$this->_filters[] = function (\SplFileInfo $current) use ($owner) {
return $current->getOwner() === $owner;
};

return $this;
}











protected function formatDate(string $date, &$operator): int
{
$operator = -1;

if (0 === \preg_match('#\bago\b#', $date)) {
$date .= ' ago';
}

if (0 !== \preg_match('#^(since|until)\b(.+)$#', $date, $matches)) {
$time = \strtotime($matches[2]);

if ('until' === $matches[1]) {
$operator = 1;
}
} else {
$time = \strtotime($date);
}

return $time;
}






public function changed(string $date): self
{
$time = $this->formatDate($date, $operator);

if (-1 === $operator) {
$this->_filters[] = function (\SplFileInfo $current) use ($time) {
return $current->getCTime() >= $time;
};
} else {
$this->_filters[] = function (\SplFileInfo $current) use ($time) {
return $current->getCTime() < $time;
};
}

return $this;
}






public function modified(string $date): self
{
$time = $this->formatDate($date, $operator);

if (-1 === $operator) {
$this->_filters[] = function (\SplFileInfo $current) use ($time) {
return $current->getMTime() >= $time;
};
} else {
$this->_filters[] = function (\SplFileInfo $current) use ($time) {
return $current->getMTime() < $time;
};
}

return $this;
}











public function filter($callback): self
{
$this->_filters[] = $callback;

return $this;
}








public function sortByName(string $locale = 'root'): self
{
if (true === \class_exists('Collator', false)) {
$collator = new \Collator($locale);

$this->_sorts[] = function (\SplFileInfo $a, \SplFileInfo $b) use ($collator) {
return $collator->compare($a->getPathname(), $b->getPathname());
};
} else {
$this->_sorts[] = function (\SplFileInfo $a, \SplFileInfo $b) {
return \strcmp($a->getPathname(), $b->getPathname());
};
}

return $this;
}






public function sortBySize(): self
{
$this->_sorts[] = function (\SplFileInfo $a, \SplFileInfo $b) {
return $a->getSize() < $b->getSize();
};

return $this;
}











public function sort($callable): self
{
$this->_sorts[] = $callable;

return $this;
}




public function childFirst(): self
{
$this->_first = \RecursiveIteratorIterator::CHILD_FIRST;

return $this;
}




public function getIterator()
{
$_iterator = new \AppendIterator();
$types = $this->getTypes();

if (!empty($types)) {
$this->_filters[] = function (\SplFileInfo $current) use ($types) {
return \in_array($current->getType(), $types);
};
}

$maxDepth = $this->getMaxDepth();
$splFileInfo = $this->getSplFileInfo();

foreach ($this->getPaths() as $path) {
if (1 === $maxDepth) {
$iterator = new \IteratorIterator(
new IteratorRecursiveDirectory(
$path,
$this->getFlags(),
$splFileInfo
),
$this->getFirst()
);
} else {
$iterator = new \RecursiveIteratorIterator(
new IteratorRecursiveDirectory(
$path,
$this->getFlags(),
$splFileInfo
),
$this->getFirst()
);

if (1 < $maxDepth) {
$iterator->setMaxDepth($maxDepth - 1);
}
}

$_iterator->append($iterator);
}

foreach ($this->getFilters() as $filter) {
$_iterator = new \CallbackFilterIterator(
$_iterator,
$filter
);
}

$sorts = $this->getSorts();

if (empty($sorts)) {
return $_iterator;
}

$array = \iterator_to_array($_iterator);

foreach ($sorts as $sort) {
\uasort($array, $sort);
}

return new \ArrayIterator($array);
}




public function setSplFileInfo(string $splFileInfo): string
{
$old = $this->_splFileInfo;
$this->_splFileInfo = $splFileInfo;

return $old;
}




public function getSplFileInfo(): string
{
return $this->_splFileInfo;
}




protected function getPaths(): array
{
return $this->_paths;
}




public function getMaxDepth(): int
{
return $this->_maxDepth;
}




public function getTypes(): array
{
return $this->_types;
}




protected function getFilters(): array
{
return $this->_filters;
}




protected function getSorts(): array
{
return $this->_sorts;
}




public function getFlags(): int
{
return $this->_flags;
}




public function getFirst(): int
{
return $this->_first;
}
}
<?php



































namespace Psy\Readline\Hoa;




class ProtocolNodeLibrary extends ProtocolNode
{



public function reach(string $queue = null)
{
$withComposer = \class_exists('Composer\Autoload\ClassLoader', false) ||
('cli' === \PHP_SAPI && \file_exists(__DIR__.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'autoload.php'));

if ($withComposer) {
return parent::reach($queue);
}

if (!empty($queue)) {
$head = $queue;

if (false !== $pos = \strpos($queue, '/')) {
$head = \substr($head, 0, $pos);
$queue = \DIRECTORY_SEPARATOR.\substr($queue, $pos + 1);
} else {
$queue = null;
}

$out = [];

foreach (\explode(';', $this->_reach) as $part) {
$out[] = "\r".$part.\strtolower($head).$queue;
}

$out[] = "\r".\dirname(__DIR__, 5).$queue;

return \implode(';', $out);
}

$out = [];

foreach (\explode(';', $this->_reach) as $part) {
$pos = \strrpos(\rtrim($part, \DIRECTORY_SEPARATOR), \DIRECTORY_SEPARATOR) + 1;
$head = \substr($part, 0, $pos);
$tail = \substr($part, $pos);
$out[] = $head.\strtolower($tail);
}

$this->_reach = \implode(';', $out);

return parent::reach($queue);
}
}
<?php



































namespace Psy\Readline\Hoa;




interface EventListenable extends EventSource
{



public function on(string $listenerId, $callable): self;
}
<?php



































namespace Psy\Readline\Hoa;




class Exception extends ExceptionIdle implements EventSource
{







public function __construct(
string $message,
int $code = 0,
$arguments = [],
\Throwable $previous = null
) {
parent::__construct($message, $code, $arguments, $previous);

if (false === Event::eventExists('hoa://Event/Exception')) {
Event::register('hoa://Event/Exception', __CLASS__);
}

$this->send();

return;
}




public function send()
{
Event::notify(
'hoa://Event/Exception',
$this,
new EventBucket($this)
);
}
}
<?php



































namespace Psy\Readline\Hoa;








class Ustring
{



public static function checkMbString(): bool
{
return \function_exists('mb_substr');
}













public static function getCharWidth(string $char): int
{
$char = (string) $char;
$c = static::toCode($char);


if (0x0 === $c) {
return 0;
}

if (0x20 > $c || (0x7F <= $c && $c < 0xA0)) {
return -1;
}


if (0xAD !== $c &&
0 !== \preg_match('#^[\p{Mn}\p{Me}\p{Cf}\x{1160}-\x{11ff}\x{200b}]#u', $char)) {
return 0;
}


return 1 +
(0x1100 <= $c &&
(0x115F >= $c || 
0x2329 === $c || 0x232A === $c ||
(0x2E80 <= $c && 0xA4CF >= $c &&
0x303F !== $c) || 
(0xAC00 <= $c && 0xD7A3 >= $c) || 
(0xF900 <= $c && 0xFAFF >= $c) || 
(0xFE10 <= $c && 0xFE19 >= $c) || 
(0xFE30 <= $c && 0xFE6F >= $c) || 
(0xFF00 <= $c && 0xFF60 >= $c) || 
(0xFFE0 <= $c && 0xFFE6 >= $c) ||
(0x20000 <= $c && 0x2FFFD >= $c) ||
(0x30000 <= $c && 0x3FFFD >= $c)));
}




public static function isCharPrintable(string $char): bool
{
return 1 <= static::getCharWidth($char);
}




public static function toCode(string $char): int
{
$char = (string) $char;
$code = \ord($char[0]);
$bytes = 1;

if (!($code & 0x80)) { 
return $code;
}

if (($code & 0xE0) === 0xC0) { 
$bytes = 2;
$code = $code & ~0xC0;
} elseif (($code & 0xF0) === 0xE0) { 
$bytes = 3;
$code = $code & ~0xE0;
} elseif (($code & 0xF8) === 0xF0) { 
$bytes = 4;
$code = $code & ~0xF0;
}

for ($i = 2; $i <= $bytes; $i++) { 
$code = ($code << 6) + (\ord($char[$i - 1]) & ~0x80);
}

return $code;
}
}
<?php



































namespace Psy\Readline\Hoa;




class EventBucket
{



protected $_source = null;




protected $_data = null;




public function __construct($data = null)
{
$this->setData($data);

return;
}




public function send(string $eventId, EventSource $source)
{
return Event::notify($eventId, $source, $this);
}




public function setSource(EventSource $source)
{
$old = $this->_source;
$this->_source = $source;

return $old;
}




public function getSource()
{
return $this->_source;
}




public function setData($data)
{
$old = $this->_data;
$this->_data = $data;

return $old;
}




public function getData()
{
return $this->_data;
}
}
<?php



































namespace Psy\Readline\Hoa;








interface StreamBufferable extends IStream
{




public function newBuffer($callable = null, int $size = null): int;




public function flush();




public function deleteBuffer(): bool;




public function getBufferLevel(): int;




public function getBufferSize(): int;
}
<?php



































namespace Psy\Readline\Hoa;






interface StreamPathable extends IStream
{



public function getBasename(): string;




public function getDirname(): string;
}
<?php



































namespace Psy\Readline\Hoa;




interface EventSource
{
}
<?php



































namespace Psy\Readline\Hoa;






class AutocompleterAggregate implements Autocompleter
{



protected $_autocompleters = null;




public function __construct(array $autocompleters)
{
$this->setAutocompleters($autocompleters);

return;
}





public function complete(&$prefix)
{
foreach ($this->getAutocompleters() as $autocompleter) {
$preg = \preg_match(
'#('.$autocompleter->getWordDefinition().')$#u',
$prefix,
$match
);

if (0 === $preg) {
continue;
}

$_prefix = $match[0];

if (null === $out = $autocompleter->complete($_prefix)) {
continue;
}

$prefix = $_prefix;

return $out;
}

return null;
}




protected function setAutocompleters(array $autocompleters)
{
$old = $this->_autocompleters;
$this->_autocompleters = new \ArrayObject($autocompleters);

return $old;
}




public function getAutocompleters()
{
return $this->_autocompleters;
}




public function getWordDefinition(): string
{
return '.*';
}
}
<?php



































namespace Psy\Readline\Hoa;






class AutocompleterPath implements Autocompleter
{



const PWD = null;




protected $_root = null;




protected $_iteratorFactory = null;




public function __construct(
string $root = null,
\Closure $iteratorFactory = null
) {
if (null === $root) {
$root = static::PWD;
}

if (null !== $root) {
$this->setRoot($root);
}

if (null !== $iteratorFactory) {
$this->setIteratorFactory($iteratorFactory);
}
}





public function complete(&$prefix)
{
$root = $this->getRoot();

if (static::PWD === $root) {
$root = \getcwd();
}

$path = $root.\DIRECTORY_SEPARATOR.$prefix;

if (!\is_dir($path)) {
$path = \dirname($path).\DIRECTORY_SEPARATOR;
$prefix = \basename($prefix);
} else {
$prefix = null;
}

$iteratorFactory = $this->getIteratorFactory() ?:
static::getDefaultIteratorFactory();

try {
$iterator = $iteratorFactory($path);
$out = [];
$length = \mb_strlen($prefix);

foreach ($iterator as $fileinfo) {
$filename = $fileinfo->getFilename();

if (null === $prefix ||
(\mb_substr($filename, 0, $length) === $prefix)) {
if ($fileinfo->isDir()) {
$out[] = $filename.'/';
} else {
$out[] = $filename;
}
}
}
} catch (\Exception $e) {
return null;
}

$count = \count($out);

if (1 === $count) {
return $out[0];
}

if (0 === $count) {
return null;
}

return $out;
}




public function getWordDefinition(): string
{
return '/?[\w\d\\_\-\.]+(/[\w\d\\_\-\.]*)*';
}




public function setRoot(string $root)
{
$old = $this->_root;
$this->_root = $root;

return $old;
}




public function getRoot()
{
return $this->_root;
}




public function setIteratorFactory(\Closure $iteratorFactory)
{
$old = $this->_iteratorFactory;
$this->_iteratorFactory = $iteratorFactory;

return $old;
}




public function getIteratorFactory()
{
return $this->_iteratorFactory;
}




public static function getDefaultIteratorFactory()
{
return function ($path) {
return new \DirectoryIterator($path);
};
}
}
<?php



































namespace Psy\Readline\Hoa;




class ProtocolNode implements \ArrayAccess, \IteratorAggregate
{



protected $_name = null;




protected $_reach = null;




private $_children = [];







public function __construct(string $name = null, string $reach = null, array $children = [])
{
if (null !== $name) {
$this->_name = $name;
}

if (null !== $reach) {
$this->_reach = $reach;
}

foreach ($children as $child) {
$this[] = $child;
}

return;
}




#[\ReturnTypeWillChange]
public function offsetSet($name, $node)
{
if (!($node instanceof self)) {
throw new ProtocolException('Protocol node must extend %s.', 0, __CLASS__);
}

if (empty($name)) {
$name = $node->getName();
}

if (empty($name)) {
throw new ProtocolException('Cannot add a node to the `hoa://` protocol without a name.', 1);
}

$this->_children[$name] = $node;
}




public function offsetGet($name): self
{
if (!isset($this[$name])) {
throw new ProtocolException('Node %s does not exist.', 2, $name);
}

return $this->_children[$name];
}




public function offsetExists($name): bool
{
return true === \array_key_exists($name, $this->_children);
}




#[\ReturnTypeWillChange]
public function offsetUnset($name)
{
unset($this->_children[$name]);
}





protected function _resolve(string $path, &$accumulator, string $id = null)
{
if (\substr($path, 0, 6) === 'hoa://') {
$path = \substr($path, 6);
}

if (empty($path)) {
return null;
}

if (null === $accumulator) {
$accumulator = [];
$posId = \strpos($path, '#');

if (false !== $posId) {
$id = \substr($path, $posId + 1);
$path = \substr($path, 0, $posId);
} else {
$id = null;
}
}

$path = \trim($path, '/');
$pos = \strpos($path, '/');

if (false !== $pos) {
$next = \substr($path, 0, $pos);
} else {
$next = $path;
}

if (isset($this[$next])) {
if (false === $pos) {
if (null === $id) {
$this->_resolveChoice($this[$next]->reach(), $accumulator);

return true;
}

$accumulator = null;

return $this[$next]->reachId($id);
}

$tnext = $this[$next];
$this->_resolveChoice($tnext->reach(), $accumulator);

return $tnext->_resolve(\substr($path, $pos + 1), $accumulator, $id);
}

$this->_resolveChoice($this->reach($path), $accumulator);

return true;
}




protected function _resolveChoice($reach, &$accumulator)
{
if (null === $reach) {
$reach = '';
}

if (empty($accumulator)) {
$accumulator = \explode(';', $reach);

return;
}

if (false === \strpos($reach, ';')) {
if (false !== $pos = \strrpos($reach, "\r")) {
$reach = \substr($reach, $pos + 1);

foreach ($accumulator as &$entry) {
$entry = null;
}
}

foreach ($accumulator as &$entry) {
$entry .= $reach;
}

return;
}

$choices = \explode(';', $reach);
$ref = $accumulator;
$accumulator = [];

foreach ($choices as $choice) {
if (false !== $pos = \strrpos($choice, "\r")) {
$choice = \substr($choice, $pos + 1);

foreach ($ref as $entry) {
$accumulator[] = $choice;
}
} else {
foreach ($ref as $entry) {
$accumulator[] = $entry.$choice;
}
}
}

unset($ref);

return;
}





public function reach(string $queue = null)
{
return empty($queue) ? $this->_reach : $queue;
}





public function reachId(string $id)
{
throw new ProtocolException('The node %s has no ID support (tried to reach #%s).', 4, [$this->getName(), $id]);
}




public function setReach(string $reach)
{
$old = $this->_reach;
$this->_reach = $reach;

return $old;
}




public function getName()
{
return $this->_name;
}




protected function getReach()
{
return $this->_reach;
}




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




public static function getRoot(): Protocol
{
return Protocol::getInstance();
}




public function __toString(): string
{
static $i = 0;

$out = \str_repeat('  ', $i).$this->getName()."\n";

foreach ($this as $node) {
++$i;
$out .= $node;
--$i;
}

return $out;
}
}
<?php



































namespace Psy\Readline\Hoa;






interface StreamPointable extends IStream
{



const SEEK_SET = \SEEK_SET;




const SEEK_CURRENT = \SEEK_CUR;




const SEEK_END = \SEEK_END;




public function rewind(): bool;




public function seek(int $offset, int $whence = self::SEEK_SET): int;




public function tell(): int;
}
<?php



































namespace Psy\Readline\Hoa;






interface StreamStatable extends IStream
{



const SIZE_UNDEFINED = -1;




public function getSize(): int;




public function getStatistic(): array;




public function getATime(): int;




public function getCTime(): int;




public function getMTime(): int;




public function getGroup(): int;




public function getOwner(): int;




public function getPermissions(): int;




public function isReadable(): bool;




public function isWritable(): bool;




public function isExecutable(): bool;




public function clearStatisticCache();




public static function clearAllStatisticCaches();
}
<?php



































namespace Psy\Readline\Hoa;




trait EventListens
{



protected $_listener = null;




public function on(string $listenerId, $callable): EventListenable
{
$listener = $this->getListener();

if (null === $listener) {
throw new EventException('Cannot attach a callable to the listener %s because '.'it has not been initialized yet.', 0, static::class);
}

$listener->attach($listenerId, $callable);

return $this;
}




protected function setListener(EventListener $listener)
{
$old = $this->_listener;
$this->_listener = $listener;

return $old;
}




protected function getListener()
{
return $this->_listener;
}
}
<?php



































namespace Psy\Readline\Hoa;






class ConsoleException extends Exception
{
}
<?php



































namespace Psy\Readline\Hoa;






class FileReadWrite extends File implements StreamIn, StreamOut
{



public function __construct(
string $streamName,
string $mode = parent::MODE_APPEND_READ_WRITE,
string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);

return;
}




protected function &_open(string $streamName, StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ_WRITE,
parent::MODE_TRUNCATE_READ_WRITE,
parent::MODE_APPEND_READ_WRITE,
parent::MODE_CREATE_READ_WRITE,
];

if (!\in_array($this->getMode(), $createModes)) {
throw new FileException('Open mode are not supported; given %d. Only %s are supported.', 0, [$this->getMode(), \implode(', ', $createModes)]);
}

\preg_match('#^(\w+)://#', $streamName, $match);

if (((isset($match[1]) && $match[1] === 'file') || !isset($match[1])) &&
!\file_exists($streamName) &&
parent::MODE_READ_WRITE === $this->getMode()) {
throw new FileDoesNotExistException('File %s does not exist.', 1, $streamName);
}

$out = parent::_open($streamName, $context);

return $out;
}




public function eof(): bool
{
return \feof($this->getStream());
}




public function read(int $length)
{
if (0 > $length) {
throw new FileException('Length must be greater than 0, given %d.', 2, $length);
}

return \fread($this->getStream(), $length);
}




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




public function readCharacter()
{
return \fgetc($this->getStream());
}




public function readBoolean()
{
return (bool) $this->read(1);
}




public function readInteger(int $length = 1)
{
return (int) $this->read($length);
}




public function readFloat(int $length = 1)
{
return (float) $this->read($length);
}





public function readArray(string $format = null)
{
return $this->scanf($format);
}




public function readLine()
{
return \fgets($this->getStream());
}




public function readAll(int $offset = 0)
{
return \stream_get_contents($this->getStream(), -1, $offset);
}




public function scanf(string $format): array
{
return \fscanf($this->getStream(), $format);
}




public function write(string $string, int $length)
{
if (0 > $length) {
throw new FileException('Length must be greater than 0, given %d.', 3, $length);
}

return \fwrite($this->getStream(), $string, $length);
}




public function writeString(string $string)
{
$string = (string) $string;

return $this->write($string, \strlen($string));
}




public function writeCharacter(string $char)
{
return $this->write((string) $char[0], 1);
}




public function writeBoolean(bool $boolean)
{
return $this->write((string) (bool) $boolean, 1);
}




public function writeInteger(int $integer)
{
$integer = (string) (int) $integer;

return $this->write($integer, \strlen($integer));
}




public function writeFloat(float $float)
{
$float = (string) (float) $float;

return $this->write($float, \strlen($float));
}




public function writeArray(array $array)
{
$array = \var_export($array, true);

return $this->write($array, \strlen($array));
}




public function writeLine(string $line)
{
if (false === $n = \strpos($line, "\n")) {
return $this->write($line."\n", \strlen($line) + 1);
}

++$n;

return $this->write(\substr($line, 0, $n), $n);
}




public function writeAll(string $string)
{
return $this->write($string, \strlen($string));
}




public function truncate(int $size): bool
{
return \ftruncate($this->getStream(), $size);
}
}
<?php



































namespace Psy\Readline\Hoa;






class FileRead extends File implements StreamIn
{



public function __construct(
string $streamName,
string $mode = parent::MODE_READ,
string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);

return;
}




protected function &_open(string $streamName, StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ,
];

if (!\in_array($this->getMode(), $createModes)) {
throw new FileException('Open mode are not supported; given %d. Only %s are supported.', 0, [$this->getMode(), \implode(', ', $createModes)]);
}

\preg_match('#^(\w+)://#', $streamName, $match);

if (((isset($match[1]) && $match[1] === 'file') || !isset($match[1])) &&
!\file_exists($streamName)) {
throw new FileDoesNotExistException('File %s does not exist.', 1, $streamName);
}

$out = parent::_open($streamName, $context);

return $out;
}




public function eof(): bool
{
return \feof($this->getStream());
}




public function read(int $length)
{
if (0 > $length) {
throw new FileException('Length must be greater than 0, given %d.', 2, $length);
}

return \fread($this->getStream(), $length);
}




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




public function readCharacter()
{
return \fgetc($this->getStream());
}




public function readBoolean()
{
return (bool) $this->read(1);
}




public function readInteger(int $length = 1)
{
return (int) $this->read($length);
}




public function readFloat(int $length = 1)
{
return (float) $this->read($length);
}





public function readArray(string $format = null)
{
return $this->scanf($format);
}




public function readLine()
{
return \fgets($this->getStream());
}




public function readAll(int $offset = 0)
{
return \stream_get_contents($this->getStream(), -1, $offset);
}




public function scanf(string $format): array
{
return \fscanf($this->getStream(), $format);
}
}
<?php



































namespace Psy\Readline\Hoa;






class Event
{



const KEY_EVENT = 0;




const KEY_SOURCE = 1;





private static $_register = [];




protected $_callable = [];




private function __construct()
{
return;
}





public static function getEvent(string $eventId): self
{
if (!isset(self::$_register[$eventId][self::KEY_EVENT])) {
self::$_register[$eventId] = [
self::KEY_EVENT => new self(),
self::KEY_SOURCE => null,
];
}

return self::$_register[$eventId][self::KEY_EVENT];
}





public static function register(string $eventId,  $source)
{
if (true === self::eventExists($eventId)) {
throw new EventException('Cannot redeclare an event with the same ID, i.e. the event '.'ID %s already exists.', 0, $eventId);
}

if (\is_object($source) && !($source instanceof EventSource)) {
throw new EventException('The source must implement \Hoa\Event\Source '.'interface; given %s.', 1, \get_class($source));
} else {
$reflection = new \ReflectionClass($source);

if (false === $reflection->implementsInterface('\Psy\Readline\Hoa\EventSource')) {
throw new EventException('The source must implement \Hoa\Event\Source '.'interface; given %s.', 2, $source);
}
}

if (!isset(self::$_register[$eventId][self::KEY_EVENT])) {
self::$_register[$eventId][self::KEY_EVENT] = new self();
}

self::$_register[$eventId][self::KEY_SOURCE] = $source;
}







public static function unregister(string $eventId, bool $hard = false)
{
if (false !== $hard) {
unset(self::$_register[$eventId]);
} else {
self::$_register[$eventId][self::KEY_SOURCE] = null;
}
}







public function attach($callable): self
{
$callable = Xcallable::from($callable);
$this->_callable[$callable->getHash()] = $callable;

return $this;
}






public function detach($callable): self
{
unset($this->_callable[Xcallable::from($callable)->getHash()]);

return $this;
}




public function isListened(): bool
{
return !empty($this->_callable);
}




public static function notify(string $eventId, EventSource $source, EventBucket $data)
{
if (false === self::eventExists($eventId)) {
throw new EventException('Event ID %s does not exist, cannot send notification.', 3, $eventId);
}

$data->setSource($source);
$event = self::getEvent($eventId);

foreach ($event->_callable as $callable) {
$callable($data);
}
}




public static function eventExists(string $eventId): bool
{
return
\array_key_exists($eventId, self::$_register) &&
self::$_register[$eventId][self::KEY_SOURCE] !== null;
}
}
<?php



































namespace Psy\Readline\Hoa;






interface StreamTouchable extends IStream
{



const OVERWRITE = true;




const DO_NOT_OVERWRITE = false;




const MAKE_DIRECTORY = true;




const DO_NOT_MAKE_DIRECTORY = false;




public function touch(int $time = -1, int $atime = -1): bool;





public function copy(string $to, bool $force = self::DO_NOT_OVERWRITE): bool;




public function move(
string $name,
bool $force = self::DO_NOT_OVERWRITE,
bool $mkdir = self::DO_NOT_MAKE_DIRECTORY
): bool;




public function delete(): bool;




public function changeGroup($group): bool;




public function changeMode(int $mode): bool;




public function changeOwner($user): bool;




public static function umask(int $umask = null): int;
}
<?php



































namespace Psy\Readline\Hoa;




class EventException extends Exception
{
}
<?php



































namespace Psy\Readline\Hoa;






class IteratorFileSystem extends \FilesystemIterator
{



protected $_splFileInfoClass = null;






public function __construct(string $path, int $flags = null, string $splFileInfoClass = null)
{
$this->_splFileInfoClass = $splFileInfoClass;

if (null === $flags) {
parent::__construct($path);
} else {
parent::__construct($path, $flags);
}

return;
}





#[\ReturnTypeWillChange]
public function current()
{
$out = parent::current();

if (null !== $this->_splFileInfoClass &&
$out instanceof \SplFileInfo) {
$out->setInfoClass($this->_splFileInfoClass);
$out = $out->getFileInfo();
}

return $out;
}
}
<?php



































namespace Psy\Readline\Hoa;








class FileLinkRead extends FileLink implements StreamIn
{









public function __construct(
string $streamName,
string $mode = parent::MODE_READ,
string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);

return;
}












protected function &_open(string $streamName, StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ,
];

if (!\in_array($this->getMode(), $createModes)) {
throw new FileException('Open mode are not supported; given %d. Only %s are supported.', 0, [$this->getMode(), \implode(', ', $createModes)]);
}

\preg_match('#^(\w+)://#', $streamName, $match);

if (((isset($match[1]) && $match[1] === 'file') || !isset($match[1])) &&
!\file_exists($streamName)) {
throw new FileDoesNotExistException('File %s does not exist.', 1, $streamName);
}

$out = parent::_open($streamName, $context);

return $out;
}






public function eof(): bool
{
return \feof($this->getStream());
}










public function read(int $length)
{
if (0 > $length) {
throw new FileException('Length must be greater than 0, given %d.', 2, $length);
}

return \fread($this->getStream(), $length);
}








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






public function readCharacter()
{
return \fgetc($this->getStream());
}






public function readBoolean()
{
return (bool) $this->read(1);
}








public function readInteger(int $length = 1)
{
return (int) $this->read($length);
}








public function readFloat(int $length = 1)
{
return (float) $this->read($length);
}









public function readArray(string $format = null)
{
return $this->scanf($format);
}






public function readLine()
{
return \fgets($this->getStream());
}








public function readAll(int $offset = 0)
{
return \stream_get_contents($this->getStream(), -1, $offset);
}








public function scanf(string $format): array
{
return \fscanf($this->getStream(), $format);
}
}
<?php



































namespace Psy\Readline\Hoa;






abstract class FileGeneric extends Stream implements StreamPathable, StreamStatable, StreamTouchable
{



protected $_mode = null;




public function getBasename(): string
{
return \basename($this->getStreamName());
}




public function getDirname(): string
{
return \dirname($this->getStreamName());
}




public function getSize(): int
{
if (false === $this->getStatistic()) {
return false;
}

return \filesize($this->getStreamName());
}




public function getStatistic(): array
{
return \fstat($this->getStream());
}




public function getATime(): int
{
return \fileatime($this->getStreamName());
}




public function getCTime(): int
{
return \filectime($this->getStreamName());
}




public function getMTime(): int
{
return \filemtime($this->getStreamName());
}




public function getGroup(): int
{
return \filegroup($this->getStreamName());
}




public function getOwner(): int
{
return \fileowner($this->getStreamName());
}




public function getPermissions(): int
{
return \fileperms($this->getStreamName());
}













public function getReadablePermissions(): string
{
$p = $this->getPermissions();

if (($p & 0xC000) === 0xC000) {
$out = 's';
} elseif (($p & 0xA000) === 0xA000) {
$out = 'l';
} elseif (($p & 0x8000) === 0x8000) {
$out = '-';
} elseif (($p & 0x6000) === 0x6000) {
$out = 'b';
} elseif (($p & 0x4000) === 0x4000) {
$out = 'd';
} elseif (($p & 0x2000) === 0x2000) {
$out = 'c';
} elseif (($p & 0x1000) === 0x1000) {
$out = 'p';
} else {
$out = 'u';
}

$out .=
(($p & 0x0100) ? 'r' : '-').
(($p & 0x0080) ? 'w' : '-').
(($p & 0x0040) ?
(($p & 0x0800) ? 's' : 'x') :
(($p & 0x0800) ? 'S' : '-')).
(($p & 0x0020) ? 'r' : '-').
(($p & 0x0010) ? 'w' : '-').
(($p & 0x0008) ?
(($p & 0x0400) ? 's' : 'x') :
(($p & 0x0400) ? 'S' : '-')).
(($p & 0x0004) ? 'r' : '-').
(($p & 0x0002) ? 'w' : '-').
(($p & 0x0001) ?
(($p & 0x0200) ? 't' : 'x') :
(($p & 0x0200) ? 'T' : '-'));

return $out;
}




public function isReadable(): bool
{
return \is_readable($this->getStreamName());
}




public function isWritable(): bool
{
return \is_writable($this->getStreamName());
}




public function isExecutable(): bool
{
return \is_executable($this->getStreamName());
}




public function clearStatisticCache()
{
\clearstatcache(true, $this->getStreamName());
}




public static function clearAllStatisticCaches()
{
\clearstatcache();
}




public function touch(int $time = null, int $atime = null): bool
{
if (null === $time) {
$time = \time();
}

if (null === $atime) {
$atime = $time;
}

return \touch($this->getStreamName(), $time, $atime);
}





public function copy(string $to, bool $force = StreamTouchable::DO_NOT_OVERWRITE): bool
{
$from = $this->getStreamName();

if ($force === StreamTouchable::DO_NOT_OVERWRITE &&
true === \file_exists($to)) {
return true;
}

if (null === $this->getStreamContext()) {
return @\copy($from, $to);
}

return @\copy($from, $to, $this->getStreamContext()->getContext());
}




public function move(
string $name,
bool $force = StreamTouchable::DO_NOT_OVERWRITE,
bool $mkdir = StreamTouchable::DO_NOT_MAKE_DIRECTORY
): bool {
$from = $this->getStreamName();

if ($force === StreamTouchable::DO_NOT_OVERWRITE &&
true === \file_exists($name)) {
return false;
}

if (StreamTouchable::MAKE_DIRECTORY === $mkdir) {
FileDirectory::create(
\dirname($name),
FileDirectory::MODE_CREATE_RECURSIVE
);
}

if (null === $this->getStreamContext()) {
return @\rename($from, $name);
}

return @\rename($from, $name, $this->getStreamContext()->getContext());
}




public function delete(): bool
{
if (null === $this->getStreamContext()) {
return @\unlink($this->getStreamName());
}

return @\unlink(
$this->getStreamName(),
$this->getStreamContext()->getContext()
);
}




public function changeGroup($group): bool
{
return \chgrp($this->getStreamName(), $group);
}




public function changeMode(int $mode): bool
{
return \chmod($this->getStreamName(), $mode);
}




public function changeOwner($user): bool
{
return \chown($this->getStreamName(), $user);
}




public static function umask(int $umask = null): int
{
if (null === $umask) {
return \umask();
}

return \umask($umask);
}




public function isFile(): bool
{
return \is_file($this->getStreamName());
}




public function isLink(): bool
{
return \is_link($this->getStreamName());
}




public function isDirectory(): bool
{
return \is_dir($this->getStreamName());
}




public function isSocket(): bool
{
return \filetype($this->getStreamName()) === 'socket';
}




public function isFIFOPipe(): bool
{
return \filetype($this->getStreamName()) === 'fifo';
}




public function isCharacterSpecial(): bool
{
return \filetype($this->getStreamName()) === 'char';
}




public function isBlockSpecial(): bool
{
return \filetype($this->getStreamName()) === 'block';
}




public function isUnknown(): bool
{
return \filetype($this->getStreamName()) === 'unknown';
}




protected function setMode(string $mode)
{
$old = $this->_mode;
$this->_mode = $mode;

return $old;
}




public function getMode()
{
return $this->_mode;
}




public function getINode(): int
{
return \fileinode($this->getStreamName());
}




public static function isCaseSensitive(): bool
{
return !(
\file_exists(\mb_strtolower(__FILE__)) &&
\file_exists(\mb_strtoupper(__FILE__))
);
}




public function getRealPath(): string
{
if (false === $out = \realpath($this->getStreamName())) {
return $this->getStreamName();
}

return $out;
}




public function getExtension(): string
{
return \pathinfo(
$this->getStreamName(),
\PATHINFO_EXTENSION
);
}




public function getFilename(): string
{
$file = \basename($this->getStreamName());

if (\defined('PATHINFO_FILENAME')) {
return \pathinfo($file, \PATHINFO_FILENAME);
}

if (\strstr($file, '.')) {
return \substr($file, 0, \strrpos($file, '.'));
}

return $file;
}
}
<?php



































namespace Psy\Readline\Hoa;





class EventListener
{



protected $_source = null;




protected $_callables = [];




public function __construct(EventListenable $source, array $ids)
{
$this->_source = $source;
$this->addIds($ids);

return;
}




public function addIds(array $ids)
{
foreach ($ids as $id) {
$this->_callables[$id] = [];
}
}




public function attach(string $listenerId, $callable): self
{
if (false === $this->listenerExists($listenerId)) {
throw new EventException('Cannot listen %s because it is not defined.', 0, $listenerId);
}

$callable = Xcallable::from($callable);
$this->_callables[$listenerId][$callable->getHash()] = $callable;

return $this;
}




public function detach(string $listenerId, $callable): self
{
unset($this->_callables[$listenerId][Xcallable::from($callable)->getHash()]);

return $this;
}




public function detachAll(string $listenerId): self
{
unset($this->_callables[$listenerId]);

return $this;
}




public function listenerExists(string $listenerId): bool
{
return \array_key_exists($listenerId, $this->_callables);
}




public function fire(string $listenerId, EventBucket $data): array
{
if (false === $this->listenerExists($listenerId)) {
throw new EventException('Cannot fire on %s because it is not defined.', 1, $listenerId);
}

$data->setSource($this->_source);
$out = [];

foreach ($this->_callables[$listenerId] as $callable) {
$out[] = $callable($data);
}

return $out;
}
}
<?php



































namespace Psy\Readline\Hoa;






interface StreamIn extends IStream
{



public function eof(): bool;




public function read(int $length);




public function readString(int $length);





public function readCharacter();




public function readBoolean();




public function readInteger(int $length = 1);




public function readFloat(int $length = 1);





public function readArray();




public function readLine();




public function readAll(int $offset = 0);




public function scanf(string $format): array;
}
<?php



































namespace Psy\Readline\Hoa;






abstract class File extends FileGeneric implements StreamBufferable, StreamLockable, StreamPointable
{




const MODE_READ = 'rb';





const MODE_READ_WRITE = 'r+b';






const MODE_TRUNCATE_WRITE = 'wb';






const MODE_TRUNCATE_READ_WRITE = 'w+b';





const MODE_APPEND_WRITE = 'ab';





const MODE_APPEND_READ_WRITE = 'a+b';








const MODE_CREATE_WRITE = 'xb';








const MODE_CREATE_READ_WRITE = 'x+b';




public function __construct(
string $streamName,
string $mode,
string $context = null,
bool $wait = false
) {
$this->setMode($mode);

switch ($streamName) {
case '0':
$streamName = 'php://stdin';

break;

case '1':
$streamName = 'php://stdout';

break;

case '2':
$streamName = 'php://stderr';

break;

default:
if (true === \ctype_digit($streamName)) {
$streamName = 'php://fd/'.$streamName;
}
}

parent::__construct($streamName, $context, $wait);

return;
}




protected function &_open(string $streamName, StreamContext $context = null)
{
if (\substr($streamName, 0, 4) === 'file' &&
false === \is_dir(\dirname($streamName))) {
throw new FileException('Directory %s does not exist. Could not open file %s.', 1, [\dirname($streamName), \basename($streamName)]);
}

if (null === $context) {
if (false === $out = @\fopen($streamName, $this->getMode(), true)) {
throw new FileException('Failed to open stream %s.', 2, $streamName);
}

return $out;
}

$out = @\fopen(
$streamName,
$this->getMode(),
true,
$context->getContext()
);

if (false === $out) {
throw new FileException('Failed to open stream %s.', 3, $streamName);
}

return $out;
}




protected function _close(): bool
{
return @\fclose($this->getStream());
}





public function newBuffer($callable = null, int $size = null): int
{
$this->setStreamBuffer($size);



return 1;
}




public function flush(): bool
{
return \fflush($this->getStream());
}




public function deleteBuffer(): bool
{
return $this->disableStreamBuffer();
}




public function getBufferLevel(): int
{
return 1;
}




public function getBufferSize(): int
{
return $this->getStreamBufferSize();
}




public function lock(int $operation): bool
{
return \flock($this->getStream(), $operation);
}




public function rewind(): bool
{
return \rewind($this->getStream());
}




public function seek(int $offset, int $whence = StreamPointable::SEEK_SET): int
{
return \fseek($this->getStream(), $offset, $whence);
}




public function tell(): int
{
$stream = $this->getStream();

if (null === $stream) {
return 0;
}

return \ftell($stream);
}




public static function create(string $name)
{
if (\file_exists($name)) {
return true;
}

return \touch($name);
}
}
<?php



































namespace Psy\Readline\Hoa;




class ProtocolWrapper
{



private $_stream = null;




private $_streamName = null;




public $context = null;





public static function realPath(string $path, bool $exists = true)
{
return ProtocolNode::getRoot()->resolve($path, $exists);
}








public function stream_cast(int $castAs)
{
return null;
}







public function stream_close()
{
if (true === @\fclose($this->getStream())) {
$this->_stream = null;
$this->_streamName = null;
}
}





public function stream_eof(): bool
{
return \feof($this->getStream());
}







public function stream_flush(): bool
{
return \fflush($this->getStream());
}















public function stream_lock(int $operation): bool
{
return \flock($this->getStream(), $operation);
}

















public function stream_metadata(string $path, int $option, $values): bool
{
$path = static::realPath($path, false);

switch ($option) {
case \STREAM_META_TOUCH:
$arity = \count($values);

if (0 === $arity) {
$out = \touch($path);
} elseif (1 === $arity) {
$out = \touch($path, $values[0]);
} else {
$out = \touch($path, $values[0], $values[1]);
}

break;

case \STREAM_META_OWNER_NAME:
case \STREAM_META_OWNER:
$out = \chown($path, $values);

break;

case \STREAM_META_GROUP_NAME:
case \STREAM_META_GROUP:
$out = \chgrp($path, $values);

break;

case \STREAM_META_ACCESS:
$out = \chmod($path, $values);

break;

default:
$out = false;
}

return $out;
}






public function stream_open(string $path, string $mode, int $options, &$openedPath): bool
{
$path = static::realPath($path, 'r' === $mode[0]);

if (Protocol::NO_RESOLUTION === $path) {
return false;
}

if (null === $this->context) {
$openedPath = \fopen($path, $mode, $options & \STREAM_USE_PATH);
} else {
$openedPath = \fopen(
$path,
$mode,
(bool) ($options & \STREAM_USE_PATH),
$this->context
);
}

if (false === \is_resource($openedPath)) {
return false;
}

$this->_stream = $openedPath;
$this->_streamName = $path;

return true;
}





public function stream_read(int $size): string
{
return \fread($this->getStream(), $size);
}












public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
{
return 0 === \fseek($this->getStream(), $offset, $whence);
}





public function stream_stat(): array
{
return \fstat($this->getStream());
}





public function stream_tell(): int
{
return \ftell($this->getStream());
}




public function stream_truncate(int $size): bool
{
return \ftruncate($this->getStream(), $size);
}





public function stream_write(string $data): int
{
return \fwrite($this->getStream(), $data);
}







public function dir_closedir()
{
\closedir($this->getStream());
$this->_stream = null;
$this->_streamName = null;
}








public function dir_opendir(string $path, int $options): bool
{
$path = static::realPath($path);
$handle = null;

if (null === $this->context) {
$handle = @\opendir($path);
} else {
$handle = @\opendir($path, $this->context);
}

if (false === $handle) {
return false;
}

$this->_stream = $handle;
$this->_streamName = $path;

return true;
}







public function dir_readdir()
{
return \readdir($this->getStream());
}








public function dir_rewinddir()
{
\rewinddir($this->getStream());
}





public function mkdir(string $path, int $mode, int $options): bool
{
if (null === $this->context) {
return \mkdir(
static::realPath($path, false),
$mode,
$options | \STREAM_MKDIR_RECURSIVE
);
}

return \mkdir(
static::realPath($path, false),
$mode,
(bool) ($options | \STREAM_MKDIR_RECURSIVE),
$this->context
);
}






public function rename(string $from, string $to): bool
{
if (null === $this->context) {
return \rename(static::realPath($from), static::realPath($to, false));
}

return \rename(
static::realPath($from),
static::realPath($to, false),
$this->context
);
}






public function rmdir(string $path, int $options): bool
{
if (null === $this->context) {
return \rmdir(static::realPath($path));
}

return \rmdir(static::realPath($path), $this->context);
}





public function unlink(string $path): bool
{
if (null === $this->context) {
return \unlink(static::realPath($path));
}

return \unlink(static::realPath($path), $this->context);
}
















public function url_stat(string $path, int $flags)
{
$path = static::realPath($path);

if (Protocol::NO_RESOLUTION === $path) {
if ($flags & \STREAM_URL_STAT_QUIET) {
return 0;
} else {
return \trigger_error(
'Path '.$path.' cannot be resolved.',
\E_WARNING
);
}
}

if ($flags & \STREAM_URL_STAT_LINK) {
return @\lstat($path);
}

return @\stat($path);
}




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




public function getStreamName()
{
return $this->_streamName;
}
}




\stream_wrapper_register('hoa', ProtocolWrapper::class);
<?php



































namespace Psy\Readline\Hoa;






class Xcallable
{



protected $_callback = null;




protected $_hash = null;






























public function __construct($call, $able = '')
{
if ($call instanceof \Closure) {
$this->_callback = $call;

return;
}

if (!\is_string($able)) {
throw new Exception('Bad callback form; the able part must be a string.', 0);
}

if ('' === $able) {
if (\is_string($call)) {
if (false === \strpos($call, '::')) {
if (!\function_exists($call)) {
throw new Exception('Bad callback form; function %s does not exist.', 1, $call);
}

$this->_callback = $call;

return;
}

list($call, $able) = \explode('::', $call);
} elseif (\is_object($call)) {
if ($call instanceof StreamOut) {
$able = null;
} elseif (\method_exists($call, '__invoke')) {
$able = '__invoke';
} else {
throw new Exception('Bad callback form; an object but without a known '.'method.', 2);
}
} elseif (\is_array($call) && isset($call[0])) {
if (!isset($call[1])) {
$this->__construct($call[0]);
return;
}

$this->__construct($call[0], $call[1]);
return;
} else {
throw new Exception('Bad callback form.', 3);
}
}

$this->_callback = [$call, $able];

return;
}




public function __invoke(...$arguments)
{
$callback = $this->getValidCallback($arguments);

return $callback(...$arguments);
}




public function getValidCallback(array &$arguments = [])
{
$callback = $this->_callback;
$head = null;

if (isset($arguments[0])) {
$head = &$arguments[0];
}



if (null !== $head &&
\is_array($callback) &&
null === $callback[1]) {
if ($head instanceof EventBucket) {
$head = $head->getData();
}

switch ($type = \gettype($head)) {
case 'string':
if (1 === \strlen($head)) {
$method = 'writeCharacter';
} else {
$method = 'writeString';
}

break;

case 'boolean':
case 'integer':
case 'array':
$method = 'write'.\ucfirst($type);

break;

case 'double':
$method = 'writeFloat';

break;

default:
$method = 'writeAll';
$head = $head."\n";
}

$callback[1] = $method;
}

return $callback;
}










public function getHash(): string
{
if (null !== $this->_hash) {
return $this->_hash;
}

$_ = &$this->_callback;

if (\is_string($_)) {
return $this->_hash = 'function#'.$_;
}

if (\is_array($_)) {
return
$this->_hash =
(\is_object($_[0])
? 'object('.\spl_object_hash($_[0]).')'.
'#'.\get_class($_[0])
: 'class#'.$_[0]).
'::'.
(null !== $_[1]
? $_[1]
: '???');
}

return $this->_hash = 'closure('.\spl_object_hash($_).')';
}




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




public static function from($call, $able = '')
{
if ($call instanceof self) {
return $call;
}

return new self($call, $able);
}
}
<?php



































namespace Psy\Readline\Hoa;









class ConsoleTput
{



protected static $_booleans = [
'auto_left_margin',
'auto_right_margin',
'no_esc_ctlc',
'ceol_standout_glitch',
'eat_newline_glitch',
'erase_overstrike',
'generic_type',
'hard_copy',
'meta_key', 
'status_line', 
'insert_null_glitch',
'memory_above',
'memory_below',
'move_insert_mode',
'move_standout_mode',
'over_strike',
'status_line_esc_ok',
'dest_tabs_magic_smso',
'tilde_glitch',
'transparent_underline',
'xon_xoff',
'needs_xon_xoff',
'prtr_silent',
'hard_cursor',
'non_rev_rmcup',
'no_pad_char',
'non_dest_scroll_region',
'can_change',
'back_color_erase',
'hue_lightness_saturation',
'col_addr_glitch',
'cr_cancels_micro_mode',
'print_wheel', 
'row_addr_glitch',
'semi_auto_right_margin',
'cpi_changes_res',
'lpi_changes_res',

'backspaces_with_bs',
'crt_no_scrolling',
'no_correctly_working_cr',
'gnu_meta_key', 
'linefeed_is_newline',
'hardware_tabs', 
'return_does_clr_eol',
];




protected static $_numbers = [
'columns',
'init_tabs',
'lines',
'lines_of_memory',
'magic_cookie_glitch',
'padding_baud_rate',
'virtual_terminal',
'width_status_line',
'num_labels',
'label_height',
'label_width',
'max_attributes',
'maximum_windows',
'max_colors',
'max_pairs',
'no_color_video',
'buffer_capacity',
'dot_vert_spacing',
'dot_horz_spacing',
'max_micro_address',
'max_micro_jump',
'micro_col_size',
'micro_line_size',
'number_of_pins',
'output_res_char',
'output_res_line',
'output_res_horz_inch',
'output_res_vert_inch',
'print_rate',
'wide_char_size',
'buttons',
'bit_image_entwining',
'bit_image_type',

'magic_cookie_glitch_ul',
'carriage_return_delay',
'new_line_delay',
'backspace_delay',
'horizontal_tab_delay',
'number_of_function_keys',
];




protected static $_strings = [
'back_tab',
'bell',
'carriage_return',
'change_scroll_region',
'clear_all_tabs',
'clear_screen',
'clr_eol',
'clr_eos',
'column_address',
'command_character',
'cursor_address',
'cursor_down',
'cursor_home',
'cursor_invisible',
'cursor_left',
'cursor_mem_address',
'cursor_normal',
'cursor_right',
'cursor_to_ll',
'cursor_up',
'cursor_visible',
'delete_character',
'delete_line',
'dis_status_line',
'down_half_line',
'enter_alt_charset_mode',
'enter_blink_mode',
'enter_bold_mode',
'enter_ca_mode',
'enter_delete_mode',
'enter_dim_mode',
'enter_insert_mode',
'enter_secure_mode',
'enter_protected_mode',
'enter_reverse_mode',
'enter_standout_mode',
'enter_underline_mode',
'erase_chars',
'exit_alt_charset_mode',
'exit_attribute_mode',
'exit_ca_mode',
'exit_delete_mode',
'exit_insert_mode',
'exit_standout_mode',
'exit_underline_mode',
'flash_screen',
'form_feed',
'from_status_line',
'init_1string',
'init_2string',
'init_3string',
'init_file',
'insert_character',
'insert_line',
'insert_padding',
'key_backspace',
'key_catab',
'key_clear',
'key_ctab',
'key_dc',
'key_dl',
'key_down',
'key_eic',
'key_eol',
'key_eos',
'key_f0',
'key_f1',
'key_f10',
'key_f2',
'key_f3',
'key_f4',
'key_f5',
'key_f6',
'key_f7',
'key_f8',
'key_f9',
'key_home',
'key_ic',
'key_il',
'key_left',
'key_ll',
'key_npage',
'key_ppage',
'key_right',
'key_sf',
'key_sr',
'key_stab',
'key_up',
'keypad_local',
'keypad_xmit',
'lab_f0',
'lab_f1',
'lab_f10',
'lab_f2',
'lab_f3',
'lab_f4',
'lab_f5',
'lab_f6',
'lab_f7',
'lab_f8',
'lab_f9',
'meta_off',
'meta_on',
'newline',
'pad_char',
'parm_dch',
'parm_delete_line',
'parm_down_cursor',
'parm_ich',
'parm_index',
'parm_insert_line',
'parm_left_cursor',
'parm_right_cursor',
'parm_rindex',
'parm_up_cursor',
'pkey_key',
'pkey_local',
'pkey_xmit',
'print_screen',
'prtr_off',
'prtr_on',
'repeat_char',
'reset_1string',
'reset_2string',
'reset_3string',
'reset_file',
'restore_cursor',
'row_address',
'save_cursor',
'scroll_forward',
'scroll_reverse',
'set_attributes',
'set_tab',
'set_window',
'tab',
'to_status_line',
'underline_char',
'up_half_line',
'init_prog',
'key_a1',
'key_a3',
'key_b2',
'key_c1',
'key_c3',
'prtr_non',
'char_padding',
'acs_chars',
'plab_norm',
'key_btab',
'enter_xon_mode',
'exit_xon_mode',
'enter_am_mode',
'exit_am_mode',
'xon_character',
'xoff_character',
'ena_acs',
'label_on',
'label_off',
'key_beg',
'key_cancel',
'key_close',
'key_command',
'key_copy',
'key_create',
'key_end',
'key_enter',
'key_exit',
'key_find',
'key_help',
'key_mark',
'key_message',
'key_move',
'key_next',
'key_open',
'key_options',
'key_previous',
'key_print',
'key_redo',
'key_reference',
'key_refresh',
'key_replace',
'key_restart',
'key_resume',
'key_save',
'key_suspend',
'key_undo',
'key_sbeg',
'key_scancel',
'key_scommand',
'key_scopy',
'key_screate',
'key_sdc',
'key_sdl',
'key_select',
'key_send',
'key_seol',
'key_sexit',
'key_sfind',
'key_shelp',
'key_shome',
'key_sic',
'key_sleft',
'key_smessage',
'key_smove',
'key_snext',
'key_soptions',
'key_sprevious',
'key_sprint',
'key_sredo',
'key_sreplace',
'key_sright',
'key_srsume',
'key_ssave',
'key_ssuspend',
'key_sundo',
'req_for_input',
'key_f11',
'key_f12',
'key_f13',
'key_f14',
'key_f15',
'key_f16',
'key_f17',
'key_f18',
'key_f19',
'key_f20',
'key_f21',
'key_f22',
'key_f23',
'key_f24',
'key_f25',
'key_f26',
'key_f27',
'key_f28',
'key_f29',
'key_f30',
'key_f31',
'key_f32',
'key_f33',
'key_f34',
'key_f35',
'key_f36',
'key_f37',
'key_f38',
'key_f39',
'key_f40',
'key_f41',
'key_f42',
'key_f43',
'key_f44',
'key_f45',
'key_f46',
'key_f47',
'key_f48',
'key_f49',
'key_f50',
'key_f51',
'key_f52',
'key_f53',
'key_f54',
'key_f55',
'key_f56',
'key_f57',
'key_f58',
'key_f59',
'key_f60',
'key_f61',
'key_f62',
'key_f63',
'clr_bol',
'clear_margins',
'set_left_margin',
'set_right_margin',
'label_format',
'set_clock',
'display_clock',
'remove_clock',
'create_window',
'goto_window',
'hangup',
'dial_phone',
'quick_dial',
'tone',
'pulse',
'flash_hook',
'fixed_pause',
'wait_tone',
'user0',
'user1',
'user2',
'user3',
'user4',
'user5',
'user6',
'user7',
'user8',
'user9',
'orig_pair',
'orig_colors',
'initialize_color',
'initialize_pair',
'set_color_pair',
'set_foreground',
'set_background',
'change_char_pitch',
'change_line_pitch',
'change_res_horz',
'change_res_vert',
'define_char',
'enter_doublewide_mode',
'enter_draft_quality',
'enter_italics_mode',
'enter_leftward_mode',
'enter_micro_mode',
'enter_near_letter_quality',
'enter_normal_quality',
'enter_shadow_mode',
'enter_subscript_mode',
'enter_superscript_mode',
'enter_upward_mode',
'exit_doublewide_mode',
'exit_italics_mode',
'exit_leftward_mode',
'exit_micro_mode',
'exit_shadow_mode',
'exit_subscript_mode',
'exit_superscript_mode',
'exit_upward_mode',
'micro_column_address',
'micro_down',
'micro_left',
'micro_right',
'micro_row_address',
'micro_up',
'order_of_pins',
'parm_down_micro',
'parm_left_micro',
'parm_right_micro',
'parm_up_micro',
'select_char_set',
'set_bottom_margin',
'set_bottom_margin_parm',
'set_left_margin_parm',
'set_right_margin_parm',
'set_top_margin',
'set_top_margin_parm',
'start_bit_image',
'start_char_set_def',
'stop_bit_image',
'stop_char_set_def',
'subscript_characters',
'superscript_characters',
'these_cause_cr',
'zero_motion',
'char_set_names',
'key_mouse',
'mouse_info',
'req_mouse_pos',
'get_mouse',
'set_a_foreground',
'set_a_background',
'pkey_plab',
'device_type',
'code_set_init',
'set0_des_seq',
'set1_des_seq',
'set2_des_seq',
'set3_des_seq',
'set_lr_margin',
'set_tb_margin',
'bit_image_repeat',
'bit_image_newline',
'bit_image_carriage_return',
'color_names',
'define_bit_image_region',
'end_bit_image_region',
'set_color_band',
'set_page_length',
'display_pc_char',
'enter_pc_charset_mode',
'exit_pc_charset_mode',
'enter_scancode_mode',
'exit_scancode_mode',
'pc_term_options',
'scancode_escape',
'alt_scancode_esc',
'enter_horizontal_hl_mode',
'enter_left_hl_mode',
'enter_low_hl_mode',
'enter_right_hl_mode',
'enter_top_hl_mode',
'enter_vertical_hl_mode',
'set_a_attributes',
'set_pglen_inch',

'termcap_init2',
'termcap_reset',
'linefeed_if_not_lf',
'backspace_if_not_bs',
'other_non_function_keys',
'arrow_key_map',
'acs_ulcorner',
'acs_llcorner',
'acs_urcorner',
'acs_lrcorner',
'acs_ltee',
'acs_rtee',
'acs_btee',
'acs_ttee',
'acs_hline',
'acs_vline',
'acs_plus',
'memory_lock',
'memory_unlock',
'box_chars_1',
];




protected $_informations = [];




public function __construct($terminfo = null)
{
if (null === $terminfo) {
$terminfo = static::getTerminfo();
}

$this->parse($terminfo);

return;
}




protected function parse(string $terminfo): array
{
if (!\file_exists($terminfo)) {
throw new ConsoleException('Terminfo file %s does not exist.', 0, $terminfo);
}

$data = \file_get_contents($terminfo);
$length = \strlen($data);
$out = ['file' => $terminfo];

$headers = [
'data_size' => $length,
'header_size' => 12,
'magic_number' => (\ord($data[1]) << 8) | \ord($data[0]),
'names_size' => (\ord($data[3]) << 8) | \ord($data[2]),
'bool_count' => (\ord($data[5]) << 8) | \ord($data[4]),
'number_count' => (\ord($data[7]) << 8) | \ord($data[6]),
'string_count' => (\ord($data[9]) << 8) | \ord($data[8]),
'string_table_size' => (\ord($data[11]) << 8) | \ord($data[10]),
];
$out['headers'] = $headers;


$i = $headers['header_size'];
$nameAndDescription = \explode('|', \substr($data, $i, $headers['names_size'] - 1));
$out['name'] = $nameAndDescription[0];
$out['description'] = $nameAndDescription[1];


$i += $headers['names_size'];
$booleans = [];
$booleanNames = &static::$_booleans;

for (
$e = 0, $max = $i + $headers['bool_count'];
$i < $max;
++$e, ++$i
) {
$booleans[$booleanNames[$e]] = 1 === \ord($data[$i]);
}

$out['booleans'] = $booleans;


if (1 === ($i % 2)) {
++$i;
}

$numbers = [];
$numberNames = &static::$_numbers;

for (
$e = 0, $max = $i + $headers['number_count'] * 2;
$i < $max;
++$e, $i += 2
) {
$name = $numberNames[$e];
$data_i0 = \ord($data[$i]);
$data_i1 = \ord($data[$i + 1]);

if ($data_i1 === 255 && $data_i0 === 255) {
$numbers[$name] = -1;
} else {
$numbers[$name] = ($data_i1 << 8) | $data_i0;
}
}

$out['numbers'] = $numbers;


$strings = [];
$stringNames = &static::$_strings;
$ii = $i + $headers['string_count'] * 2;

for (
$e = 0, $max = $ii;
$i < $max;
++$e, $i += 2
) {
$name = $stringNames[$e];
$data_i0 = \ord($data[$i]);
$data_i1 = \ord($data[$i + 1]);

if ($data_i1 === 255 && $data_i0 === 255) {
continue;
}

$a = ($data_i1 << 8) | $data_i0;
$strings[$name] = $a;

if (65534 === $a) {
continue;
}

$b = $ii + $a;
$c = $b;

while ($c < $length && \ord($data[$c])) {
$c++;
}

$value = \substr($data, $b, $c - $b);
$strings[$name] = false !== $value ? $value : null;
}

$out['strings'] = $strings;

return $this->_informations = $out;
}




public function getInformations(): array
{
return $this->_informations;
}




public function has(string $boolean): bool
{
if (!isset($this->_informations['booleans'][$boolean])) {
return false;
}

return $this->_informations['booleans'][$boolean];
}




public function count(string $number): int
{
if (!isset($this->_informations['numbers'][$number])) {
return 0;
}

return $this->_informations['numbers'][$number];
}




public function get(string $string)
{
if (!isset($this->_informations['strings'][$string])) {
return null;
}

return $this->_informations['strings'][$string];
}




public static function getTerm(): string
{
return
isset($_SERVER['TERM']) && !empty($_SERVER['TERM'])
? $_SERVER['TERM']
: (\defined('PHP_WINDOWS_VERSION_PLATFORM') ? 'windows-ansi' : 'xterm');
}




public static function getTerminfo($term = null): string
{
$paths = [];

if (isset($_SERVER['TERMINFO'])) {
$paths[] = $_SERVER['TERMINFO'];
}

if (isset($_SERVER['HOME'])) {
$paths[] = $_SERVER['HOME'].\DIRECTORY_SEPARATOR.'.terminfo';
}

if (isset($_SERVER['TERMINFO_DIRS'])) {
foreach (\explode(':', $_SERVER['TERMINFO_DIRS']) as $path) {
$paths[] = $path;
}
}

$paths[] = '/usr/share/terminfo';
$paths[] = '/usr/share/lib/terminfo';
$paths[] = '/lib/terminfo';
$paths[] = '/usr/lib/terminfo';
$paths[] = '/usr/local/share/terminfo';
$paths[] = '/usr/local/share/lib/terminfo';
$paths[] = '/usr/local/lib/terminfo';
$paths[] = '/usr/local/ncurses/lib/terminfo';
$paths[] = 'hoa://Library/Terminfo';

$term = $term ?: static::getTerm();
$fileHexa = \dechex(\ord($term[0])).\DIRECTORY_SEPARATOR.$term;
$fileAlpha = $term[0].\DIRECTORY_SEPARATOR.$term;
$pathname = null;

foreach ($paths as $path) {
if (\file_exists($_ = $path.\DIRECTORY_SEPARATOR.$fileHexa) ||
\file_exists($_ = $path.\DIRECTORY_SEPARATOR.$fileAlpha)) {
$pathname = $_;

break;
}
}

if (null === $pathname && 'xterm' !== $term) {
return static::getTerminfo('xterm');
}

return $pathname ?? '';
}




public static function isSupported(): bool
{
if (static::getTerminfo() === '') {
return false;
}

$requiredVars = [
'clear_screen',
'clr_bol',
'clr_eol',
'clr_eos',
'initialize_color',
'parm_down_cursor',
'parm_index',
'parm_left_cursor',
'parm_right_cursor',
'parm_rindex',
'parm_up_cursor',
'user6',
'user7',
];

$tput = new self();

foreach ($requiredVars as $var) {
if ($tput->get($var) === null) {
return false;
}
}

return true;
}
}
<?php



































namespace Psy\Readline\Hoa;






class Console
{



const IS_FIFO = 0;




const IS_CHARACTER = 1;




const IS_DIRECTORY = 2;




const IS_BLOCK = 3;




const IS_REGULAR = 4;




const IS_LINK = 5;




const IS_SOCKET = 6;




const IS_WHITEOUT = 7;




private static $_advanced = null;




private static $_old = null;




protected static $_mode = [];




protected static $_input = null;




protected static $_output = null;




protected static $_tput = null;




public static function advancedInteraction(bool $force = false): bool
{
if (null !== self::$_advanced) {
return self::$_advanced;
}

if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
return self::$_advanced = false;
}

if (false === $force &&
true === \defined('STDIN') &&
false === self::isDirect(\STDIN)) {
return self::$_advanced = false;
}

self::$_old = ConsoleProcessus::execute('stty -g < /dev/tty', false);
ConsoleProcessus::execute('stty -echo -icanon min 1 time 0 < /dev/tty', false);

return self::$_advanced = true;
}




public static function restoreInteraction()
{
if (null === self::$_old) {
return;
}

ConsoleProcessus::execute('stty '.self::$_old.' < /dev/tty', false);

return;
}





public static function getMode($pipe = \STDIN): int
{
$_pipe = (int) $pipe;

if (isset(self::$_mode[$_pipe])) {
return self::$_mode[$_pipe];
}

$stat = \fstat($pipe);

switch ($stat['mode'] & 0170000) {

case 0010000:
$mode = self::IS_FIFO;

break;


case 0020000:
$mode = self::IS_CHARACTER;

break;


case 0040000:
$mode = self::IS_DIRECTORY;

break;


case 0060000:
$mode = self::IS_BLOCK;

break;


case 0100000:
$mode = self::IS_REGULAR;

break;


case 0120000:
$mode = self::IS_LINK;

break;


case 0140000:
$mode = self::IS_SOCKET;

break;


case 0160000:
$mode = self::IS_WHITEOUT;

break;

default:
$mode = -1;
}

return self::$_mode[$_pipe] = $mode;
}








public static function isDirect($pipe): bool
{
return self::IS_CHARACTER === self::getMode($pipe);
}







public static function isPipe($pipe): bool
{
return self::IS_FIFO === self::getMode($pipe);
}







public static function isRedirection($pipe): bool
{
$mode = self::getMode($pipe);

return
self::IS_REGULAR === $mode ||
self::IS_DIRECTORY === $mode ||
self::IS_LINK === $mode ||
self::IS_SOCKET === $mode ||
self::IS_BLOCK === $mode;
}




public static function setInput(ConsoleInput $input)
{
$old = static::$_input;
static::$_input = $input;

return $old;
}




public static function getInput(): ConsoleInput
{
if (null === static::$_input) {
static::$_input = new ConsoleInput();
}

return static::$_input;
}




public static function setOutput(ConsoleOutput $output)
{
$old = static::$_output;
static::$_output = $output;

return $old;
}




public static function getOutput(): ConsoleOutput
{
if (null === static::$_output) {
static::$_output = new ConsoleOutput();
}

return static::$_output;
}




public static function setTput(ConsoleTput $tput)
{
$old = static::$_tput;
static::$_tput = $tput;

return $old;
}




public static function getTput(): ConsoleTput
{
if (null === static::$_tput) {
static::$_tput = new ConsoleTput();
}

return static::$_tput;
}




public static function isTmuxRunning(): bool
{
return isset($_SERVER['TMUX']);
}
}




\register_shutdown_function([Console::class, 'restoreInteraction']);
<?php



































namespace Psy\Readline\Hoa;







class ConsoleOutput implements StreamOut
{



protected $_considerMultiplexer = false;




protected $_output = null;




public function __construct(StreamOut $output = null)
{
$this->_output = $output;

return;
}




public function getStream(): StreamOut
{
return $this->_output;
}




public function write(string $string, int $length)
{
if (0 > $length) {
throw new ConsoleException('Length must be greater than 0, given %d.', 0, $length);
}

$out = \substr($string, 0, $length);

if (true === $this->isMultiplexerConsidered()) {
if (true === Console::isTmuxRunning()) {
$out =
"\033Ptmux;".
\str_replace("\033", "\033\033", $out).
"\033\\";
}

$length = \strlen($out);
}

if (null === $this->_output) {
echo $out;
} else {
$this->_output->write($out, $length);
}
}




public function writeString(string $string)
{
$string = (string) $string;

return $this->write($string, \strlen($string));
}




public function writeCharacter(string $character)
{
return $this->write((string) $character[0], 1);
}




public function writeBoolean(bool $boolean)
{
return $this->write(((bool) $boolean) ? '1' : '0', 1);
}




public function writeInteger(int $integer)
{
$integer = (string) (int) $integer;

return $this->write($integer, \strlen($integer));
}




public function writeFloat(float $float)
{
$float = (string) (float) $float;

return $this->write($float, \strlen($float));
}




public function writeArray(array $array)
{
$array = \var_export($array, true);

return $this->write($array, \strlen($array));
}




public function writeLine(string $line)
{
if (false === $n = \strpos($line, "\n")) {
return $this->write($line."\n", \strlen($line) + 1);
}

++$n;

return $this->write(\substr($line, 0, $n), $n);
}




public function writeAll(string $string)
{
return $this->write($string ?? '', \strlen($string ?? ''));
}




public function truncate(int $size): bool
{
return false;
}




public function considerMultiplexer(bool $consider): bool
{
$old = $this->_considerMultiplexer;
$this->_considerMultiplexer = $consider;

return $old;
}




public function isMultiplexerConsidered(): bool
{
return $this->_considerMultiplexer;
}
}
<?php



































namespace Psy\Readline\Hoa;






interface StreamOut extends IStream
{



public function write(string $string, int $length);




public function writeString(string $string);




public function writeCharacter(string $character);




public function writeBoolean(bool $boolean);




public function writeInteger(int $integer);




public function writeFloat(float $float);




public function writeArray(array $array);




public function writeLine(string $line);




public function writeAll(string $string);




public function truncate(int $size): bool;
}
<?php



































namespace Psy\Readline\Hoa;






class StreamException extends Exception
{
}
<?php



































namespace Psy\Readline\Hoa;






class IteratorSplFileInfo extends \SplFileInfo
{



protected $_hash = null;




protected $_relativePath = null;




public function __construct(string $filename, string $relativePath = null)
{
parent::__construct($filename);

if (-1 !== $mtime = $this->getMTime()) {
$this->_hash = \md5($this->getPathname().$mtime);
}

$this->_relativePath = $relativePath;

return;
}




public function getHash(): string
{
return $this->_hash;
}




public function getMTime(): int
{
try {
return parent::getMTime();
} catch (\RuntimeException $e) {
return -1;
}
}




public function setRelativePath(string $relativePath)
{
$old = $this->_relativePath;
$this->_relativePath = $relativePath;

return $old;
}




public function getRelativePath()
{
return $this->_relativePath;
}




public function getRelativePathname(): string
{
if (null === $relative = $this->getRelativePath()) {
return $this->getPathname();
}

return \substr($this->getPathname(), \strlen($relative));
}
}
<?php










namespace Psy\VarDumper;

use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;




class Presenter
{
const VERBOSE = 1;

private $cloner;
private $dumper;
private $exceptionsImportants = [
"\0*\0message",
"\0*\0code",
"\0*\0file",
"\0*\0line",
"\0Exception\0previous",
];
private $styles = [
'num' => 'number',
'integer' => 'integer',
'float' => 'float',
'const' => 'const',
'str' => 'string',
'cchr' => 'default',
'note' => 'class',
'ref' => 'default',
'public' => 'public',
'protected' => 'protected',
'private' => 'private',
'meta' => 'comment',
'key' => 'comment',
'index' => 'number',
];

public function __construct(OutputFormatter $formatter, $forceArrayIndexes = false)
{

$oldLocale = \setlocale(\LC_NUMERIC, 0);
\setlocale(\LC_NUMERIC, 'C');

$this->dumper = new Dumper($formatter, $forceArrayIndexes);
$this->dumper->setStyles($this->styles);


\setlocale(\LC_NUMERIC, $oldLocale);

$this->cloner = new Cloner();
$this->cloner->addCasters(['*' => function ($obj, array $a, Stub $stub, $isNested, $filter = 0) {
if ($filter || $isNested) {
if ($obj instanceof \Throwable) {
$a = Caster::filter($a, Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_EMPTY, $this->exceptionsImportants);
} else {
$a = Caster::filter($a, Caster::EXCLUDE_PROTECTED | Caster::EXCLUDE_PRIVATE);
}
}

return $a;
}]);
}








public function addCasters(array $casters)
{
$this->cloner->addCasters($casters);
}






public function presentRef($value): string
{
return $this->present($value, 0);
}










public function present($value, int $depth = null, int $options = 0): string
{
$data = $this->cloner->cloneVar($value, !($options & self::VERBOSE) ? Caster::EXCLUDE_VERBOSE : 0);

if (null !== $depth) {
$data = $data->withMaxDepth($depth);
}


$oldLocale = \setlocale(\LC_NUMERIC, 0);
\setlocale(\LC_NUMERIC, 'C');

$output = '';
$this->dumper->dump($data, function ($line, $depth) use (&$output) {
if ($depth >= 0) {
if ('' !== $output) {
$output .= \PHP_EOL;
}
$output .= \str_repeat('  ', $depth).$line;
}
});


\setlocale(\LC_NUMERIC, $oldLocale);

return OutputFormatter::escape($output);
}
}
<?php










namespace Psy\VarDumper;

use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\VarDumper\Cloner\Cursor;
use Symfony\Component\VarDumper\Dumper\CliDumper;




class Dumper extends CliDumper
{
private $formatter;
private $forceArrayIndexes;

private const ONLY_CONTROL_CHARS = '/^[\x00-\x1F\x7F]+$/';
private const CONTROL_CHARS = '/([\x00-\x1F\x7F]+)/';
private const CONTROL_CHARS_MAP = [
"\0" => '\0',
"\t" => '\t',
"\n" => '\n',
"\v" => '\v',
"\f" => '\f',
"\r" => '\r',
"\033" => '\e',
];

public function __construct(OutputFormatter $formatter, $forceArrayIndexes = false)
{
$this->formatter = $formatter;
$this->forceArrayIndexes = $forceArrayIndexes;
parent::__construct();
$this->setColors(false);
}




public function enterHash(Cursor $cursor, $type, $class, $hasChild): void
{
if (Cursor::HASH_INDEXED === $type || Cursor::HASH_ASSOC === $type) {
$class = 0;
}
parent::enterHash($cursor, $type, $class, $hasChild);
}




protected function dumpKey(Cursor $cursor): void
{
if ($this->forceArrayIndexes || Cursor::HASH_INDEXED !== $cursor->hashType) {
parent::dumpKey($cursor);
}
}

protected function style($style, $value, $attr = []): string
{
if ('ref' === $style) {
$value = \strtr($value, '@', '#');
}

$styled = '';
$cchr = $this->styles['cchr'];

$chunks = \preg_split(self::CONTROL_CHARS, $value, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
foreach ($chunks as $chunk) {
if (\preg_match(self::ONLY_CONTROL_CHARS, $chunk)) {
$chars = '';
$i = 0;
do {
$chars .= isset(self::CONTROL_CHARS_MAP[$chunk[$i]]) ? self::CONTROL_CHARS_MAP[$chunk[$i]] : \sprintf('\x%02X', \ord($chunk[$i]));
} while (isset($chunk[++$i]));

$chars = $this->formatter->escape($chars);
$styled .= "<{$cchr}>{$chars}</{$cchr}>";
} else {
$styled .= $this->formatter->escape($chunk);
}
}

$style = $this->styles[$style];

return "<{$style}>{$styled}</{$style}>";
}




protected function dumpLine($depth, $endOfValue = false): void
{
if ($endOfValue && 0 < $depth) {
$this->line .= ',';
}
$this->line = $this->formatter->format($this->line);
parent::dumpLine($depth, $endOfValue);
}
}
<?php










namespace Psy\VarDumper;





interface PresenterAware
{





public function setPresenter(Presenter $presenter);
}
<?php










namespace Psy\VarDumper;

use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;
use Symfony\Component\VarDumper\Cloner\VarCloner;




class Cloner extends VarCloner
{
private $filter = 0;




public function cloneVar($var, $filter = 0): Data
{
$this->filter = $filter;

return parent::cloneVar($var, $filter);
}




protected function castResource(Stub $stub, $isNested): array
{
return Caster::EXCLUDE_VERBOSE & $this->filter ? [] : parent::castResource($stub, $isNested);
}
}
<?php










namespace Psy\TabCompletion;

use Psy\TabCompletion\Matcher\AbstractMatcher;






class AutoCompleter
{

protected $matchers;






public function addMatcher(AbstractMatcher $matcher)
{
$this->matchers[] = $matcher;
}




public function activate()
{
\readline_completion_function([&$this, 'callback']);
}










public function processCallback(string $input, int $index, array $info = []): array
{


$line = $info['line_buffer'];
if (isset($info['end'])) {
$line = \substr($line, 0, $info['end']);
}
if ($line === '' && $input !== '') {
$line = $input;
}

$tokens = \token_get_all('<?php '.$line);


$tokens = \array_filter($tokens, function ($token) {
return !AbstractMatcher::tokenIs($token, AbstractMatcher::T_WHITESPACE);
});

$tokens = \array_values($tokens);

$matches = [];
foreach ($this->matchers as $matcher) {
if ($matcher->hasMatched($tokens)) {
$matches = \array_merge($matcher->getMatches($tokens), $matches);
}
}

$matches = \array_unique($matches);

return !empty($matches) ? $matches : [''];
}











public function callback(string $input, int $index): array
{
return $this->processCallback($input, $index, \readline_info());
}




public function __destruct()
{


if (\function_exists('readline_callback_handler_remove')) {
\readline_callback_handler_remove();
}
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class MongoDatabaseMatcher extends AbstractContextAwareMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {

\array_pop($tokens);
}
$objectToken = \array_pop($tokens);
$objectName = \str_replace('$', '', $objectToken[1]);
$object = $this->getVariable($objectName);

if (!$object instanceof \MongoDB) {
return [];
}

return \array_filter(
$object->getCollectionNames(),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;









class ClassMethodsMatcher extends AbstractMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {

\array_pop($tokens);
}

$class = $this->getNamespaceAndClass($tokens);

try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $re) {
return [];
}

if (self::needCompleteClass($tokens[1])) {
$methods = $reflection->getMethods();
} else {
$methods = $reflection->getMethods(\ReflectionMethod::IS_STATIC);
}

$methods = \array_map(function (\ReflectionMethod $method) {
return $method->getName();
}, $methods);

return \array_map(
function ($name) use ($class) {
$chunks = \explode('\\', $class);
$className = \array_pop($chunks);

return $className.'::'.$name;
},
\array_filter($methods, function ($method) use ($input) {
return AbstractMatcher::startsWith($input, $method);
})
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($prevToken, self::T_DOUBLE_COLON) && self::tokenIs($token, self::T_STRING):
case self::tokenIs($token, self::T_DOUBLE_COLON):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;

use Psy\Context;
use Psy\ContextAware;









abstract class AbstractContextAwareMatcher extends AbstractMatcher implements ContextAware
{





protected $context;






public function setContext(Context $context)
{
$this->context = $context;
}








protected function getVariable(string $var)
{
return $this->context->get($var);
}






protected function getVariables(): array
{
return $this->context->getAll();
}
}
<?php










namespace Psy\TabCompletion\Matcher;

use Psy\Command\Command;









class CommandsMatcher extends AbstractMatcher
{

protected $commands = [];






public function __construct(array $commands)
{
$this->setCommands($commands);
}






public function setCommands(array $commands)
{
$names = [];
foreach ($commands as $command) {
$names = \array_merge([$command->getName()], $names);
$names = \array_merge($command->getAliases(), $names);
}
$this->commands = $names;
}






protected function isCommand(string $name): bool
{
return \in_array($name, $this->commands);
}






protected function matchCommand(string $name): bool
{
foreach ($this->commands as $cmd) {
if ($this->startsWith($name, $cmd)) {
return true;
}
}

return false;
}




public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

return \array_filter($this->commands, function ($command) use ($input) {
return AbstractMatcher::startsWith($input, $command);
});
}




public function hasMatched(array $tokens): bool
{
 \array_shift($tokens);
$command = \array_shift($tokens);

switch (true) {
case self::tokenIs($command, self::T_STRING) &&
!$this->isCommand($command[1]) &&
$this->matchCommand($command[1]) &&
empty($tokens):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class FunctionsMatcher extends AbstractMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$func = $this->getInput($tokens);

$functions = \get_defined_functions();
$allFunctions = \array_merge($functions['user'], $functions['internal']);

return \array_filter($allFunctions, function ($function) use ($func) {
return AbstractMatcher::startsWith($func, $function);
});
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($prevToken, self::T_NEW):
return false;
case self::hasToken([self::T_OPEN_TAG, self::T_STRING], $token):
case self::isOperator($token):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class ConstantsMatcher extends AbstractMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$const = $this->getInput($tokens);

return \array_filter(\array_keys(\get_defined_constants()), function ($constant) use ($const) {
return AbstractMatcher::startsWith($const, $constant);
});
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($prevToken, self::T_NEW):
case self::tokenIs($prevToken, self::T_NS_SEPARATOR):
return false;
case self::hasToken([self::T_OPEN_TAG, self::T_STRING], $token):
case self::isOperator($token):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;

class FunctionDefaultParametersMatcher extends AbstractDefaultParametersMatcher
{
public function getMatches(array $tokens, array $info = []): array
{
\array_pop($tokens); 

$functionName = \array_pop($tokens);

try {
$reflection = new \ReflectionFunction($functionName[1]);
} catch (\ReflectionException $e) {
return [];
}

$parameters = $reflection->getParameters();

return $this->getDefaultParameterCompletion($parameters);
}

public function hasMatched(array $tokens): bool
{
$openBracket = \array_pop($tokens);

if ($openBracket !== '(') {
return false;
}

$functionName = \array_pop($tokens);

if (!self::tokenIs($functionName, self::T_STRING)) {
return false;
}

if (!\function_exists($functionName[1])) {
return false;
}

return true;
}
}
<?php










namespace Psy\TabCompletion\Matcher;






abstract class AbstractMatcher
{

const CONSTANT_SYNTAX = '^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$';
const VAR_SYNTAX = '^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$';
const MISC_OPERATORS = '+-*/^|&';

const T_OPEN_TAG = 'T_OPEN_TAG';
const T_VARIABLE = 'T_VARIABLE';
const T_OBJECT_OPERATOR = 'T_OBJECT_OPERATOR';
const T_DOUBLE_COLON = 'T_DOUBLE_COLON';
const T_NEW = 'T_NEW';
const T_CLONE = 'T_CLONE';
const T_NS_SEPARATOR = 'T_NS_SEPARATOR';
const T_STRING = 'T_STRING';
const T_NAME_QUALIFIED = 'T_NAME_QUALIFIED';
const T_WHITESPACE = 'T_WHITESPACE';
const T_AND_EQUAL = 'T_AND_EQUAL';
const T_BOOLEAN_AND = 'T_BOOLEAN_AND';
const T_BOOLEAN_OR = 'T_BOOLEAN_OR';

const T_ENCAPSED_AND_WHITESPACE = 'T_ENCAPSED_AND_WHITESPACE';
const T_REQUIRE = 'T_REQUIRE';
const T_REQUIRE_ONCE = 'T_REQUIRE_ONCE';
const T_INCLUDE = 'T_INCLUDE';
const T_INCLUDE_ONCE = 'T_INCLUDE_ONCE';








public function hasMatched(array $tokens): bool
{
return false;
}






protected function getInput(array $tokens): string
{
$var = '';
$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
$var = $firstToken[1];
}

return $var;
}






protected function getNamespaceAndClass(array $tokens): string
{
$class = '';
while (self::hasToken(
[self::T_NS_SEPARATOR, self::T_STRING, self::T_NAME_QUALIFIED],
$token = \array_pop($tokens)
)) {
if (self::needCompleteClass($token)) {
continue;
}

$class = $token[1].$class;
}

return $class;
}









abstract public function getMatches(array $tokens, array $info = []): array;







public static function startsWith(string $prefix, string $word): bool
{
return \preg_match(\sprintf('#^%s#', $prefix), $word);
}







public static function hasSyntax($token, string $syntax = self::VAR_SYNTAX): bool
{
if (!\is_array($token)) {
return false;
}

$regexp = \sprintf('#%s#', $syntax);

return (bool) \preg_match($regexp, $token[1]);
}







public static function tokenIs($token, string $which): bool
{
if (!\is_array($token)) {
return false;
}

return \token_name($token[0]) === $which;
}






public static function isOperator($token): bool
{
if (!\is_string($token)) {
return false;
}

return \strpos(self::MISC_OPERATORS, $token) !== false;
}

public static function needCompleteClass($token): bool
{
return \in_array($token[1], ['doc', 'ls', 'show']);
}







public static function hasToken(array $coll, $token): bool
{
if (!\is_array($token)) {
return false;
}

return \in_array(\token_name($token[0]), $coll);
}
}
<?php










namespace Psy\TabCompletion\Matcher;

use InvalidArgumentException;









class ObjectAttributesMatcher extends AbstractContextAwareMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {

\array_pop($tokens);
}
$objectToken = \array_pop($tokens);
if (!\is_array($objectToken)) {
return [];
}
$objectName = \str_replace('$', '', $objectToken[1]);

try {
$object = $this->getVariable($objectName);
} catch (InvalidArgumentException $e) {
return [];
}

if (!\is_object($object)) {
return [];
}

return \array_filter(
\array_keys(\get_class_vars(\get_class($object))),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;

class ObjectMethodDefaultParametersMatcher extends AbstractDefaultParametersMatcher
{
public function getMatches(array $tokens, array $info = []): array
{
$openBracket = \array_pop($tokens);
$functionName = \array_pop($tokens);
$methodOperator = \array_pop($tokens);

$objectToken = \array_pop($tokens);
if (!\is_array($objectToken)) {
return [];
}

$objectName = \str_replace('$', '', $objectToken[1]);

try {
$object = $this->getVariable($objectName);
$reflection = new \ReflectionObject($object);
} catch (\InvalidArgumentException $e) {
return [];
} catch (\ReflectionException $e) {
return [];
}

$methods = $reflection->getMethods();

foreach ($methods as $method) {
if ($method->getName() === $functionName[1]) {
return $this->getDefaultParameterCompletion($method->getParameters());
}
}

return [];
}

public function hasMatched(array $tokens): bool
{
$openBracket = \array_pop($tokens);

if ($openBracket !== '(') {
return false;
}

$functionName = \array_pop($tokens);

if (!self::tokenIs($functionName, self::T_STRING)) {
return false;
}

$operator = \array_pop($tokens);

if (!self::tokenIs($operator, self::T_OBJECT_OPERATOR)) {
return false;
}

return true;
}
}
<?php










namespace Psy\TabCompletion\Matcher;

abstract class AbstractDefaultParametersMatcher extends AbstractContextAwareMatcher
{





public function getDefaultParameterCompletion(array $reflectionParameters): array
{
$parametersProcessed = [];

foreach ($reflectionParameters as $parameter) {
if (!$parameter->isDefaultValueAvailable()) {
return [];
}

$defaultValue = $this->valueToShortString($parameter->getDefaultValue());

$parametersProcessed[] = \sprintf('$%s = %s', $parameter->getName(), $defaultValue);
}

if (empty($parametersProcessed)) {
return [];
}

return [\implode(', ', $parametersProcessed).')'];
}








private function valueToShortString($value): string
{
if (!\is_array($value)) {
return \json_encode($value);
}

$chunks = [];
$chunksSequential = [];

$allSequential = true;

foreach ($value as $key => $item) {
$allSequential = $allSequential && \is_numeric($key) && $key === \count($chunksSequential);

$keyString = $this->valueToShortString($key);
$itemString = $this->valueToShortString($item);

$chunks[] = "{$keyString} => {$itemString}";
$chunksSequential[] = $itemString;
}

$chunksToImplode = $allSequential ? $chunksSequential : $chunks;

return '['.\implode(', ', $chunksToImplode).']';
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class KeywordsMatcher extends AbstractMatcher
{
protected $keywords = [
'array', 'clone', 'declare', 'die', 'echo', 'empty', 'eval', 'exit', 'include',
'include_once', 'isset', 'list', 'print', 'require', 'require_once', 'unset',
];

protected $mandatoryStartKeywords = [
'die', 'echo', 'print', 'unset',
];






public function getKeywords(): array
{
return $this->keywords;
}






public function isKeyword(string $keyword): bool
{
return \in_array($keyword, $this->keywords);
}




public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

return \array_filter($this->keywords, function ($keyword) use ($input) {
return AbstractMatcher::startsWith($input, $keyword);
});
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::hasToken([self::T_OPEN_TAG, self::T_VARIABLE], $token):

case self::hasToken([self::T_OPEN_TAG, self::T_VARIABLE], $prevToken) &&
self::tokenIs($token, self::T_STRING):
case self::isOperator($token):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class MongoClientMatcher extends AbstractContextAwareMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {

\array_pop($tokens);
}
$objectToken = \array_pop($tokens);
$objectName = \str_replace('$', '', $objectToken[1]);
$object = $this->getVariable($objectName);

if (!$object instanceof \MongoClient) {
return [];
}

$list = $object->listDBs();

return \array_filter(
\array_map(function ($info) {
return $info['name'];
}, $list['databases']),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;

class ClassMethodDefaultParametersMatcher extends AbstractDefaultParametersMatcher
{
public function getMatches(array $tokens, array $info = []): array
{
$openBracket = \array_pop($tokens);
$functionName = \array_pop($tokens);
$methodOperator = \array_pop($tokens);

$class = $this->getNamespaceAndClass($tokens);

try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $e) {

return [];
}

$methods = $reflection->getMethods(\ReflectionMethod::IS_STATIC);

foreach ($methods as $method) {
if ($method->getName() === $functionName[1]) {
return $this->getDefaultParameterCompletion($method->getParameters());
}
}

return [];
}

public function hasMatched(array $tokens): bool
{
$openBracket = \array_pop($tokens);

if ($openBracket !== '(') {
return false;
}

$functionName = \array_pop($tokens);

if (!self::tokenIs($functionName, self::T_STRING)) {
return false;
}

$operator = \array_pop($tokens);

if (!self::tokenIs($operator, self::T_DOUBLE_COLON)) {
return false;
}

return true;
}
}
<?php










namespace Psy\TabCompletion\Matcher;

use InvalidArgumentException;









class ObjectMethodsMatcher extends AbstractContextAwareMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {

\array_pop($tokens);
}
$objectToken = \array_pop($tokens);
if (!\is_array($objectToken)) {
return [];
}
$objectName = \str_replace('$', '', $objectToken[1]);

try {
$object = $this->getVariable($objectName);
} catch (InvalidArgumentException $e) {
return [];
}

if (!\is_object($object)) {
return [];
}

return \array_filter(
\get_class_methods($object),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var) &&

!AbstractMatcher::startsWith('__', $var);
}
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class VariablesMatcher extends AbstractContextAwareMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$var = \str_replace('$', '', $this->getInput($tokens));

return \array_filter(\array_keys($this->getVariables()), function ($variable) use ($var) {
return AbstractMatcher::startsWith($var, $variable);
});
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);

switch (true) {
case self::hasToken([self::T_OPEN_TAG, self::T_VARIABLE], $token):
case \is_string($token) && $token === '$':
case self::isOperator($token):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;









class ClassAttributesMatcher extends AbstractMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$input = $this->getInput($tokens);

$firstToken = \array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {

\array_pop($tokens);
}

$class = $this->getNamespaceAndClass($tokens);

try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $re) {
return [];
}

$vars = \array_merge(
\array_map(
function ($var) {
return '$'.$var;
},
\array_keys($reflection->getStaticProperties())
),
\array_keys($reflection->getConstants())
);

return \array_map(
function ($name) use ($class) {
$chunks = \explode('\\', $class);
$className = \array_pop($chunks);

return $className.'::'.$name;
},
\array_filter(
$vars,
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
)
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

switch (true) {
case self::tokenIs($prevToken, self::T_DOUBLE_COLON) && self::tokenIs($token, self::T_STRING):
case self::tokenIs($token, self::T_DOUBLE_COLON):
return true;
}

return false;
}
}
<?php










namespace Psy\TabCompletion\Matcher;








class ClassNamesMatcher extends AbstractMatcher
{



public function getMatches(array $tokens, array $info = []): array
{
$class = $this->getNamespaceAndClass($tokens);
if ($class !== '' && $class[0] === '\\') {
$class = \substr($class, 1, \strlen($class));
}
$quotedClass = \preg_quote($class);

return \array_map(
function ($className) use ($class) {

$nsPos = \substr_count($class, '\\');
$pieces = \explode('\\', $className);

return \implode('\\', \array_slice($pieces, $nsPos, \count($pieces)));
},
\array_filter(
\array_merge(\get_declared_classes(), \get_declared_interfaces()),
function ($className) use ($quotedClass) {
return AbstractMatcher::startsWith($quotedClass, $className);
}
)
);
}




public function hasMatched(array $tokens): bool
{
$token = \array_pop($tokens);
$prevToken = \array_pop($tokens);

$ignoredTokens = [
self::T_INCLUDE, self::T_INCLUDE_ONCE, self::T_REQUIRE, self::T_REQUIRE_ONCE,
];

switch (true) {
case self::hasToken([$ignoredTokens], $token):
case self::hasToken([$ignoredTokens], $prevToken):
case \is_string($token) && $token === '$':
return false;
case self::hasToken([self::T_NEW, self::T_OPEN_TAG, self::T_NS_SEPARATOR, self::T_STRING], $prevToken):
case self::hasToken([self::T_NEW, self::T_OPEN_TAG, self::T_NS_SEPARATOR], $token):
case self::hasToken([self::T_OPEN_TAG, self::T_VARIABLE], $token):
case self::isOperator($token):
return true;
}

return false;
}
}
<?php










namespace Psy\Formatter;

use Psy\Util\Docblock;
use Symfony\Component\Console\Formatter\OutputFormatter;




class DocblockFormatter implements ReflectorFormatter
{
private static $vectorParamTemplates = [
'type' => 'info',
'var' => 'strong',
];








public static function format(\Reflector $reflector): string
{
$docblock = new Docblock($reflector);
$chunks = [];

if (!empty($docblock->desc)) {
$chunks[] = '<comment>Description:</comment>';
$chunks[] = self::indent(OutputFormatter::escape($docblock->desc), '  ');
$chunks[] = '';
}

if (!empty($docblock->tags)) {
foreach ($docblock::$vectors as $name => $vector) {
if (isset($docblock->tags[$name])) {
$chunks[] = \sprintf('<comment>%s:</comment>', self::inflect($name));
$chunks[] = self::formatVector($vector, $docblock->tags[$name]);
$chunks[] = '';
}
}

$tags = self::formatTags(\array_keys($docblock::$vectors), $docblock->tags);
if (!empty($tags)) {
$chunks[] = $tags;
$chunks[] = '';
}
}

return \rtrim(\implode("\n", $chunks));
}









private static function formatVector(array $vector, array $lines): string
{
$template = [' '];
foreach ($vector as $type) {
$max = 0;
foreach ($lines as $line) {
$chunk = $line[$type];
$cur = empty($chunk) ? 0 : \strlen($chunk) + 1;
if ($cur > $max) {
$max = $cur;
}
}

$template[] = self::getVectorParamTemplate($type, $max);
}
$template = \implode(' ', $template);

return \implode("\n", \array_map(function ($line) use ($template) {
$escaped = \array_map(function ($l) {
if ($l === null) {
return '';
}

return OutputFormatter::escape($l);
}, $line);

return \rtrim(\vsprintf($template, $escaped));
}, $lines));
}









private static function formatTags(array $skip, array $tags): string
{
$chunks = [];

foreach ($tags as $name => $values) {
if (\in_array($name, $skip)) {
continue;
}

foreach ($values as $value) {
$chunks[] = \sprintf('<comment>%s%s</comment> %s', self::inflect($name), empty($value) ? '' : ':', OutputFormatter::escape($value));
}

$chunks[] = '';
}

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







private static function getVectorParamTemplate(string $type, int $max): string
{
if (!isset(self::$vectorParamTemplates[$type])) {
return \sprintf('%%-%ds', $max);
}

return \sprintf('<%s>%%-%ds</%s>', self::$vectorParamTemplates[$type], $max, self::$vectorParamTemplates[$type]);
}







private static function indent(string $text, string $indent = '  '): string
{
return $indent.\str_replace("\n", "\n".$indent, $text);
}






private static function inflect(string $text): string
{
$words = \trim(\preg_replace('/[\s_-]+/', ' ', \preg_replace('/([a-z])([A-Z])/', '$1 $2', $text)));

return \implode(' ', \array_map('ucfirst', \explode(' ', $words)));
}
}
<?php










namespace Psy\Formatter;

use Psy\Input\FilterOptions;
use Symfony\Component\Console\Formatter\OutputFormatter;




class TraceFormatter
{










public static function formatTrace(\Throwable $throwable, FilterOptions $filter = null, int $count = null, bool $includePsy = true): array
{
if ($cwd = \getcwd()) {
$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
}

if ($count === null) {
$count = \PHP_INT_MAX;
}

$lines = [];

$trace = $throwable->getTrace();
\array_unshift($trace, [
'function' => '',
'file' => $throwable->getFile() !== null ? $throwable->getFile() : 'n/a',
'line' => $throwable->getLine() !== null ? $throwable->getLine() : 'n/a',
'args' => [],
]);

if (!$includePsy) {
for ($i = \count($trace) - 1; $i >= 0; $i--) {
$thing = isset($trace[$i]['class']) ? $trace[$i]['class'] : $trace[$i]['function'];
if (\preg_match('/\\\\?Psy\\\\/', $thing)) {
$trace = \array_slice($trace, $i + 1);
break;
}
}
}

for ($i = 0, $count = \min($count, \count($trace)); $i < $count; $i++) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = $trace[$i]['function'];
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';


if ($cwd !== false) {
$file = \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file);
}


if (\preg_match("#/src/Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code$#", \str_replace('\\', '/', $file))) {
$file = "eval()'d code";
}


if ($filter !== null && !$filter->match(\sprintf('%s%s%s() at %s:%s', $class, $type, $function, $file, $line))) {
continue;
}

$lines[] = \sprintf(
' <class>%s</class>%s%s() at <info>%s:%s</info>',
OutputFormatter::escape($class),
OutputFormatter::escape($type),
OutputFormatter::escape($function),
OutputFormatter::escape($file),
OutputFormatter::escape($line)
);
}

return $lines;
}
}
<?php










namespace Psy\Formatter;

use Psy\Reflection\ReflectionConstant;
use Psy\Reflection\ReflectionLanguageConstruct;
use Psy\Util\Json;
use Symfony\Component\Console\Formatter\OutputFormatter;




class SignatureFormatter implements ReflectorFormatter
{









public static function format(\Reflector $reflector): string
{
switch (true) {
case $reflector instanceof \ReflectionFunction:
case $reflector instanceof ReflectionLanguageConstruct:
return self::formatFunction($reflector);

case $reflector instanceof \ReflectionClass:

return self::formatClass($reflector);

case $reflector instanceof \ReflectionClassConstant:
return self::formatClassConstant($reflector);

case $reflector instanceof \ReflectionMethod:
return self::formatMethod($reflector);

case $reflector instanceof \ReflectionProperty:
return self::formatProperty($reflector);

case $reflector instanceof ReflectionConstant:
return self::formatConstant($reflector);

default:
throw new \InvalidArgumentException('Unexpected Reflector class: '.\get_class($reflector));
}
}








public static function formatName(\Reflector $reflector): string
{
return $reflector->getName();
}








private static function formatModifiers(\Reflector $reflector): string
{
return \implode(' ', \array_map(function ($modifier) {
return \sprintf('<keyword>%s</keyword>', $modifier);
}, \Reflection::getModifierNames($reflector->getModifiers())));
}








private static function formatClass(\ReflectionClass $reflector): string
{
$chunks = [];

if ($modifiers = self::formatModifiers($reflector)) {
$chunks[] = $modifiers;
}

if ($reflector->isTrait()) {
$chunks[] = 'trait';
} else {
$chunks[] = $reflector->isInterface() ? 'interface' : 'class';
}

$chunks[] = \sprintf('<class>%s</class>', self::formatName($reflector));

if ($parent = $reflector->getParentClass()) {
$chunks[] = 'extends';
$chunks[] = \sprintf('<class>%s</class>', $parent->getName());
}

$interfaces = $reflector->getInterfaceNames();
if (!empty($interfaces)) {
\sort($interfaces);

$chunks[] = $reflector->isInterface() ? 'extends' : 'implements';
$chunks[] = \implode(', ', \array_map(function ($name) {
return \sprintf('<class>%s</class>', $name);
}, $interfaces));
}

return \implode(' ', $chunks);
}








private static function formatClassConstant($reflector): string
{
$value = $reflector->getValue();
$style = self::getTypeStyle($value);

return \sprintf(
'<keyword>const</keyword> <const>%s</const> = <%s>%s</%s>',
self::formatName($reflector),
$style,
OutputFormatter::escape(Json::encode($value)),
$style
);
}








private static function formatConstant(ReflectionConstant $reflector): string
{
$value = $reflector->getValue();
$style = self::getTypeStyle($value);

return \sprintf(
'<keyword>define</keyword>(<string>%s</string>, <%s>%s</%s>)',
OutputFormatter::escape(Json::encode($reflector->getName())),
$style,
OutputFormatter::escape(Json::encode($value)),
$style
);
}






private static function getTypeStyle($value): string
{
if (\is_int($value) || \is_float($value)) {
return 'number';
} elseif (\is_string($value)) {
return 'string';
} elseif (\is_bool($value) || $value === null) {
return 'bool';
} else {
return 'strong'; 
}
}








private static function formatProperty(\ReflectionProperty $reflector): string
{
return \sprintf(
'%s <strong>$%s</strong>',
self::formatModifiers($reflector),
$reflector->getName()
);
}








private static function formatFunction(\ReflectionFunctionAbstract $reflector): string
{
return \sprintf(
'<keyword>function</keyword> %s<function>%s</function>(%s)%s',
$reflector->returnsReference() ? '&' : '',
self::formatName($reflector),
\implode(', ', self::formatFunctionParams($reflector)),
self::formatFunctionReturnType($reflector)
);
}








private static function formatFunctionReturnType(\ReflectionFunctionAbstract $reflector): string
{
if (!\method_exists($reflector, 'hasReturnType') || !$reflector->hasReturnType()) {
return '';
}

return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType(), true));
}








private static function formatMethod(\ReflectionMethod $reflector): string
{
return \sprintf(
'%s %s',
self::formatModifiers($reflector),
self::formatFunction($reflector)
);
}








private static function formatFunctionParams(\ReflectionFunctionAbstract $reflector): array
{
$params = [];
foreach ($reflector->getParameters() as $param) {
$hint = '';
try {
if (\method_exists($param, 'getType')) {

$defaultIsNull = $param->isOptional() && $param->isDefaultValueAvailable() && $param->getDefaultValue() === null;
$hint = self::formatReflectionType($param->getType(), !$defaultIsNull);
} else {
if ($param->isArray()) {
$hint = '<keyword>array</keyword>';
} elseif ($class = $param->getClass()) {
$hint = \sprintf('<class>%s</class>', $class->getName());
}
}
} catch (\Throwable $e) {







$chunks = \explode('$'.$param->getName(), (string) $param);
$chunks = \explode(' ', \trim($chunks[0]));
$guess = \end($chunks);

$hint = \sprintf('<urgent>%s</urgent>', OutputFormatter::escape($guess));

}

if ($param->isOptional()) {
if (!$param->isDefaultValueAvailable()) {
$value = 'unknown';
$typeStyle = 'urgent';
} else {
$value = $param->getDefaultValue();
$typeStyle = self::getTypeStyle($value);
$value = \is_array($value) ? '[]' : ($value === null ? 'null' : \var_export($value, true));
}
$default = \sprintf(' = <%s>%s</%s>', $typeStyle, OutputFormatter::escape($value), $typeStyle);
} else {
$default = '';
}

$params[] = \sprintf(
'%s%s%s<strong>$%s</strong>%s',
$param->isPassedByReference() ? '&' : '',
$hint,
$hint !== '' ? ' ' : '',
$param->getName(),
$default
);
}

return $params;
}






private static function formatReflectionType(?\ReflectionType $type, bool $indicateNullable): string
{
if ($type === null) {
return '';
}

if ($type instanceof \ReflectionUnionType) {
$delimeter = '|';
} elseif ($type instanceof \ReflectionIntersectionType) {
$delimeter = '&';
} else {
return self::formatReflectionNamedType($type, $indicateNullable);
}

$formattedTypes = [];
foreach ($type->getTypes() as $namedType) {
$formattedTypes[] = self::formatReflectionNamedType($namedType, $indicateNullable);
}

return \implode($delimeter, $formattedTypes);
}




private static function formatReflectionNamedType(\ReflectionNamedType $type, bool $indicateNullable): string
{
$typeStyle = $type->isBuiltin() ? 'keyword' : 'class';
$nullable = $indicateNullable && $type->allowsNull() ? '?' : '';

return \sprintf('<%s>%s%s</%s>', $typeStyle, $nullable, OutputFormatter::escape($type->getName()), $typeStyle);
}
}
<?php










namespace Psy\Formatter;




interface ReflectorFormatter
{



public static function format(\Reflector $reflector): string;
}
<?php










namespace Psy\Formatter;

use Psy\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;




class CodeFormatter implements ReflectorFormatter
{
const LINE_MARKER = '  <urgent>></urgent> ';
const NO_LINE_MARKER = '    ';

const HIGHLIGHT_DEFAULT = 'default';
const HIGHLIGHT_KEYWORD = 'keyword';

const HIGHLIGHT_PUBLIC = 'public';
const HIGHLIGHT_PROTECTED = 'protected';
const HIGHLIGHT_PRIVATE = 'private';

const HIGHLIGHT_CONST = 'const';
const HIGHLIGHT_NUMBER = 'number';
const HIGHLIGHT_STRING = 'string';
const HIGHLIGHT_COMMENT = 'code_comment';
const HIGHLIGHT_INLINE_HTML = 'inline_html';

private static $tokenMap = [

\T_OPEN_TAG => self::HIGHLIGHT_DEFAULT,
\T_OPEN_TAG_WITH_ECHO => self::HIGHLIGHT_DEFAULT,
\T_CLOSE_TAG => self::HIGHLIGHT_DEFAULT,
\T_STRING => self::HIGHLIGHT_DEFAULT,
\T_VARIABLE => self::HIGHLIGHT_DEFAULT,
\T_NS_SEPARATOR => self::HIGHLIGHT_DEFAULT,


\T_PUBLIC => self::HIGHLIGHT_PUBLIC,
\T_PROTECTED => self::HIGHLIGHT_PROTECTED,
\T_PRIVATE => self::HIGHLIGHT_PRIVATE,


\T_DIR => self::HIGHLIGHT_CONST,
\T_FILE => self::HIGHLIGHT_CONST,
\T_METHOD_C => self::HIGHLIGHT_CONST,
\T_NS_C => self::HIGHLIGHT_CONST,
\T_LINE => self::HIGHLIGHT_CONST,
\T_CLASS_C => self::HIGHLIGHT_CONST,
\T_FUNC_C => self::HIGHLIGHT_CONST,
\T_TRAIT_C => self::HIGHLIGHT_CONST,


\T_DNUMBER => self::HIGHLIGHT_NUMBER,
\T_LNUMBER => self::HIGHLIGHT_NUMBER,
\T_ENCAPSED_AND_WHITESPACE => self::HIGHLIGHT_STRING,
\T_CONSTANT_ENCAPSED_STRING => self::HIGHLIGHT_STRING,


\T_COMMENT => self::HIGHLIGHT_COMMENT,
\T_DOC_COMMENT => self::HIGHLIGHT_COMMENT,


\T_INLINE_HTML => self::HIGHLIGHT_INLINE_HTML,
];








public static function format(\Reflector $reflector): string
{
if (self::isReflectable($reflector)) {
if ($code = @\file_get_contents($reflector->getFileName())) {
return self::formatCode($code, self::getStartLine($reflector), $reflector->getEndLine());
}
}

throw new RuntimeException('Source code unavailable');
}













public static function formatCode(string $code, int $startLine = 1, int $endLine = null, int $markLine = null): string
{
$spans = self::tokenizeSpans($code);
$lines = self::splitLines($spans, $startLine, $endLine);
$lines = self::formatLines($lines);
$lines = self::numberLines($lines, $markLine);

return \implode('', \iterator_to_array($lines));
}










private static function getStartLine(\Reflector $reflector): int
{
$startLine = $reflector->getStartLine();

if ($docComment = $reflector->getDocComment()) {
$startLine -= \preg_match_all('/(\r\n?|\n)/', $docComment) + 1;
}

return \max($startLine, 1);
}













private static function tokenizeSpans(string $code): \Generator
{
$spanType = null;
$buffer = '';

foreach (\token_get_all($code) as $token) {
$nextType = self::nextHighlightType($token, $spanType);
$spanType = $spanType ?: $nextType;

if ($spanType !== $nextType) {
yield [$spanType, $buffer];
$spanType = $nextType;
$buffer = '';
}

$buffer .= \is_array($token) ? $token[1] : $token;
}

if ($spanType !== null && $buffer !== '') {
yield [$spanType, $buffer];
}
}









private static function nextHighlightType($token, $currentType)
{
if ($token === '"') {
return self::HIGHLIGHT_STRING;
}

if (\is_array($token)) {
if ($token[0] === \T_WHITESPACE) {
return $currentType;
}

if (\array_key_exists($token[0], self::$tokenMap)) {
return self::$tokenMap[$token[0]];
}
}

return self::HIGHLIGHT_KEYWORD;
}












private static function splitLines(\Generator $spans, int $startLine = 1, int $endLine = null): \Generator
{
$lineNum = 1;
$buffer = [];

foreach ($spans as list($spanType, $spanText)) {
foreach (\preg_split('/(\r\n?|\n)/', $spanText) as $index => $spanLine) {
if ($index > 0) {
if ($lineNum >= $startLine) {
yield $lineNum => $buffer;
}

$lineNum++;
$buffer = [];

if ($endLine !== null && $lineNum > $endLine) {
return;
}
}

if ($spanLine !== '') {
$buffer[] = [$spanType, $spanLine];
}
}
}

if (!empty($buffer)) {
yield $lineNum => $buffer;
}
}








private static function formatLines(\Generator $spanLines): \Generator
{
foreach ($spanLines as $lineNum => $spanLine) {
$line = '';

foreach ($spanLine as list($spanType, $spanText)) {
if ($spanType === self::HIGHLIGHT_DEFAULT) {
$line .= OutputFormatter::escape($spanText);
} else {
$line .= \sprintf('<%s>%s</%s>', $spanType, OutputFormatter::escape($spanText), $spanType);
}
}

yield $lineNum => $line.\PHP_EOL;
}
}













private static function numberLines(\Generator $lines, int $markLine = null): \Generator
{
$lines = \iterator_to_array($lines);


\end($lines);
$pad = \strlen(\key($lines));


if ($markLine !== null) {
if ($markLine > \key($lines)) {
$markLine = null;
}

\reset($lines);
if ($markLine < \key($lines)) {
$markLine = null;
}
}

foreach ($lines as $lineNum => $line) {
$mark = '';
if ($markLine !== null) {
$mark = ($markLine === $lineNum) ? self::LINE_MARKER : self::NO_LINE_MARKER;
}

yield \sprintf("%s<aside>%{$pad}s</aside>: %s", $mark, $lineNum, $line);
}
}








private static function isReflectable(\Reflector $reflector): bool
{
return ($reflector instanceof \ReflectionClass || $reflector instanceof \ReflectionFunctionAbstract) && \is_file($reflector->getFileName());
}
}
<?php










namespace Psy\ExecutionLoop;

use Psy\Context;
use Psy\Exception\BreakException;
use Psy\Shell;







class ProcessForker extends AbstractListener
{
private $savegame;
private $up;

private static $pcntlFunctions = [
'pcntl_fork',
'pcntl_signal_dispatch',
'pcntl_signal',
'pcntl_waitpid',
'pcntl_wexitstatus',
];

private static $posixFunctions = [
'posix_getpid',
'posix_kill',
];




public static function isSupported(): bool
{
return self::isPcntlSupported() && !self::disabledPcntlFunctions() && self::isPosixSupported() && !self::disabledPosixFunctions();
}




public static function isPcntlSupported(): bool
{
foreach (self::$pcntlFunctions as $func) {
if (!\function_exists($func)) {
return false;
}
}

return true;
}




public static function disabledPcntlFunctions()
{
return self::checkDisabledFunctions(self::$pcntlFunctions);
}




public static function isPosixSupported(): bool
{
foreach (self::$posixFunctions as $func) {
if (!\function_exists($func)) {
return false;
}
}

return true;
}




public static function disabledPosixFunctions()
{
return self::checkDisabledFunctions(self::$posixFunctions);
}

private static function checkDisabledFunctions(array $functions): array
{
return \array_values(\array_intersect($functions, \array_map('strtolower', \array_map('trim', \explode(',', \ini_get('disable_functions'))))));
}









public function beforeRun(Shell $shell)
{
list($up, $down) = \stream_socket_pair(\STREAM_PF_UNIX, \STREAM_SOCK_STREAM, \STREAM_IPPROTO_IP);

if (!$up) {
throw new \RuntimeException('Unable to create socket pair');
}

$pid = \pcntl_fork();
if ($pid < 0) {
throw new \RuntimeException('Unable to start execution loop');
} elseif ($pid > 0) {



\fclose($up);


$read = [$down];
$write = null;
$except = null;

do {
$n = @\stream_select($read, $write, $except, null);

if ($n === 0) {
throw new \RuntimeException('Process timed out waiting for execution loop');
}

if ($n === false) {
$err = \error_get_last();
if (!isset($err['message']) || \stripos($err['message'], 'interrupted system call') === false) {
$msg = $err['message'] ?
\sprintf('Error waiting for execution loop: %s', $err['message']) :
'Error waiting for execution loop';
throw new \RuntimeException($msg);
}
}
} while ($n < 1);

$content = \stream_get_contents($down);
\fclose($down);

if ($content) {
$shell->setScopeVariables(@\unserialize($content));
}

throw new BreakException('Exiting main thread');
}


if (!@\cli_set_process_title('psysh (loop)')) {

if (\function_exists('setproctitle')) {
@\setproctitle('psysh (loop)');
}
}


\fclose($down);


$this->up = $up;
}






public function beforeLoop(Shell $shell)
{
$this->createSavegame();
}






public function afterLoop(Shell $shell)
{

if (isset($this->savegame)) {
\posix_kill($this->savegame, \SIGKILL);
\pcntl_signal_dispatch();
}
}







public function afterRun(Shell $shell)
{

if (isset($this->up)) {
\fwrite($this->up, $this->serializeReturn($shell->getScopeVariables(false)));
\fclose($this->up);

\posix_kill(\posix_getpid(), \SIGKILL);
}
}








private function createSavegame()
{

$this->savegame = \posix_getpid();

$pid = \pcntl_fork();
if ($pid < 0) {
throw new \RuntimeException('Unable to create savegame fork');
} elseif ($pid > 0) {

\pcntl_waitpid($pid, $status);


if (!\pcntl_wexitstatus($status)) {
\posix_kill(\posix_getpid(), \SIGKILL);
}


$this->createSavegame();
}
}











private function serializeReturn(array $return): string
{
$serializable = [];

foreach ($return as $key => $value) {

if (Context::isSpecialVariableName($key)) {
continue;
}


if (\is_resource($value) || $value instanceof \Closure) {
continue;
}

if (\version_compare(\PHP_VERSION, '8.1', '>=') && $value instanceof \UnitEnum) {

$ref = new \ReflectionObject($value);
if (\strpos($ref->getFileName(), ": eval()'d code") !== false) {
continue;
}
}

try {
@\serialize($value);
$serializable[$key] = $value;
} catch (\Throwable $e) {

}
}

return @\serialize($serializable);
}
}
<?php










namespace Psy\ExecutionLoop;

use Psy\Exception\ParseErrorException;
use Psy\ParserFactory;
use Psy\Shell;






class RunkitReloader extends AbstractListener
{
private $parser;
private $timestamps = [];




public static function isSupported(): bool
{

return \extension_loaded('runkit') || \extension_loaded('runkit7') && \function_exists('runkit_import');
}




public function __construct()
{
$this->parser = (new ParserFactory())->createParser();
}







public function onInput(Shell $shell, string $input)
{
$this->reload($shell);
}






private function reload(Shell $shell)
{
\clearstatcache();
$modified = [];

foreach (\get_included_files() as $file) {
$timestamp = \filemtime($file);

if (!isset($this->timestamps[$file])) {
$this->timestamps[$file] = $timestamp;
continue;
}

if ($this->timestamps[$file] === $timestamp) {
continue;
}

if (!$this->lintFile($file)) {
$msg = \sprintf('Modified file "%s" could not be reloaded', $file);
$shell->writeException(new ParseErrorException($msg));
continue;
}

$modified[] = $file;
$this->timestamps[$file] = $timestamp;
}














foreach ($modified as $file) {
$flags = (
RUNKIT_IMPORT_FUNCTIONS |
RUNKIT_IMPORT_CLASSES |
RUNKIT_IMPORT_CLASS_METHODS |
RUNKIT_IMPORT_CLASS_CONSTS |
RUNKIT_IMPORT_CLASS_PROPS |
RUNKIT_IMPORT_OVERRIDE
);


if (\extension_loaded('runkit7')) {
$flags &= ~RUNKIT_IMPORT_CLASS_PROPS & ~RUNKIT_IMPORT_CLASS_STATIC_PROPS;
runkit7_import($file, $flags);
} else {
runkit_import($file, $flags);
}
}
}








private function lintFile(string $file): bool
{

try {
$this->parser->parse(\file_get_contents($file));
} catch (\Throwable $e) {
return false;
}

return true;
}
}
<?php










namespace Psy\ExecutionLoop;

use Psy\Shell;




interface Listener
{



public static function isSupported(): bool;






public function beforeRun(Shell $shell);






public function beforeLoop(Shell $shell);











public function onInput(Shell $shell, string $input);















public function onExecute(Shell $shell, string $code);






public function afterLoop(Shell $shell);






public function afterRun(Shell $shell);
}
<?php










namespace Psy\ExecutionLoop;

use Psy\Shell;




abstract class AbstractListener implements Listener
{



public function beforeRun(Shell $shell)
{
}




public function beforeLoop(Shell $shell)
{
}




public function onInput(Shell $shell, string $input)
{
}




public function onExecute(Shell $shell, string $code)
{
}




public function afterLoop(Shell $shell)
{
}




public function afterRun(Shell $shell)
{
}
}
<?php










namespace Psy;





class Sudo
{








public static function fetchProperty($object, string $property)
{
$prop = self::getProperty(new \ReflectionObject($object), $property);

return $prop->getValue($object);
}










public static function assignProperty($object, string $property, $value)
{
$prop = self::getProperty(new \ReflectionObject($object), $property);
$prop->setValue($object, $value);

return $value;
}










public static function callMethod($object, string $method, ...$args)
{
$refl = new \ReflectionObject($object);
$reflMethod = $refl->getMethod($method);
$reflMethod->setAccessible(true);

return $reflMethod->invokeArgs($object, $args);
}









public static function fetchStaticProperty($class, string $property)
{
$prop = self::getProperty(new \ReflectionClass($class), $property);
$prop->setAccessible(true);

return $prop->getValue();
}










public static function assignStaticProperty($class, string $property, $value)
{
$prop = self::getProperty(new \ReflectionClass($class), $property);
$refl = $prop->getDeclaringClass();

if (\method_exists($refl, 'setStaticPropertyValue')) {
$refl->setStaticPropertyValue($property, $value);
} else {
$prop->setValue($value);
}

return $value;
}










public static function callStatic($class, string $method, ...$args)
{
$refl = new \ReflectionClass($class);
$reflMethod = $refl->getMethod($method);
$reflMethod->setAccessible(true);

return $reflMethod->invokeArgs(null, $args);
}









public static function fetchClassConst($class, string $const)
{
$refl = new \ReflectionClass($class);

do {
if ($refl->hasConstant($const)) {
return $refl->getConstant($const);
}

$refl = $refl->getParentClass();
} while ($refl !== false);

return false;
}







public static function newInstance(string $class, ...$args)
{
$refl = new \ReflectionClass($class);
$instance = $refl->newInstanceWithoutConstructor();

$constructor = $refl->getConstructor();
$constructor->setAccessible(true);
$constructor->invokeArgs($instance, $args);

return $instance;
}











private static function getProperty(\ReflectionClass $refl, string $property): \ReflectionProperty
{
$firstException = null;
do {
try {
$prop = $refl->getProperty($property);
$prop->setAccessible(true);

return $prop;
} catch (\ReflectionException $e) {
if ($firstException === null) {
$firstException = $e;
}

$refl = $refl->getParentClass();
}
} while ($refl !== false);

throw $firstException;
}
}
<?php










namespace Psy;

use Psy\CodeCleaner\NoReturnValue;
use Psy\Exception\BreakException;
use Psy\Exception\ErrorException;
use Psy\Exception\Exception as PsyException;
use Psy\Exception\RuntimeException;
use Psy\Exception\ThrowUpException;
use Psy\ExecutionLoop\ProcessForker;
use Psy\ExecutionLoop\RunkitReloader;
use Psy\Formatter\TraceFormatter;
use Psy\Input\ShellInput;
use Psy\Input\SilentInput;
use Psy\Output\ShellOutput;
use Psy\TabCompletion\Matcher;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command as BaseCommand;
use Symfony\Component\Console\Exception\ExceptionInterface as SymfonyConsoleException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;











class Shell extends Application
{
const VERSION = 'v0.12.0';

private $config;
private $cleaner;
private $output;
private $originalVerbosity;
private $readline;
private $inputBuffer;
private $code;
private $codeBuffer;
private $codeBufferOpen;
private $codeStack;
private $stdoutBuffer;
private $context;
private $includes;
private $outputWantsNewline = false;
private $loopListeners;
private $autoCompleter;
private $matchers = [];
private $commandsMatcher;
private $lastExecSuccess = true;
private $nonInteractive = false;
private $errorReporting;






public function __construct(Configuration $config = null)
{
$this->config = $config ?: new Configuration();
$this->cleaner = $this->config->getCodeCleaner();
$this->context = new Context();
$this->includes = [];
$this->readline = $this->config->getReadline();
$this->inputBuffer = [];
$this->codeStack = [];
$this->stdoutBuffer = '';
$this->loopListeners = $this->getDefaultLoopListeners();

parent::__construct('Psy Shell', self::VERSION);

$this->config->setShell($this);


\Psy\info($this->config);
}







public static function isIncluded(array $trace): bool
{
$isIncluded = isset($trace[0]['function']) &&
\in_array($trace[0]['function'], ['require', 'include', 'require_once', 'include_once']);


if ($isIncluded && \array_key_exists('_composer_autoload_path', $GLOBALS) && \preg_match('{[\\\\/]psysh$}', $trace[0]['file'])) {


return isset($trace[1]['function']) &&
\in_array($trace[1]['function'], ['require', 'include', 'require_once', 'include_once']);
}

return $isIncluded;
}




public static function isPhar(): bool
{
return \class_exists("\Phar") && \Phar::running() !== '' && \strpos(__FILE__, \Phar::running(true)) === 0;
}












public static function debug(array $vars = [], $bindTo = null): array
{
@\trigger_error('`Psy\\Shell::debug` is deprecated; call `Psy\\debug` instead.', \E_USER_DEPRECATED);

return \Psy\debug($vars, $bindTo);
}










public function add(BaseCommand $command): BaseCommand
{
if ($ret = parent::add($command)) {
if ($ret instanceof ContextAware) {
$ret->setContext($this->context);
}

if ($ret instanceof PresenterAware) {
$ret->setPresenter($this->config->getPresenter());
}

if (isset($this->commandsMatcher)) {
$this->commandsMatcher->setCommands($this->all());
}
}

return $ret;
}






protected function getDefaultInputDefinition(): InputDefinition
{
return new InputDefinition([
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
]);
}






protected function getDefaultCommands(): array
{
$sudo = new Command\SudoCommand();
$sudo->setReadline($this->readline);

$hist = new Command\HistoryCommand();
$hist->setReadline($this->readline);

return [
new Command\HelpCommand(),
new Command\ListCommand(),
new Command\DumpCommand(),
new Command\DocCommand(),
new Command\ShowCommand(),
new Command\WtfCommand(),
new Command\WhereamiCommand(),
new Command\ThrowUpCommand(),
new Command\TimeitCommand(),
new Command\TraceCommand(),
new Command\BufferCommand(),
new Command\ClearCommand(),
new Command\EditCommand($this->config->getRuntimeDir()),

$sudo,
$hist,
new Command\ExitCommand(),
];
}




protected function getDefaultMatchers(): array
{


$this->commandsMatcher = new Matcher\CommandsMatcher($this->all());

return [
$this->commandsMatcher,
new Matcher\KeywordsMatcher(),
new Matcher\VariablesMatcher(),
new Matcher\ConstantsMatcher(),
new Matcher\FunctionsMatcher(),
new Matcher\ClassNamesMatcher(),
new Matcher\ClassMethodsMatcher(),
new Matcher\ClassAttributesMatcher(),
new Matcher\ObjectMethodsMatcher(),
new Matcher\ObjectAttributesMatcher(),
new Matcher\ClassMethodDefaultParametersMatcher(),
new Matcher\ObjectMethodDefaultParametersMatcher(),
new Matcher\FunctionDefaultParametersMatcher(),
];
}






protected function getDefaultLoopListeners(): array
{
$listeners = [];

if (ProcessForker::isSupported() && $this->config->usePcntl()) {
$listeners[] = new ProcessForker();
}

if (RunkitReloader::isSupported()) {
$listeners[] = new RunkitReloader();
}

return $listeners;
}






public function addMatchers(array $matchers)
{
$this->matchers = \array_merge($this->matchers, $matchers);

if (isset($this->autoCompleter)) {
$this->addMatchersToAutoCompleter($matchers);
}
}






public function addTabCompletionMatchers(array $matchers)
{
@\trigger_error('`addTabCompletionMatchers` is deprecated; call `addMatchers` instead.', \E_USER_DEPRECATED);

$this->addMatchers($matchers);
}






public function setOutput(OutputInterface $output)
{
$this->output = $output;
$this->originalVerbosity = $output->getVerbosity();
}









public function run(InputInterface $input = null, OutputInterface $output = null): int
{

$input = new ArrayInput([]);

if ($output === null) {
$output = $this->config->getOutput();
}

$this->setAutoExit(false);
$this->setCatchExceptions(false);

try {
return parent::run($input, $output);
} catch (\Throwable $e) {
$this->writeException($e);
}

return 1;
}











public function doRun(InputInterface $input, OutputInterface $output): int
{
$this->setOutput($output);
$this->resetCodeBuffer();

if ($input->isInteractive()) {

return $this->doInteractiveRun();
} else {
return $this->doNonInteractiveRun($this->config->rawOutput());
}
}











private function doInteractiveRun(): int
{
$this->initializeTabCompletion();
$this->readline->readHistory();

$this->output->writeln($this->getHeader());
$this->writeVersionInfo();
$this->writeStartupMessage();

try {
$this->beforeRun();
$this->loadIncludes();
$loop = new ExecutionLoopClosure($this);
$loop->execute();
$this->afterRun();
} catch (ThrowUpException $e) {
throw $e->getPrevious();
} catch (BreakException $e) {

}

return 0;
}











private function doNonInteractiveRun(bool $rawOutput): int
{
$this->nonInteractive = true;


if (!$rawOutput && !$this->config->outputIsPiped()) {
$this->output->writeln($this->getHeader());
$this->writeVersionInfo();
$this->writeStartupMessage();
}

$this->beforeRun();
$this->loadIncludes();




if (!empty($this->inputBuffer) || $this->config->inputIsPiped()) {
$this->getInput(false);
}

if ($this->hasCode()) {
$ret = $this->execute($this->flushCode());
$this->writeReturnValue($ret, $rawOutput);
}

$this->afterRun();
$this->nonInteractive = false;

return 0;
}




protected function configureIO(InputInterface $input, OutputInterface $output): void
{

$input->setInteractive($this->config->getInputInteractive());

if ($this->config->getOutputDecorated() !== null) {
$output->setDecorated($this->config->getOutputDecorated());
}

$output->setVerbosity($this->config->getOutputVerbosity());
}




private function loadIncludes()
{

$load = function (self $__psysh__) {
\set_error_handler([$__psysh__, 'handleError']);
foreach ($__psysh__->getIncludes() as $__psysh_include__) {
try {
include_once $__psysh_include__;
} catch (\Exception $_e) {
$__psysh__->writeException($_e);
}
}
\restore_error_handler();
unset($__psysh_include__);


\extract($__psysh__->getScopeVariables(false));


$__psysh__->setScopeVariables(\get_defined_vars());
};

$load($this);
}











public function getInput(bool $interactive = true)
{
$this->codeBufferOpen = false;

do {

$this->output->setVerbosity($this->originalVerbosity);

$input = $this->readline();








if ($input === false) {
if (!$interactive) {
return;
}

$this->output->writeln('');

if ($this->hasCode()) {
$this->resetCodeBuffer();
} else {
throw new BreakException('Ctrl+D');
}
}


if (\trim($input) === '' && !$this->codeBufferOpen) {
continue;
}

$input = $this->onInput($input);


if ($this->hasCommand($input) && !$this->inputInOpenStringOrComment($input)) {
$this->addHistory($input);
$this->runCommand($input);

continue;
}

$this->addCode($input);
} while (!$interactive || !$this->hasValidCode());
}








private function inputInOpenStringOrComment(string $input): bool
{
if (!$this->hasCode()) {
return false;
}

$code = $this->codeBuffer;
$code[] = $input;
$tokens = @\token_get_all('<?php '.\implode("\n", $code));
$last = \array_pop($tokens);

return $last === '"' || $last === '`' ||
(\is_array($last) && \in_array($last[0], [\T_ENCAPSED_AND_WHITESPACE, \T_START_HEREDOC, \T_COMMENT]));
}




protected function beforeRun()
{
foreach ($this->loopListeners as $listener) {
$listener->beforeRun($this);
}
}




public function beforeLoop()
{
foreach ($this->loopListeners as $listener) {
$listener->beforeLoop($this);
}
}






public function onInput(string $input): string
{
foreach ($this->loopListeners as $listeners) {
if (($return = $listeners->onInput($this, $input)) !== null) {
$input = $return;
}
}

return $input;
}






public function onExecute(string $code): string
{
$this->errorReporting = \error_reporting();

foreach ($this->loopListeners as $listener) {
if (($return = $listener->onExecute($this, $code)) !== null) {
$code = $return;
}
}

$output = $this->output;
if ($output instanceof ConsoleOutput) {
$output = $output->getErrorOutput();
}

$output->writeln(\sprintf('<whisper>%s</whisper>', OutputFormatter::escape($code)), ConsoleOutput::VERBOSITY_DEBUG);

return $code;
}




public function afterLoop()
{
foreach ($this->loopListeners as $listener) {
$listener->afterLoop($this);
}
}




protected function afterRun()
{
foreach ($this->loopListeners as $listener) {
$listener->afterRun($this);
}
}






public function setScopeVariables(array $vars)
{
$this->context->setAll($vars);
}










public function getScopeVariables(bool $includeBoundObject = true): array
{
$vars = $this->context->getAll();

if (!$includeBoundObject) {
unset($vars['this']);
}

return $vars;
}










public function getSpecialScopeVariables(bool $includeBoundObject = true): array
{
$vars = $this->context->getSpecialVariables();

if (!$includeBoundObject) {
unset($vars['this']);
}

return $vars;
}












public function getScopeVariablesDiff(array $currentVars): array
{
$newVars = [];

foreach ($this->getScopeVariables(false) as $key => $value) {
if (!\array_key_exists($key, $currentVars) || $currentVars[$key] !== $value) {
$newVars[$key] = $value;
}
}

return $newVars;
}






public function getUnusedCommandScopeVariableNames(): array
{
return $this->context->getUnusedCommandScopeVariableNames();
}






public function getScopeVariableNames(): array
{
return \array_keys($this->context->getAll());
}








public function getScopeVariable(string $name)
{
return $this->context->get($name);
}






public function setBoundObject($boundObject)
{
$this->context->setBoundObject($boundObject);
}






public function getBoundObject()
{
return $this->context->getBoundObject();
}






public function setBoundClass($boundClass)
{
$this->context->setBoundClass($boundClass);
}






public function getBoundClass()
{
return $this->context->getBoundClass();
}






public function setIncludes(array $includes = [])
{
$this->includes = $includes;
}






public function getIncludes(): array
{
return \array_merge($this->config->getDefaultIncludes(), $this->includes);
}






public function hasCode(): bool
{
return !empty($this->codeBuffer);
}








protected function hasValidCode(): bool
{
return !$this->codeBufferOpen && $this->code !== false;
}







public function addCode(string $code, bool $silent = false)
{
try {

if (\substr(\rtrim($code), -1) === '\\') {
$this->codeBufferOpen = true;
$code = \substr(\rtrim($code), 0, -1);
} else {
$this->codeBufferOpen = false;
}

$this->codeBuffer[] = $silent ? new SilentInput($code) : $code;
$this->code = $this->cleaner->clean($this->codeBuffer, $this->config->requireSemicolons());
} catch (\Throwable $e) {

$this->addCodeBufferToHistory();

throw $e;
}
}













private function setCode(string $code, bool $silent = false)
{
if ($this->hasCode()) {
$this->codeStack[] = [$this->codeBuffer, $this->codeBufferOpen, $this->code];
}

$this->resetCodeBuffer();
try {
$this->addCode($code, $silent);
} catch (\Throwable $e) {
$this->popCodeStack();

throw $e;
}

if (!$this->hasValidCode()) {
$this->popCodeStack();

throw new \InvalidArgumentException('Unexpected end of input');
}
}








public function getCodeBuffer(): array
{
return $this->codeBuffer;
}










protected function runCommand(string $input)
{
$command = $this->getCommand($input);

if (empty($command)) {
throw new \InvalidArgumentException('Command not found: '.$input);
}

$input = new ShellInput(\str_replace('\\', '\\\\', \rtrim($input, " \t\n\r\0\x0B;")));

if ($input->hasParameterOption(['--help', '-h'])) {
$helpCommand = $this->get('help');
if (!$helpCommand instanceof Command\HelpCommand) {
throw new RuntimeException('Invalid help command instance');
}
$helpCommand->setCommand($command);

return $helpCommand->run(new StringInput(''), $this->output);
}

return $command->run($input, $this->output);
}







public function resetCodeBuffer()
{
$this->codeBuffer = [];
$this->code = false;
}









public function addInput($input, bool $silent = false)
{
foreach ((array) $input as $line) {
$this->inputBuffer[] = $silent ? new SilentInput($line) : $line;
}
}









public function flushCode()
{
if ($this->hasValidCode()) {
$this->addCodeBufferToHistory();
$code = $this->code;
$this->popCodeStack();

return $code;
}
}




private function popCodeStack()
{
$this->resetCodeBuffer();

if (empty($this->codeStack)) {
return;
}

list($codeBuffer, $codeBufferOpen, $code) = \array_pop($this->codeStack);

$this->codeBuffer = $codeBuffer;
$this->codeBufferOpen = $codeBufferOpen;
$this->code = $code;
}













private function addHistory($line)
{
if ($line instanceof SilentInput) {
return;
}


if (\trim($line) !== '' && \substr($line, 0, 1) !== ' ') {
$this->readline->addHistory($line);
}
}




private function addCodeBufferToHistory()
{
$codeBuffer = \array_filter($this->codeBuffer, function ($line) {
return !$line instanceof SilentInput;
});

$this->addHistory(\implode("\n", $codeBuffer));
}








public function getNamespace()
{
if ($namespace = $this->cleaner->getNamespace()) {
return \implode('\\', $namespace);
}
}









public function writeStdout(string $out, int $phase = \PHP_OUTPUT_HANDLER_END)
{
if ($phase & \PHP_OUTPUT_HANDLER_START) {
if ($this->output instanceof ShellOutput) {
$this->output->startPaging();
}
}

$isCleaning = $phase & \PHP_OUTPUT_HANDLER_CLEAN;


if ($out !== '' && !$isCleaning) {
$this->output->write($out, false, OutputInterface::OUTPUT_RAW);
$this->outputWantsNewline = (\substr($out, -1) !== "\n");
$this->stdoutBuffer .= $out;
}


if ($phase & \PHP_OUTPUT_HANDLER_END) {

if ($this->outputWantsNewline) {
if (!$this->config->rawOutput() && !$this->config->outputIsPiped()) {
$this->output->writeln(\sprintf('<whisper>%s</whisper>', $this->config->useUnicode() ? '⏎' : '\\n'));
} else {
$this->output->writeln('');
}
$this->outputWantsNewline = false;
}


if ($this->stdoutBuffer !== '') {
$this->context->setLastStdout($this->stdoutBuffer);
$this->stdoutBuffer = '';
}

if ($this->output instanceof ShellOutput) {
$this->output->stopPaging();
}
}
}












public function writeReturnValue($ret, bool $rawOutput = false)
{
$this->lastExecSuccess = true;

if ($ret instanceof NoReturnValue) {
return;
}

$this->context->setReturnValue($ret);

if ($rawOutput) {
$formatted = \var_export($ret, true);
} else {
$prompt = $this->config->theme()->returnValue();
$indent = \str_repeat(' ', \strlen($prompt));
$formatted = $this->presentValue($ret);
$formattedRetValue = \sprintf('<whisper>%s</whisper>', $prompt);

$formatted = $formattedRetValue.\str_replace(\PHP_EOL, \PHP_EOL.$indent, $formatted);
}

if ($this->output instanceof ShellOutput) {
$this->output->page($formatted.\PHP_EOL);
} else {
$this->output->writeln($formatted);
}
}











public function writeException(\Throwable $e)
{

if ($e instanceof BreakException && $this->nonInteractive) {
$this->resetCodeBuffer();

return;
}


if (!$e instanceof BreakException) {
$this->lastExecSuccess = false;
$this->context->setLastException($e);
}

$output = $this->output;
if ($output instanceof ConsoleOutput) {
$output = $output->getErrorOutput();
}

if (!$this->config->theme()->compact()) {
$output->writeln('');
}

$output->writeln($this->formatException($e));

if (!$this->config->theme()->compact()) {
$output->writeln('');
}


if (!$e instanceof BreakException && $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
$trace = TraceFormatter::formatTrace($e);
if (\count($trace) !== 0) {
$output->writeln('--');
$output->write($trace, true);
$output->writeln('');
}
}

$this->resetCodeBuffer();
}






public function getLastExecSuccess(): bool
{
return $this->lastExecSuccess;
}








public function formatException(\Throwable $e): string
{
$indent = $this->config->theme()->compact() ? '' : '  ';

if ($e instanceof BreakException) {
return \sprintf('%s<info> INFO </info> %s.', $indent, \rtrim($e->getRawMessage(), '.'));
} elseif ($e instanceof PsyException) {
$message = $e->getLine() > 1
? \sprintf('%s in %s on line %d', $e->getRawMessage(), $e->getFile(), $e->getLine())
: \sprintf('%s in %s', $e->getRawMessage(), $e->getFile());

$messageLabel = \strtoupper($this->getMessageLabel($e));
} else {
$message = $e->getMessage();
$messageLabel = $this->getMessageLabel($e);
}

$message = \preg_replace(
"#(\\w:)?([\\\\/]\\w+)*[\\\\/]src[\\\\/]Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code#",
"eval()'d code",
$message
);

$message = \str_replace(" in eval()'d code", '', $message);
$message = \trim($message);


if (!empty($message) && !\in_array(\substr($message, -1), ['.', '?', '!', ':'])) {
$message = "$message.";
}


$message = \str_replace(\getcwd().\DIRECTORY_SEPARATOR, '', $message);

$severity = ($e instanceof \ErrorException) ? $this->getSeverity($e) : 'error';

return \sprintf('%s<%s> %s </%s> %s', $indent, $severity, $messageLabel, $severity, OutputFormatter::escape($message));
}






protected function getSeverity(\ErrorException $e): string
{
$severity = $e->getSeverity();
if ($severity & \error_reporting()) {
switch ($severity) {
case \E_WARNING:
case \E_NOTICE:
case \E_CORE_WARNING:
case \E_COMPILE_WARNING:
case \E_USER_WARNING:
case \E_USER_NOTICE:
case \E_USER_DEPRECATED:
case \E_DEPRECATED:
case \E_STRICT:
return 'warning';

default:
return 'error';
}
} else {

return 'warning';
}
}






protected function getMessageLabel(\Throwable $e): string
{
if ($e instanceof \ErrorException) {
$severity = $e->getSeverity();

if ($severity & \error_reporting()) {
switch ($severity) {
case \E_WARNING:
return 'Warning';
case \E_NOTICE:
return 'Notice';
case \E_CORE_WARNING:
return 'Core Warning';
case \E_COMPILE_WARNING:
return 'Compile Warning';
case \E_USER_WARNING:
return 'User Warning';
case \E_USER_NOTICE:
return 'User Notice';
case \E_USER_DEPRECATED:
return 'User Deprecated';
case \E_DEPRECATED:
return 'Deprecated';
case \E_STRICT:
return 'Strict';
}
}
}

if ($e instanceof PsyException || $e instanceof SymfonyConsoleException) {
$exceptionShortName = (new \ReflectionClass($e))->getShortName();
$typeParts = \preg_split('/(?=[A-Z])/', $exceptionShortName);

switch ($exceptionShortName) {
case 'RuntimeException':
case 'LogicException':

break;
default:
if (\end($typeParts) === 'Exception') {
\array_pop($typeParts);
}
break;
}

return \trim(\strtoupper(\implode(' ', $typeParts)));
}

return \get_class($e);
}









public function execute(string $code, bool $throwExceptions = false)
{
$this->setCode($code, true);
$closure = new ExecutionClosure($this);

if ($throwExceptions) {
return $closure->execute();
}

try {
return $closure->execute();
} catch (\Throwable $_e) {
$this->writeException($_e);
}
}


























public function handleError($errno, $errstr, $errfile, $errline)
{




if ($errno & (\E_ERROR | \E_PARSE | \E_CORE_ERROR | \E_COMPILE_ERROR | \E_USER_ERROR | \E_RECOVERABLE_ERROR)) {
ErrorException::throwException($errno, $errstr, $errfile, $errline);
}



$errorsSuppressed = $this->errorReporting !== null && $this->errorReporting !== \error_reporting();


if ($errno & \error_reporting() || (!$errorsSuppressed && ($errno & $this->config->errorLoggingLevel()))) {
$this->writeException(new ErrorException($errstr, 0, $errno, $errfile, $errline));
}
}










protected function presentValue($val): string
{
return $this->config->getPresenter()->present($val);
}








protected function getCommand(string $input)
{
$input = new StringInput($input);
if ($name = $input->getFirstArgument()) {
return $this->get($name);
}
}








protected function hasCommand(string $input): bool
{
if (\preg_match('/([^\s]+?)(?:\s|$)/A', \ltrim($input), $match)) {
return $this->has($match[1]);
}

return false;
}






protected function getPrompt()
{
if ($this->output->isQuiet()) {
return null;
}

$theme = $this->config->theme();

if ($this->hasCode()) {
return $theme->bufferPrompt();
}

return $theme->prompt();
}














protected function readline(bool $interactive = true)
{
$prompt = $this->config->theme()->replayPrompt();

if (!empty($this->inputBuffer)) {
$line = \array_shift($this->inputBuffer);
if (!$line instanceof SilentInput) {
$this->output->writeln(\sprintf('<whisper>%s</whisper><aside>%s</aside>', $prompt, OutputFormatter::escape($line)));
}

return $line;
}

$bracketedPaste = $interactive && $this->config->useBracketedPaste();

if ($bracketedPaste) {
\printf("\e[?2004h"); 
}

$line = $this->readline->readline($this->getPrompt());

if ($bracketedPaste) {
\printf("\e[?2004l"); 
}

return $line;
}




protected function getHeader(): string
{
return \sprintf('<whisper>%s by Justin Hileman</whisper>', $this->getVersion());
}






public function getVersion(): string
{
@\trigger_error('`getVersion` is deprecated; call `self::getVersionHeader` instead.', \E_USER_DEPRECATED);

return self::getVersionHeader($this->config->useUnicode());
}






public static function getVersionHeader(bool $useUnicode = false): string
{
$separator = $useUnicode ? '—' : '-';

return \sprintf('Psy Shell %s (PHP %s %s %s)', self::VERSION, \PHP_VERSION, $separator, \PHP_SAPI);
}






public function getManualDb()
{
return $this->config->getManualDb();
}







protected function initializeTabCompletion()
{
if (!$this->config->useTabCompletion()) {
return;
}

$this->autoCompleter = $this->config->getAutoCompleter();



$this->addMatchersToAutoCompleter($this->getDefaultMatchers());
$this->addMatchersToAutoCompleter($this->matchers);

$this->autoCompleter->activate();
}






private function addMatchersToAutoCompleter(array $matchers)
{
foreach ($matchers as $matcher) {
if ($matcher instanceof ContextAware) {
$matcher->setContext($this->context);
}
$this->autoCompleter->addMatcher($matcher);
}
}






protected function writeVersionInfo()
{
if (\PHP_SAPI !== 'cli') {
return;
}

try {
$client = $this->config->getChecker();
if (!$client->isLatest()) {
$this->output->writeln(\sprintf('<whisper>New version is available at psysh.org/psysh (current: %s, latest: %s)</whisper>', self::VERSION, $client->getLatest()));
}
} catch (\InvalidArgumentException $e) {
$this->output->writeln($e->getMessage());
}
}




protected function writeStartupMessage()
{
$message = $this->config->getStartupMessage();
if ($message !== null && $message !== '') {
$this->output->writeln($message);
}
}
}
<?php










namespace Psy\VersionUpdater;

use Psy\Exception\ErrorException;

class Installer
{



protected $installLocation;




protected $tempDirectory;

public function __construct(string $tempDirectory = null)
{
$this->tempDirectory = $tempDirectory ?: \sys_get_temp_dir();
$this->installLocation = \Phar::running(false);
}




public function getTempDirectory(): string
{
return $this->tempDirectory;
}




public function isInstallLocationWritable(): bool
{
return \is_writable($this->installLocation);
}




public function isTempDirectoryWritable(): bool
{
return \is_writable($this->tempDirectory);
}






public function isValidSource(string $sourceArchive): bool
{
if (!\class_exists('\PharData')) {
return false;
}
$pharArchive = new \PharData($sourceArchive);

return $pharArchive->valid();
}






public function install(string $sourceArchive): bool
{
$pharArchive = new \PharData($sourceArchive);
$outputDirectory = \tempnam($this->tempDirectory, 'psysh-');


if (!\unlink($outputDirectory) || !\mkdir($outputDirectory, 0700)) {
return false;
}

$pharArchive->extractTo($outputDirectory, ['psysh'], true);

$renamed = \rename($outputDirectory.'/psysh', $this->installLocation);


\rmdir($outputDirectory);

return $renamed;
}






public function createBackup(string $version): bool
{
$backupFilename = $this->getBackupFilename($version);

if (\file_exists($backupFilename) && !\is_writable($backupFilename)) {
return false;
}

return \rename($this->installLocation, $backupFilename);
}








public function restoreFromBackup(string $version): bool
{
$backupFilename = $this->getBackupFilename($version);

if (!\file_exists($backupFilename)) {
throw new ErrorException("Cannot restore from backup. File not found! [{$backupFilename}]");
}

return \rename($backupFilename, $this->installLocation);
}






public function getBackupFilename(string $version): string
{
$installFilename = \basename($this->installLocation);

return \sprintf('%s/%s.%s', $this->tempDirectory, $installFilename, $version);
}
}
<?php










namespace Psy\VersionUpdater;

interface Checker
{
const ALWAYS = 'always';
const DAILY = 'daily';
const WEEKLY = 'weekly';
const MONTHLY = 'monthly';
const NEVER = 'never';

public function isLatest(): bool;

public function getLatest(): string;
}
<?php










namespace Psy\VersionUpdater;

class IntervalChecker extends GitHubChecker
{
private $cacheFile;
private $interval;

public function __construct($cacheFile, $interval)
{
$this->cacheFile = $cacheFile;
$this->interval = $interval;
}

public function fetchLatestRelease()
{

$cached = \json_decode(@\file_get_contents($this->cacheFile, false));
if ($cached && isset($cached->last_check) && isset($cached->release)) {
$now = new \DateTime();
$lastCheck = new \DateTime($cached->last_check);
if ($lastCheck >= $now->sub($this->getDateInterval())) {
return $cached->release;
}
}


$release = parent::fetchLatestRelease();
if ($release && isset($release->tag_name)) {
$this->updateCache($release);
}

return $release;
}




private function getDateInterval(): \DateInterval
{
switch ($this->interval) {
case Checker::DAILY:
return new \DateInterval('P1D');
case Checker::WEEKLY:
return new \DateInterval('P1W');
case Checker::MONTHLY:
return new \DateInterval('P1M');
}

throw new \RuntimeException('Invalid interval configured');
}

private function updateCache($release)
{
$data = [
'last_check' => \date(\DATE_ATOM),
'release' => $release,
];

\file_put_contents($this->cacheFile, \json_encode($data));
}
}
<?php










namespace Psy\VersionUpdater\Downloader;

use Psy\Exception\ErrorException;
use Psy\VersionUpdater\Downloader;

class Factory
{



public static function getDownloader(): Downloader
{
if (\extension_loaded('curl')) {
return new CurlDownloader();
} elseif (\ini_get('allow_url_fopen')) {
return new FileDownloader();
}
throw new ErrorException('No downloader available.');
}
}
<?php










namespace Psy\VersionUpdater\Downloader;

use Psy\VersionUpdater\Downloader;

class FileDownloader implements Downloader
{
private $tempDir = null;
private $outputFile = null;


public function setTempDir(string $tempDir)
{
$this->tempDir = $tempDir;
}


public function download(string $url): bool
{
$tempDir = $this->tempDir ?: \sys_get_temp_dir();
$this->outputFile = \tempnam($tempDir, 'psysh-archive-');
$targetName = $this->outputFile.'.tar.gz';

if (!\rename($this->outputFile, $targetName)) {
return false;
}

$this->outputFile = $targetName;

return (bool) \file_put_contents($this->outputFile, \file_get_contents($url));
}


public function getFilename(): string
{
return $this->outputFile;
}


public function cleanup()
{
if (\file_exists($this->outputFile)) {
\unlink($this->outputFile);
}
}
}
<?php










namespace Psy\VersionUpdater\Downloader;

use Psy\Exception\ErrorException;
use Psy\Shell;
use Psy\VersionUpdater\Downloader;

class CurlDownloader implements Downloader
{
private $tempDir = null;
private $outputFile = null;


public function setTempDir(string $tempDir)
{
$this->tempDir = $tempDir;
}


public function download(string $url): bool
{
$tempDir = $this->tempDir ?: \sys_get_temp_dir();
$this->outputFile = \tempnam($tempDir, 'psysh-archive-');
$targetName = $this->outputFile.'.tar.gz';

if (!\rename($this->outputFile, $targetName)) {
return false;
}

$this->outputFile = $targetName;

$outputHandle = \fopen($this->outputFile, 'w');
if (!$outputHandle) {
return false;
}
$curl = \curl_init();
\curl_setopt_array($curl, [
\CURLOPT_FAILONERROR => true,
\CURLOPT_HEADER => 0,
\CURLOPT_FOLLOWLOCATION => true,
\CURLOPT_TIMEOUT => 10,
\CURLOPT_FILE => $outputHandle,
\CURLOPT_HTTPHEADER => [
'User-Agent' => 'PsySH/'.Shell::VERSION,
],
]);
\curl_setopt($curl, \CURLOPT_URL, $url);
$result = \curl_exec($curl);
$error = \curl_error($curl);
\curl_close($curl);

\fclose($outputHandle);

if (!$result) {
throw new ErrorException('cURL Error: '.$error);
}

return (bool) $result;
}


public function getFilename(): string
{
return $this->outputFile;
}


public function cleanup()
{
if (\file_exists($this->outputFile)) {
\unlink($this->outputFile);
}
}
}
<?php










namespace Psy\VersionUpdater;

use Psy\Exception\ErrorException;

interface Downloader
{





public function setTempDir(string $tempDir);






public function download(string $url): bool;




public function getFilename(): string;






public function cleanup();
}
<?php










namespace Psy\VersionUpdater;

use Psy\Exception\ErrorException;
use Psy\Shell;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;






class SelfUpdate
{
const URL_PREFIX = 'https://github.com/bobthecow/psysh/releases/download';
const SUCCESS = 0;
const FAILURE = 1;


private $checker;


private $installer;


private $downloader;

public function __construct(Checker $checker, Installer $installer)
{
$this->checker = $checker;
$this->installer = $installer;
}








public function setDownloader(Downloader $downloader)
{
$this->downloader = $downloader;
}






private function getDownloader(): Downloader
{
if (!isset($this->downloader)) {
return Downloader\Factory::getDownloader();
}

return $this->downloader;
}









private function getAssetUrl(string $latestVersion): string
{
$versionPostfix = '';
if (\strpos(Shell::VERSION, '+')) {
$versionPostfix = '-'.\substr(Shell::VERSION, \strpos(Shell::VERSION, '+') + 1);
}
$downloadFilename = \sprintf('psysh-%s%s.tar.gz', $latestVersion, $versionPostfix);



return \sprintf('%s/%s/%s', self::URL_PREFIX, $latestVersion, $downloadFilename);
}









public function run(InputInterface $input, OutputInterface $output): int
{
$currentVersion = Shell::VERSION;


if ($this->checker->isLatest()) {

$output->writeln('<info>Current version is up-to-date.</info>');

return self::SUCCESS;
}


if (!$this->installer->isInstallLocationWritable()) {
$output->writeln('<error>Installed version is not writable.</error>');

return self::FAILURE;
}

if (!$this->installer->isTempDirectoryWritable()) {
$output->writeln('<error>Temporary directory is not writable.</error>');

return self::FAILURE;
}

$latestVersion = $this->checker->getLatest();
$downloadUrl = $this->getAssetUrl($latestVersion);

$output->write("Downloading PsySH $latestVersion ...");

try {
$downloader = $this->getDownloader();
$downloader->setTempDir($this->installer->getTempDirectory());
$downloaded = $downloader->download($downloadUrl);
} catch (ErrorException $e) {
$output->write(' <error>Failed.</error>');
$output->writeln(\sprintf('<error>%s</error>', $e->getMessage()));

return self::FAILURE;
}

if (!$downloaded) {
$output->writeln('<error>Download failed.</error>');
$downloader->cleanup();

return self::FAILURE;
} else {
$output->write(' <info>OK</info>'.\PHP_EOL);
}

$downloadedFile = $downloader->getFilename();

if (!$this->installer->isValidSource($downloadedFile)) {
$downloader->cleanup();
$output->writeln('<error>Downloaded file is not a valid archive.</error>');

return self::FAILURE;
}


$backupCreated = $this->installer->createBackup($currentVersion);
if (!$backupCreated) {
$downloader->cleanup();
$output->writeln('<error>Failed to create a backup of the current version.</error>');

return self::FAILURE;
} elseif ($input->getOption('verbose')) {
$backupFilename = $this->installer->getBackupFilename($currentVersion);
$output->writeln('Created backup of current version: '.$backupFilename);
}

if (!$this->installer->install($downloadedFile)) {
$this->installer->restoreFromBackup($currentVersion);
$downloader->cleanup();
$output->writeln("<error>Failed to install new PsySH version $latestVersion.</error>");

return self::FAILURE;
}


$downloader->cleanup();

$output->writeln("Updated PsySH from $currentVersion to <info>$latestVersion</info>");

return self::SUCCESS;
}
}
<?php










namespace Psy\VersionUpdater;

use Psy\Shell;

class GitHubChecker implements Checker
{
const URL = 'https://api.github.com/repos/bobthecow/psysh/releases/latest';

private $latest;

public function isLatest(): bool
{


$version = \preg_replace('/[+-]\w+/', '', Shell::VERSION);

return \version_compare($version, $this->getLatest(), '>=');
}

public function getLatest(): string
{
if (!isset($this->latest)) {
$this->setLatest($this->getVersionFromTag());
}

return $this->latest;
}




public function setLatest(string $version)
{
$this->latest = $version;
}




private function getVersionFromTag()
{
$contents = $this->fetchLatestRelease();
if (!$contents || !isset($contents->tag_name)) {
throw new \InvalidArgumentException('Unable to check for updates');
}
$this->setLatest($contents->tag_name);

return $this->getLatest();
}






public function fetchLatestRelease()
{
$context = \stream_context_create([
'http' => [
'user_agent' => 'PsySH/'.Shell::VERSION,
'timeout' => 1.0,
],
]);

\set_error_handler(function () {


});

$result = @\file_get_contents(self::URL, false, $context);

\restore_error_handler();

return \json_decode($result);
}
}
<?php










namespace Psy\VersionUpdater;

use Psy\Shell;




class NoopChecker implements Checker
{
public function isLatest(): bool
{
return true;
}

public function getLatest(): string
{
return Shell::VERSION;
}
}
<?php










namespace Psy;

use Psy\Exception\BreakException;
use Psy\Exception\ThrowUpException;






class ExecutionLoopClosure extends ExecutionClosure
{



public function __construct(Shell $__psysh__)
{
$this->setClosure($__psysh__, function () use ($__psysh__) {

\extract($__psysh__->getScopeVariables(false));

while (true) {
$__psysh__->beforeLoop();

try {
$__psysh__->getInput();

try {

if ($__psysh__->getLastExecSuccess()) {
\extract($__psysh__->getScopeVariablesDiff(\get_defined_vars()));
}


\ob_start([$__psysh__, 'writeStdout'], 1);


\set_error_handler([$__psysh__, 'handleError']);


$_ = eval($__psysh__->onExecute($__psysh__->flushCode() ?: ExecutionClosure::NOOP_INPUT));
} catch (\Throwable $_e) {

if (\ob_get_level() > 0) {
\ob_end_clean();
}

throw $_e;
} finally {

\restore_error_handler();
}


\ob_end_flush();


$__psysh__->setScopeVariables(\get_defined_vars());

$__psysh__->writeReturnValue($_);
} catch (BreakException $_e) {
$__psysh__->writeException($_e);

return;
} catch (ThrowUpException $_e) {
$__psysh__->writeException($_e);

throw $_e;
} catch (\Throwable $_e) {
$__psysh__->writeException($_e);
}

$__psysh__->afterLoop();
}
});
}
}
<?php










namespace Psy;

use PhpParser\Parser;
use PhpParser\ParserFactory as OriginalParserFactory;




class ParserFactory
{



public function createParser(): Parser
{
$factory = new OriginalParserFactory();

if (!\method_exists($factory, 'createForHostVersion')) {
return $factory->create(OriginalParserFactory::PREFER_PHP7);
}

return $factory->createForHostVersion();
}
}
<?php










namespace Psy;




interface EnvInterface
{





public function get(string $key);
}
<?php










namespace Psy;




class SuperglobalsEnv implements EnvInterface
{





public function get(string $key)
{
if (isset($_SERVER[$key]) && $_SERVER[$key]) {
return $_SERVER[$key];
}

return null;
}
}
<?php










namespace Psy\Reflection;




class ReflectionLanguageConstruct extends \ReflectionFunctionAbstract
{
public $keyword;




private static $languageConstructs = [
'isset' => [
'var' => [],
'...' => [
'isOptional' => true,
'defaultValue' => null,
],
],

'unset' => [
'var' => [],
'...' => [
'isOptional' => true,
'defaultValue' => null,
],
],

'empty' => [
'var' => [],
],

'echo' => [
'arg1' => [],
'...' => [
'isOptional' => true,
'defaultValue' => null,
],
],

'print' => [
'arg' => [],
],

'die' => [
'status' => [
'isOptional' => true,
'defaultValue' => 0,
],
],

'exit' => [
'status' => [
'isOptional' => true,
'defaultValue' => 0,
],
],
];






public function __construct(string $keyword)
{
if (!self::isLanguageConstruct($keyword)) {
throw new \InvalidArgumentException('Unknown language construct: '.$keyword);
}

$this->keyword = $keyword;
}






public static function export($name)
{
throw new \RuntimeException('Not yet implemented because it\'s unclear what I should do here :)');
}




public function getName(): string
{
return $this->keyword;
}




public function returnsReference(): bool
{
return false;
}






public function getParameters(): array
{
$params = [];
foreach (self::$languageConstructs[$this->keyword] as $parameter => $opts) {
$params[] = new ReflectionLanguageConstructParameter($this->keyword, $parameter, $opts);
}

return $params;
}










#[\ReturnTypeWillChange]
public function getFileName()
{
return false;
}




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






public static function isLanguageConstruct(string $keyword): bool
{
return \array_key_exists($keyword, self::$languageConstructs);
}
}
<?php










namespace Psy\Reflection;






class ReflectionLanguageConstructParameter extends \ReflectionParameter
{
private $function;
private $parameter;
private $opts;

public function __construct($function, $parameter, array $opts)
{
$this->function = $function;
$this->parameter = $parameter;
$this->opts = $opts;
}




public function getClass(): ?\ReflectionClass
{
return null;
}






public function isArray(): bool
{
return \array_key_exists('isArray', $this->opts) && $this->opts['isArray'];
}








#[\ReturnTypeWillChange]
public function getDefaultValue()
{
if ($this->isDefaultValueAvailable()) {
return $this->opts['defaultValue'];
}

return null;
}






public function getName(): string
{
return $this->parameter;
}






public function isOptional(): bool
{
return \array_key_exists('isOptional', $this->opts) && $this->opts['isOptional'];
}






public function isDefaultValueAvailable(): bool
{
return \array_key_exists('defaultValue', $this->opts);
}








public function isPassedByReference(): bool
{
return \array_key_exists('isPassedByReference', $this->opts) && $this->opts['isPassedByReference'];
}
}
<?php










namespace Psy\Reflection;






class ReflectionConstant implements \Reflector
{
public $name;
private $value;

private static $magicConstants = [
'__LINE__',
'__FILE__',
'__DIR__',
'__FUNCTION__',
'__CLASS__',
'__TRAIT__',
'__METHOD__',
'__NAMESPACE__',
'__COMPILER_HALT_OFFSET__',
];






public function __construct(string $name)
{
$this->name = $name;

if (!\defined($name) && !self::isMagicConstant($name)) {
throw new \InvalidArgumentException('Unknown constant: '.$name);
}

if (!self::isMagicConstant($name)) {
$this->value = @\constant($name);
}
}









public static function export(string $name, bool $return = false)
{
$refl = new self($name);
$value = $refl->getValue();

$str = \sprintf('Constant [ %s %s ] { %s }', \gettype($value), $refl->getName(), $value);

if ($return) {
return $str;
}

echo $str."\n";
}

public static function isMagicConstant($name)
{
return \in_array($name, self::$magicConstants);
}






public function getDocComment(): bool
{
return false;
}




public function getName(): string
{
return $this->name;
}






public function getNamespaceName(): string
{
if (!$this->inNamespace()) {
return '';
}

return \preg_replace('/\\\\[^\\\\]+$/', '', $this->name);
}






public function getValue()
{
return $this->value;
}




public function inNamespace(): bool
{
return \strpos($this->name, '\\') !== false;
}




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







public function getFileName()
{
return;

}






public function getStartLine()
{
throw new \RuntimeException('Not yet implemented because it\'s unclear what I should do here :)');
}






public function getEndLine()
{
return $this->getStartLine();
}
}
<?php










namespace Psy\Reflection;




class ReflectionNamespace implements \Reflector
{
private $name;






public function __construct(string $name)
{
$this->name = $name;
}






public function getName(): string
{
return $this->name;
}






public static function export($name)
{
throw new \RuntimeException('Not yet implemented because it\'s unclear what I should do here :)');
}






public function __toString(): string
{
return $this->getName();
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\AssignRef;
use PhpParser\Node\Stmt\Foreach_;
use Psy\Exception\FatalErrorException;




class EmptyArrayDimFetchPass extends CodeCleanerPass
{
const EXCEPTION_MESSAGE = 'Cannot use [] for reading';

private $theseOnesAreFine = [];




public function beforeTraverse(array $nodes)
{
$this->theseOnesAreFine = [];
}








public function enterNode(Node $node)
{
if ($node instanceof Assign && $node->var instanceof ArrayDimFetch) {
$this->theseOnesAreFine[] = $node->var;
} elseif ($node instanceof AssignRef && $node->expr instanceof ArrayDimFetch) {
$this->theseOnesAreFine[] = $node->expr;
} elseif ($node instanceof Foreach_ && $node->valueVar instanceof ArrayDimFetch) {
$this->theseOnesAreFine[] = $node->valueVar;
} elseif ($node instanceof ArrayDimFetch && $node->var instanceof ArrayDimFetch) {

if (\in_array($node, $this->theseOnesAreFine)) {
$this->theseOnesAreFine[] = $node->var;
}
}

if ($node instanceof ArrayDimFetch && $node->dim === null) {
if (!\in_array($node, $this->theseOnesAreFine)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, $node->getStartLine());
}
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\MagicConst\Dir;
use PhpParser\Node\Scalar\MagicConst\File;
use PhpParser\Node\Scalar\String_;




class MagicConstantsPass extends CodeCleanerPass
{








public function enterNode(Node $node)
{
if ($node instanceof Dir) {
return new FuncCall(new Name('getcwd'), [], $node->getAttributes());
} elseif ($node instanceof File) {
return new String_('', $node->getAttributes());
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use Psy\Exception\FatalErrorException;




class PassableByReferencePass extends CodeCleanerPass
{
const EXCEPTION_MESSAGE = 'Only variables can be passed by reference';








public function enterNode(Node $node)
{

if ($node instanceof FuncCall) {

if ($node->name instanceof Expr || $node->name instanceof Variable) {
return;
}

$name = (string) $node->name;

if ($name === 'array_multisort') {
return $this->validateArrayMultisort($node);
}

try {
$refl = new \ReflectionFunction($name);
} catch (\ReflectionException $e) {

return;
}

foreach ($refl->getParameters() as $key => $param) {
if (\array_key_exists($key, $node->args)) {
$arg = $node->args[$key];
if ($param->isPassedByReference() && !$this->isPassableByReference($arg)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
}
}

private function isPassableByReference(Node $arg): bool
{

if ($arg->value instanceof Array_) {
return $arg->unpack;
}



return $arg->value instanceof ClassConstFetch ||
$arg->value instanceof PropertyFetch ||
$arg->value instanceof Variable ||
$arg->value instanceof FuncCall ||
$arg->value instanceof MethodCall ||
$arg->value instanceof StaticCall ||
$arg->value instanceof ArrayDimFetch;
}
















private function validateArrayMultisort(Node $node)
{
$nonPassable = 2; 
foreach ($node->args as $arg) {
if ($this->isPassableByReference($arg)) {
$nonPassable = 0;
} elseif (++$nonPassable > 2) {


throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Namespace_;
use Psy\CodeCleaner;












class NamespacePass extends CodeCleanerPass
{
private $namespace = null;
private $cleaner;




public function __construct(CodeCleaner $cleaner)
{
$this->cleaner = $cleaner;
}











public function beforeTraverse(array $nodes)
{
if (empty($nodes)) {
return $nodes;
}

$last = \end($nodes);

if ($last instanceof Namespace_) {
$kind = $last->getAttribute('kind');



if ($kind === null || $kind === Namespace_::KIND_SEMICOLON) {

$this->setNamespace($last->name);
} else {

$this->setNamespace(null);
}

return $nodes;
}

return $this->namespace ? [new Namespace_($this->namespace, $nodes)] : $nodes;
}







private function setNamespace($namespace)
{
$this->namespace = $namespace;
$this->cleaner->setNamespace($namespace === null ? null : $this->getParts($namespace));
}






protected function getParts(Name $name): array
{
return \method_exists($name, 'getParts') ? $name->getParts() : $name->parts;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Goto_;
use PhpParser\Node\Stmt\Label;
use Psy\Exception\FatalErrorException;













class LabelContextPass extends CodeCleanerPass
{

private $functionDepth;


private $labelDeclarations;

private $labelGotos;






public function beforeTraverse(array $nodes)
{
$this->functionDepth = 0;
$this->labelDeclarations = [];
$this->labelGotos = [];
}




public function enterNode(Node $node)
{
if ($node instanceof FunctionLike) {
$this->functionDepth++;

return;
}


if ($this->functionDepth !== 0) {
return;
}

if ($node instanceof Goto_) {
$this->labelGotos[\strtolower($node->name)] = $node->getStartLine();
} elseif ($node instanceof Label) {
$this->labelDeclarations[\strtolower($node->name)] = $node->getStartLine();
}
}






public function leaveNode(Node $node)
{
if ($node instanceof FunctionLike) {
$this->functionDepth--;
}
}




public function afterTraverse(array $nodes)
{
foreach ($this->labelGotos as $name => $line) {
if (!isset($this->labelDeclarations[$name])) {
$msg = "'goto' to undefined label '{$name}'";
throw new FatalErrorException($msg, 0, \E_ERROR, null, $line);
}
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Exit_;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Break_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Switch_;




class ImplicitReturnPass extends CodeCleanerPass
{





public function beforeTraverse(array $nodes): array
{
return $this->addImplicitReturn($nodes);
}






private function addImplicitReturn(array $nodes): array
{

if (empty($nodes)) {
return [new Return_(NoReturnValue::create())];
}

$last = \end($nodes);





if ($last instanceof If_) {
$last->stmts = $this->addImplicitReturn($last->stmts);

foreach ($last->elseifs as $elseif) {
$elseif->stmts = $this->addImplicitReturn($elseif->stmts);
}

if ($last->else) {
$last->else->stmts = $this->addImplicitReturn($last->else->stmts);
}
} elseif ($last instanceof Switch_) {
foreach ($last->cases as $case) {

$caseLast = \end($case->stmts);
if ($caseLast instanceof Break_) {
$case->stmts = $this->addImplicitReturn(\array_slice($case->stmts, 0, -1));
$case->stmts[] = $caseLast;
}
}
} elseif ($last instanceof Expr && !($last instanceof Exit_)) {

$nodes[\count($nodes) - 1] = new Return_($last, [
'startLine' => $last->getStartLine(),
'endLine' => $last->getEndLine(),
]);

} elseif ($last instanceof Expression && !($last->expr instanceof Exit_)) {
$nodes[\count($nodes) - 1] = new Return_($last->expr, [
'startLine' => $last->getStartLine(),
'endLine' => $last->getEndLine(),
]);
} elseif ($last instanceof Namespace_) {
$last->stmts = $this->addImplicitReturn($last->stmts);
}











if (self::isNonExpressionStmt($last)) {
$nodes[] = new Return_(NoReturnValue::create());
}

return $nodes;
}









private static function isNonExpressionStmt(Node $node): bool
{
return $node instanceof Stmt &&
!$node instanceof Expression &&
!$node instanceof Return_ &&
!$node instanceof Namespace_;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Psy\Exception\FatalErrorException;




class AbstractClassPass extends CodeCleanerPass
{
private $class;
private $abstractMethods;








public function enterNode(Node $node)
{
if ($node instanceof Class_) {
$this->class = $node;
$this->abstractMethods = [];
} elseif ($node instanceof ClassMethod) {
if ($node->isAbstract()) {
$name = \sprintf('%s::%s', $this->class->name, $node->name);
$this->abstractMethods[] = $name;

if ($node->stmts !== null) {
$msg = \sprintf('Abstract function %s cannot contain body', $name);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
}








public function leaveNode(Node $node)
{
if ($node instanceof Class_) {
$count = \count($this->abstractMethods);
if ($count > 0 && !$node->isAbstract()) {
$msg = \sprintf(
'Class %s contains %d abstract method%s must therefore be declared abstract or implement the remaining methods (%s)',
$node->name,
$count,
($count === 1) ? '' : 's',
\implode(', ', $this->abstractMethods)
);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\VariadicPlaceholder;
use Psy\Exception\ErrorException;





class CalledClassPass extends CodeCleanerPass
{
private $inClass;






public function beforeTraverse(array $nodes)
{
$this->inClass = false;
}








public function enterNode(Node $node)
{
if ($node instanceof Class_ || $node instanceof Trait_) {
$this->inClass = true;
} elseif ($node instanceof FuncCall && !$this->inClass) {




if (!empty($node->args) && !$this->isNull($node->args[0])) {
return;
}


if (!($node->name instanceof Name)) {
return;
}

$name = \strtolower($node->name);
if (\in_array($name, ['get_class', 'get_called_class'])) {
$msg = \sprintf('%s() called without object from outside a class', $name);
throw new ErrorException($msg, 0, \E_USER_WARNING, null, $node->getStartLine());
}
}
}






public function leaveNode(Node $node)
{
if ($node instanceof Class_) {
$this->inClass = false;
}
}

private function isNull(Node $node): bool
{
if ($node instanceof VariadicPlaceholder) {
return false;
}

return $node->value instanceof ConstFetch && \strtolower($node->value->name) === 'null';
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Isset_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Unset_;
use PhpParser\Node\VariadicPlaceholder;
use Psy\Exception\FatalErrorException;






class FunctionReturnInWriteContextPass extends CodeCleanerPass
{
const ISSET_MESSAGE = 'Cannot use isset() on the result of an expression (you can use "null !== expression" instead)';
const EXCEPTION_MESSAGE = "Can't use function return value in write context";












public function enterNode(Node $node)
{
if ($node instanceof Array_ || $this->isCallNode($node)) {
$items = $node instanceof Array_ ? $node->items : $node->args;
foreach ($items as $item) {
if ($item instanceof VariadicPlaceholder) {
continue;
}

if ($item && $item->byRef && $this->isCallNode($item->value)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
} elseif ($node instanceof Isset_ || $node instanceof Unset_) {
foreach ($node->vars as $var) {
if (!$this->isCallNode($var)) {
continue;
}

$msg = $node instanceof Isset_ ? self::ISSET_MESSAGE : self::EXCEPTION_MESSAGE;
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
} elseif ($node instanceof Assign && $this->isCallNode($node->var)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}

private function isCallNode(Node $node): bool
{
return $node instanceof FuncCall || $node instanceof MethodCall || $node instanceof StaticCall;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Stmt\Namespace_;




abstract class NamespaceAwarePass extends CodeCleanerPass
{
protected $namespace;
protected $currentScope;









public function beforeTraverse(array $nodes)
{
$this->namespace = [];
$this->currentScope = [];
}









public function enterNode(Node $node)
{
if ($node instanceof Namespace_) {
$this->namespace = isset($node->name) ? $this->getParts($node->name) : [];
}
}






protected function getFullyQualifiedName($name): string
{
if ($name instanceof FullyQualifiedName) {
return \implode('\\', $this->getParts($name));
}

if ($name instanceof Name) {
$name = $this->getParts($name);
} elseif (!\is_array($name)) {
$name = [$name];
}

return \implode('\\', \array_merge($this->namespace, $name));
}






protected function getParts(Name $name): array
{
return \method_exists($name, 'getParts') ? $name->getParts() : $name->parts;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\UnionType;
use Psy\Exception\FatalErrorException;




class ReturnTypePass extends CodeCleanerPass
{
const MESSAGE = 'A function with return type must return a value';
const NULLABLE_MESSAGE = 'A function with return type must return a value (did you mean "return null;" instead of "return;"?)';
const VOID_MESSAGE = 'A void function must not return a value';
const VOID_NULL_MESSAGE = 'A void function must not return a value (did you mean "return;" instead of "return null;"?)';
const NULLABLE_VOID_MESSAGE = 'Void type cannot be nullable';

private $returnTypeStack = [];






public function enterNode(Node $node)
{
if ($this->isFunctionNode($node)) {
$this->returnTypeStack[] = $node->returnType;

return;
}

if (!empty($this->returnTypeStack) && $node instanceof Return_) {
$expectedType = \end($this->returnTypeStack);
if ($expectedType === null) {
return;
}

$msg = null;

if ($this->typeName($expectedType) === 'void') {

if ($expectedType instanceof NullableType) {
$msg = self::NULLABLE_VOID_MESSAGE;
} elseif ($node->expr instanceof ConstFetch && \strtolower($node->expr->name) === 'null') {
$msg = self::VOID_NULL_MESSAGE;
} elseif ($node->expr !== null) {
$msg = self::VOID_MESSAGE;
}
} else {

if ($node->expr === null) {
$msg = $expectedType instanceof NullableType ? self::NULLABLE_MESSAGE : self::MESSAGE;
}
}

if ($msg !== null) {
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}
}






public function leaveNode(Node $node)
{
if (!empty($this->returnTypeStack) && $this->isFunctionNode($node)) {
\array_pop($this->returnTypeStack);
}
}

private function isFunctionNode(Node $node): bool
{
return $node instanceof Function_ || $node instanceof Closure;
}

private function typeName(Node $node): string
{
if ($node instanceof UnionType) {
return \implode('|', \array_map([$this, 'typeName'], $node->types));
}

if ($node instanceof NullableType) {
return \strtolower($node->type->name);
}

if ($node instanceof Identifier) {
return \strtolower($node->name);
}

throw new \InvalidArgumentException('Unable to find type name');
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Isset_;
use PhpParser\Node\Expr\NullsafePropertyFetch;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use Psy\Exception\FatalErrorException;





class IssetPass extends CodeCleanerPass
{
const EXCEPTION_MSG = 'Cannot use isset() on the result of an expression (you can use "null !== expression" instead)';








public function enterNode(Node $node)
{
if (!$node instanceof Isset_) {
return;
}

foreach ($node->vars as $var) {
if (!$var instanceof Variable && !$var instanceof ArrayDimFetch && !$var instanceof PropertyFetch && !$var instanceof NullsafePropertyFetch) {
throw new FatalErrorException(self::EXCEPTION_MSG, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Namespace_;
use Psy\Exception\FatalErrorException;














class ValidConstructorPass extends CodeCleanerPass
{
private $namespace;




public function beforeTraverse(array $nodes)
{
$this->namespace = [];
}











public function enterNode(Node $node)
{
if ($node instanceof Namespace_) {
$this->namespace = isset($node->name) ? $this->getParts($node->name) : [];
} elseif ($node instanceof Class_) {
$constructor = null;
foreach ($node->stmts as $stmt) {
if ($stmt instanceof ClassMethod) {

if ('__construct' === \strtolower($stmt->name)) {
$this->validateConstructor($stmt, $node);

return;
}


if (empty($this->namespace) && \strtolower($node->name) === \strtolower($stmt->name)) {
$constructor = $stmt;
}
}
}

if ($constructor) {
$this->validateConstructor($constructor, $node);
}
}
}








private function validateConstructor(Node $constructor, Node $classNode)
{
if ($constructor->isStatic()) {
$msg = \sprintf(
'Constructor %s::%s() cannot be static',
\implode('\\', \array_merge($this->namespace, (array) $classNode->name->toString())),
$constructor->name
);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getStartLine());
}

if (\method_exists($constructor, 'getReturnType') && $constructor->getReturnType()) {
$msg = \sprintf(
'Constructor %s::%s() cannot declare a return type',
\implode('\\', \array_merge($this->namespace, (array) $classNode->name->toString())),
$constructor->name
);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getStartLine());
}
}






protected function getParts(Name $name): array
{
return \method_exists($name, 'getParts') ? $name->getParts() : $name->parts;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;








class NoReturnValue
{



public static function create(): New_
{
return new New_(new FullyQualifiedName(self::class));
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Exit_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use Psy\Exception\BreakException;

class ExitPass extends CodeCleanerPass
{







public function leaveNode(Node $node)
{
if ($node instanceof Exit_) {
return new StaticCall(new FullyQualifiedName(BreakException::class), 'exitShell');
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Include_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Scalar\LNumber;
use Psy\Exception\ErrorException;
use Psy\Exception\FatalErrorException;




class RequirePass extends CodeCleanerPass
{
private static $requireTypes = [Include_::TYPE_REQUIRE, Include_::TYPE_REQUIRE_ONCE];






public function enterNode(Node $origNode)
{
if (!$this->isRequireNode($origNode)) {
return;
}

$node = clone $origNode;










$node->expr = new StaticCall(
new FullyQualifiedName(self::class),
'resolve',
[new Arg($origNode->expr), new Arg(new LNumber($origNode->getStartLine()))],
$origNode->getAttributes()
);

return $node;
}

















public static function resolve($file, $startLine = null): string
{
$file = (string) $file;

if ($file === '') {



if (\E_WARNING & \error_reporting()) {
ErrorException::throwException(\E_WARNING, 'Filename cannot be empty', null, $startLine);
}


}

$resolvedPath = \stream_resolve_include_path($file);
if ($file === '' || !$resolvedPath) {
$msg = \sprintf("Failed opening required '%s'", $file);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $startLine);
}







if ($resolvedPath !== $file && $file[0] !== '.') {
$runningPhar = \Phar::running();
if (\strpos($runningPhar, 'psysh') !== false && \is_file($runningPhar.\DIRECTORY_SEPARATOR.$file)) {
foreach (self::getIncludePath() as $prefix) {
$resolvedPath = $prefix.\DIRECTORY_SEPARATOR.$file;
if (\is_file($resolvedPath)) {
return $resolvedPath;
}
}
}
}

return $file;
}

private function isRequireNode(Node $node): bool
{
return $node instanceof Include_ && \in_array($node->type, self::$requireTypes);
}

private static function getIncludePath(): array
{
if (\PATH_SEPARATOR === ':') {
return \preg_split('#:(?!//)#', \get_include_path());
}

return \explode(\PATH_SEPARATOR, \get_include_path());
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use Psy\Exception\ParseErrorException;




class ListPass extends CodeCleanerPass
{









public function enterNode(Node $node)
{
if (!$node instanceof Assign) {
return;
}

if (!$node->var instanceof Array_ && !$node->var instanceof List_) {
return;
}


$items = isset($node->var->items) ? $node->var->items : $node->var->vars;

if ($items === [] || $items === [null]) {
throw new ParseErrorException('Cannot use empty list', ['startLine' => $node->var->getStartLine(), 'endLine' => $node->var->getEndLine()]);
}

$itemFound = false;
foreach ($items as $item) {
if ($item === null) {
continue;
}

$itemFound = true;

if (!self::isValidArrayItem($item)) {
$msg = 'Assignments can only happen to writable values';
throw new ParseErrorException($msg, ['startLine' => $item->getStartLine(), 'endLine' => $item->getEndLine()]);
}
}

if (!$itemFound) {
throw new ParseErrorException('Cannot use empty list');
}
}






private static function isValidArrayItem(Node $item): bool
{
$value = ($item instanceof ArrayItem) ? $item->value : $item;

while ($value instanceof ArrayDimFetch || $value instanceof PropertyFetch) {
$value = $value->var;
}



return $value instanceof Variable || $value instanceof MethodCall || $value instanceof FuncCall;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use Psy\Exception\RuntimeException;




class LeavePsyshAlonePass extends CodeCleanerPass
{









public function enterNode(Node $node)
{
if ($node instanceof Variable && $node->name === '__psysh__') {
throw new RuntimeException('Don\'t mess with $__psysh__; bad things will happen');
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Variable;
use Psy\Exception\FatalErrorException;






class AssignThisVariablePass extends CodeCleanerPass
{









public function enterNode(Node $node)
{
if ($node instanceof Assign && $node->var instanceof Variable && $node->var->name === 'this') {
throw new FatalErrorException('Cannot re-assign $this', 0, \E_ERROR, null, $node->getStartLine());
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\VariadicPlaceholder;
use Psy\Exception\FatalErrorException;








class CallTimePassByReferencePass extends CodeCleanerPass
{
const EXCEPTION_MESSAGE = 'Call-time pass-by-reference has been removed';










public function enterNode(Node $node)
{
if (!$node instanceof FuncCall && !$node instanceof MethodCall && !$node instanceof StaticCall) {
return;
}

foreach ($node->args as $arg) {
if ($arg instanceof VariadicPlaceholder) {
continue;
}

if ($arg->byRef) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\NodeVisitorAbstract;




abstract class CodeCleanerPass extends NodeVisitorAbstract
{

}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Do_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\While_;
use Psy\Exception\FatalErrorException;







class ValidClassNamePass extends NamespaceAwarePass
{
const CLASS_TYPE = 'class';
const INTERFACE_TYPE = 'interface';
const TRAIT_TYPE = 'trait';

private $conditionalScopes = 0;












public function enterNode(Node $node)
{
parent::enterNode($node);

if (self::isConditional($node)) {
$this->conditionalScopes++;

return;
}

if ($this->conditionalScopes === 0) {
if ($node instanceof Class_) {
$this->validateClassStatement($node);
} elseif ($node instanceof Interface_) {
$this->validateInterfaceStatement($node);
} elseif ($node instanceof Trait_) {
$this->validateTraitStatement($node);
}
}
}






public function leaveNode(Node $node)
{
if (self::isConditional($node)) {
$this->conditionalScopes--;
}
}

private static function isConditional(Node $node): bool
{
return $node instanceof If_ ||
$node instanceof While_ ||
$node instanceof Do_ ||
$node instanceof Switch_ ||
$node instanceof Ternary;
}






protected function validateClassStatement(Class_ $stmt)
{
$this->ensureCanDefine($stmt, self::CLASS_TYPE);
if (isset($stmt->extends)) {
$this->ensureClassExists($this->getFullyQualifiedName($stmt->extends), $stmt);
}
$this->ensureInterfacesExist($stmt->implements, $stmt);
}






protected function validateInterfaceStatement(Interface_ $stmt)
{
$this->ensureCanDefine($stmt, self::INTERFACE_TYPE);
$this->ensureInterfacesExist($stmt->extends, $stmt);
}






protected function validateTraitStatement(Trait_ $stmt)
{
$this->ensureCanDefine($stmt, self::TRAIT_TYPE);
}









protected function ensureCanDefine(Stmt $stmt, string $scopeType = self::CLASS_TYPE)
{

if ($stmt->name === null) {
return;
}

$name = $this->getFullyQualifiedName($stmt->name);


$errorType = null;
if ($this->classExists($name)) {
$errorType = self::CLASS_TYPE;
} elseif ($this->interfaceExists($name)) {
$errorType = self::INTERFACE_TYPE;
} elseif ($this->traitExists($name)) {
$errorType = self::TRAIT_TYPE;
}

if ($errorType !== null) {
throw $this->createError(\sprintf('%s named %s already exists', \ucfirst($errorType), $name), $stmt);
}



$this->currentScope[\strtolower($name)] = $scopeType;
}









protected function ensureClassExists(string $name, Stmt $stmt)
{
if (!$this->classExists($name)) {
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
}
}









protected function ensureClassOrInterfaceExists(string $name, Stmt $stmt)
{
if (!$this->classExists($name) && !$this->interfaceExists($name)) {
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
}
}









protected function ensureClassOrTraitExists(string $name, Stmt $stmt)
{
if (!$this->classExists($name) && !$this->traitExists($name)) {
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
}
}










protected function ensureMethodExists(string $class, string $name, Stmt $stmt)
{
$this->ensureClassOrTraitExists($class, $stmt);


if (\in_array(\strtolower($class), ['self', 'parent', 'static'])) {
return;
}


if ($this->findInScope($class) === self::CLASS_TYPE) {
return;
}


if ($name instanceof Expr) {
return;
}

if (!\method_exists($class, $name) && !\method_exists($class, '__callStatic')) {
throw $this->createError(\sprintf('Call to undefined method %s::%s()', $class, $name), $stmt);
}
}









protected function ensureInterfacesExist(array $interfaces, Stmt $stmt)
{
foreach ($interfaces as $interface) {

$name = $this->getFullyQualifiedName($interface);
if (!$this->interfaceExists($name)) {
throw $this->createError(\sprintf('Interface \'%s\' not found', $name), $stmt);
}
}
}








protected function classExists(string $name): bool
{



if (\in_array(\strtolower($name), ['self', 'static', 'parent'])) {
return true;
}

return \class_exists($name) || $this->findInScope($name) === self::CLASS_TYPE;
}






protected function interfaceExists(string $name): bool
{
return \interface_exists($name) || $this->findInScope($name) === self::INTERFACE_TYPE;
}






protected function traitExists(string $name): bool
{
return \trait_exists($name) || $this->findInScope($name) === self::TRAIT_TYPE;
}








protected function findInScope(string $name)
{
$name = \strtolower($name);
if (isset($this->currentScope[$name])) {
return $this->currentScope[$name];
}
}







protected function createError(string $msg, Stmt $stmt): FatalErrorException
{
return new FatalErrorException($msg, 0, \E_ERROR, null, $stmt->getStartLine());
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Stmt\Do_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\While_;
use Psy\Exception\FatalErrorException;







class ValidFunctionNamePass extends NamespaceAwarePass
{
private $conditionalScopes = 0;










public function enterNode(Node $node)
{
parent::enterNode($node);

if (self::isConditional($node)) {
$this->conditionalScopes++;
} elseif ($node instanceof Function_) {
$name = $this->getFullyQualifiedName($node->name);



if ($this->conditionalScopes === 0) {
if (\function_exists($name) ||
isset($this->currentScope[\strtolower($name)])) {
$msg = \sprintf('Cannot redeclare %s()', $name);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}

$this->currentScope[\strtolower($name)] = true;
}
}






public function leaveNode(Node $node)
{
if (self::isConditional($node)) {
$this->conditionalScopes--;
}
}

private static function isConditional(Node $node)
{
return $node instanceof If_ ||
$node instanceof While_ ||
$node instanceof Do_ ||
$node instanceof Switch_;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use Psy\Exception\FatalErrorException;




class FinalClassPass extends CodeCleanerPass
{
private $finalClasses;






public function beforeTraverse(array $nodes)
{
$this->finalClasses = [];
}








public function enterNode(Node $node)
{
if ($node instanceof Class_) {
if ($node->extends) {
$extends = (string) $node->extends;
if ($this->isFinalClass($extends)) {
$msg = \sprintf('Class %s may not inherit from final class (%s)', $node->name, $extends);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}

if ($node->isFinal()) {
$this->finalClasses[\strtolower($node->name)] = true;
}
}
}




private function isFinalClass(string $name): bool
{
if (!\class_exists($name)) {
return isset($this->finalClasses[\strtolower($name)]);
}

$refl = new \ReflectionClass($name);

return $refl->isFinal();
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Break_;
use PhpParser\Node\Stmt\Continue_;
use PhpParser\Node\Stmt\Do_;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\While_;
use Psy\Exception\FatalErrorException;




class LoopContextPass extends CodeCleanerPass
{
private $loopDepth;






public function beforeTraverse(array $nodes)
{
$this->loopDepth = 0;
}











public function enterNode(Node $node)
{
switch (true) {
case $node instanceof Do_:
case $node instanceof For_:
case $node instanceof Foreach_:
case $node instanceof Switch_:
case $node instanceof While_:
$this->loopDepth++;
break;

case $node instanceof Break_:
case $node instanceof Continue_:
$operator = $node instanceof Break_ ? 'break' : 'continue';

if ($this->loopDepth === 0) {
$msg = \sprintf("'%s' not in the 'loop' or 'switch' context", $operator);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}

if ($node->num instanceof LNumber || $node->num instanceof DNumber) {
$num = $node->num->value;
if ($node->num instanceof DNumber || $num < 1) {
$msg = \sprintf("'%s' operator accepts only positive numbers", $operator);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}

if ($num > $this->loopDepth) {
$msg = \sprintf("Cannot '%s' %d levels", $operator, $num);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
} elseif ($node->num) {
$msg = \sprintf("'%s' operator with non-constant operand is no longer supported", $operator);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
break;
}
}






public function leaveNode(Node $node)
{
switch (true) {
case $node instanceof Do_:
case $node instanceof For_:
case $node instanceof Foreach_:
case $node instanceof Switch_:
case $node instanceof While_:
$this->loopDepth--;
break;
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\DeclareDeclare;
use Psy\Exception\FatalErrorException;











class StrictTypesPass extends CodeCleanerPass
{
const EXCEPTION_MESSAGE = 'strict_types declaration must have 0 or 1 as its value';

private $strictTypes = false;




public function __construct(bool $strictTypes = false)
{
$this->strictTypes = $strictTypes;
}













public function beforeTraverse(array $nodes)
{
$prependStrictTypes = $this->strictTypes;

foreach ($nodes as $node) {
if ($node instanceof Declare_) {
foreach ($node->declares as $declare) {
if ($declare->key->toString() === 'strict_types') {
$value = $declare->value;
if (!$value instanceof LNumber || ($value->value !== 0 && $value->value !== 1)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}

$this->strictTypes = $value->value === 1;
}
}
}
}

if ($prependStrictTypes) {
$first = \reset($nodes);
if (!$first instanceof Declare_) {
$declare = new Declare_([new DeclareDeclare('strict_types', new LNumber(1))]);
\array_unshift($nodes, $declare);
}
}

return $nodes;
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\FunctionLike;
use Psy\Exception\FatalErrorException;

class FunctionContextPass extends CodeCleanerPass
{

private $functionDepth;






public function beforeTraverse(array $nodes)
{
$this->functionDepth = 0;
}




public function enterNode(Node $node)
{
if ($node instanceof FunctionLike) {
$this->functionDepth++;

return;
}


if ($this->functionDepth !== 0) {
return;
}


if ($node instanceof Yield_) {
$msg = 'The "yield" expression can only be used inside a function';
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}






public function leaveNode(Node $node)
{
if ($node instanceof FunctionLike) {
$this->functionDepth--;
}
}
}
<?php










namespace Psy\CodeCleaner;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseItem;
use PhpParser\Node\Stmt\UseUse;
use PhpParser\NodeTraverser;











class UseStatementPass extends CodeCleanerPass
{
private $aliases = [];
private $lastAliases = [];
private $lastNamespace = null;












public function enterNode(Node $node)
{
if ($node instanceof Namespace_) {


if (\strtolower($node->name ?: '') === \strtolower($this->lastNamespace ?: '')) {
$this->aliases = $this->lastAliases;
}
}
}











public function leaveNode(Node $node)
{

if ($node instanceof Use_) {
foreach ($node->uses as $useItem) {
$this->aliases[\strtolower($useItem->getAlias())] = $useItem->name;
}

return NodeTraverser::REMOVE_NODE;
}


if ($node instanceof GroupUse) {
foreach ($node->uses as $useItem) {
$this->aliases[\strtolower($useItem->getAlias())] = Name::concat($node->prefix, $useItem->name, [
'startLine' => $node->prefix->getAttribute('startLine'),
'endLine' => $useItem->name->getAttribute('endLine'),
]);
}

return NodeTraverser::REMOVE_NODE;
}


if ($node instanceof Namespace_) {
$this->lastNamespace = $node->name;
$this->lastAliases = $this->aliases;
$this->aliases = [];

return;
}



if ($node instanceof UseUse || $node instanceof UseItem) {
return;
}


foreach ($node as $name => $subNode) {
if ($subNode instanceof Name) {
if ($replacement = $this->findAlias($subNode)) {
$node->$name = $replacement;
}
}
}

return $node;
}








private function findAlias(Name $name)
{
$that = \strtolower($name);
foreach ($this->aliases as $alias => $prefix) {
if ($that === $alias) {
return new FullyQualifiedName($prefix->toString());
} elseif (\substr($that, 0, \strlen($alias) + 1) === $alias.'\\') {
return new FullyQualifiedName($prefix->toString().\substr($name, \strlen($alias)));
}
}
}
}
<?php










namespace Psy;

use Psy\Exception\DeprecatedException;
use Psy\Exception\RuntimeException;
use Psy\ExecutionLoop\ProcessForker;
use Psy\Output\OutputPager;
use Psy\Output\ShellOutput;
use Psy\Output\Theme;
use Psy\TabCompletion\AutoCompleter;
use Psy\VarDumper\Presenter;
use Psy\VersionUpdater\Checker;
use Psy\VersionUpdater\GitHubChecker;
use Psy\VersionUpdater\IntervalChecker;
use Psy\VersionUpdater\NoopChecker;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class Configuration
{
const COLOR_MODE_AUTO = 'auto';
const COLOR_MODE_FORCED = 'forced';
const COLOR_MODE_DISABLED = 'disabled';

const INTERACTIVE_MODE_AUTO = 'auto';
const INTERACTIVE_MODE_FORCED = 'forced';
const INTERACTIVE_MODE_DISABLED = 'disabled';

const VERBOSITY_QUIET = 'quiet';
const VERBOSITY_NORMAL = 'normal';
const VERBOSITY_VERBOSE = 'verbose';
const VERBOSITY_VERY_VERBOSE = 'very_verbose';
const VERBOSITY_DEBUG = 'debug';

private static $AVAILABLE_OPTIONS = [
'codeCleaner',
'colorMode',
'configDir',
'dataDir',
'defaultIncludes',
'eraseDuplicates',
'errorLoggingLevel',
'forceArrayIndexes',
'formatterStyles',
'historyFile',
'historySize',
'interactiveMode',
'manualDbFile',
'pager',
'prompt',
'rawOutput',
'requireSemicolons',
'runtimeDir',
'startupMessage',
'strictTypes',
'theme',
'updateCheck',
'useBracketedPaste',
'usePcntl',
'useReadline',
'useTabCompletion',
'useUnicode',
'verbosity',
'warnOnMultipleConfigs',
'yolo',
];

private $defaultIncludes;
private $configDir;
private $dataDir;
private $runtimeDir;
private $configFile;

private $historyFile;
private $historySize;
private $eraseDuplicates;
private $manualDbFile;
private $hasReadline;
private $useReadline;
private $useBracketedPaste;
private $hasPcntl;
private $usePcntl;
private $newCommands = [];
private $pipedInput;
private $pipedOutput;
private $rawOutput = false;
private $requireSemicolons = false;
private $strictTypes = false;
private $useUnicode;
private $useTabCompletion;
private $newMatchers = [];
private $errorLoggingLevel = \E_ALL;
private $warnOnMultipleConfigs = false;
private $colorMode = self::COLOR_MODE_AUTO;
private $interactiveMode = self::INTERACTIVE_MODE_AUTO;
private $updateCheck;
private $startupMessage;
private $forceArrayIndexes = false;

private $formatterStyles = [];
private $verbosity = self::VERBOSITY_NORMAL;
private $yolo = false;

private $theme;


private $readline;

private $output;
private $shell;
private $cleaner;
private $pager;
private $manualDb;
private $presenter;
private $autoCompleter;
private $checker;

private $prompt;
private $configPaths;








public function __construct(array $config = [])
{
$this->configPaths = new ConfigPaths();


if (isset($config['configFile'])) {
$this->configFile = $config['configFile'];
} elseif (isset($_SERVER['PSYSH_CONFIG']) && $_SERVER['PSYSH_CONFIG']) {
$this->configFile = $_SERVER['PSYSH_CONFIG'];
} elseif (\PHP_SAPI === 'cli-server' && ($configFile = \getenv('PSYSH_CONFIG'))) {
$this->configFile = $configFile;
}


if (isset($config['baseDir'])) {
$msg = "The 'baseDir' configuration option is deprecated; ".
"please specify 'configDir' and 'dataDir' options instead";
throw new DeprecatedException($msg);
}

unset($config['configFile'], $config['baseDir']);


$this->loadConfig($config);
$this->init();
}

















public static function fromInput(InputInterface $input): self
{
$config = new self(['configFile' => self::getConfigFileFromInput($input)]);


if (self::getOptionFromInput($input, ['color', 'ansi'])) {
$config->setColorMode(self::COLOR_MODE_FORCED);
} elseif (self::getOptionFromInput($input, ['no-color', 'no-ansi'])) {
$config->setColorMode(self::COLOR_MODE_DISABLED);
}


if ($verbosity = self::getVerbosityFromInput($input)) {
$config->setVerbosity($verbosity);
}


if (self::getOptionFromInput($input, ['interactive', 'interaction'], ['-a', '-i'])) {
$config->setInteractiveMode(self::INTERACTIVE_MODE_FORCED);
} elseif (self::getOptionFromInput($input, ['no-interactive', 'no-interaction'], ['-n'])) {
$config->setInteractiveMode(self::INTERACTIVE_MODE_DISABLED);
}


if (self::getOptionFromInput($input, ['compact'])) {
$config->setTheme('compact');
}



if (!$config->getInputInteractive()) {
if (self::getOptionFromInput($input, ['raw-output'], ['-r'])) {
$config->setRawOutput(true);
}
}


if (self::getOptionFromInput($input, ['yolo'])) {
$config->setYolo(true);
}

return $config;
}






private static function getConfigFileFromInput(InputInterface $input)
{

if ($input->hasOption('config')) {
return $input->getOption('config');
}

return $input->getParameterOption('--config', null, true) ?: $input->getParameterOption('-c', null, true);
}










private static function getOptionFromInput(InputInterface $input, array $names, array $otherParams = []): bool
{

foreach ($names as $name) {
if ($input->hasOption($name) && $input->getOption($name)) {
return true;
}
}

foreach ($names as $name) {
$otherParams[] = '--'.$name;
}

foreach ($otherParams as $name) {
if ($input->hasParameterOption($name, true)) {
return true;
}
}

return false;
}









private static function getVerbosityFromInput(InputInterface $input)
{

if (self::getOptionFromInput($input, ['quiet'], ['-q'])) {
return self::VERBOSITY_QUIET;
}









if ($input->hasOption('verbose') && $input->getOption('verbose') !== true) {
switch ($input->getOption('verbose')) {
case '-1':
return self::VERBOSITY_QUIET;
case '0': 
return self::VERBOSITY_NORMAL;
case '1':
case null: 
return self::VERBOSITY_VERBOSE;
case '2':
case 'v': 
return self::VERBOSITY_VERY_VERBOSE;
case '3':
case 'vv': 
return self::VERBOSITY_DEBUG;
default: 
return;
}
}


if ($input->hasParameterOption('--verbose=-1', true) || $input->getParameterOption('--verbose', false, true) === '-1') {
return self::VERBOSITY_QUIET;
}

if ($input->hasParameterOption('--verbose=0', true) || $input->getParameterOption('--verbose', false, true) === '0') {
return self::VERBOSITY_NORMAL;
}


if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === '3') {
return self::VERBOSITY_DEBUG;
}

if ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === '2') {
return self::VERBOSITY_VERY_VERBOSE;
}

if ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true)) {
return self::VERBOSITY_VERBOSE;
}
}








public static function getInputOptions(): array
{
return [
new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'Use an alternate PsySH config file location.'),
new InputOption('cwd', null, InputOption::VALUE_REQUIRED, 'Use an alternate working directory.'),

new InputOption('color', null, InputOption::VALUE_NONE, 'Force colors in output.'),
new InputOption('no-color', null, InputOption::VALUE_NONE, 'Disable colors in output.'),

new InputOption('ansi', null, InputOption::VALUE_NONE, 'Force colors in output.'),
new InputOption('no-ansi', null, InputOption::VALUE_NONE, 'Disable colors in output.'),

new InputOption('quiet', 'q', InputOption::VALUE_NONE, 'Shhhhhh.'),
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_OPTIONAL, 'Increase the verbosity of messages.', '0'),
new InputOption('compact', null, InputOption::VALUE_NONE, 'Run PsySH with compact output.'),
new InputOption('interactive', 'i|a', InputOption::VALUE_NONE, 'Force PsySH to run in interactive mode.'),
new InputOption('no-interactive', 'n', InputOption::VALUE_NONE, 'Run PsySH without interactive input. Requires input from stdin.'),

new InputOption('interaction', null, InputOption::VALUE_NONE, 'Force PsySH to run in interactive mode.'),
new InputOption('no-interaction', null, InputOption::VALUE_NONE, 'Run PsySH without interactive input. Requires input from stdin.'),
new InputOption('raw-output', 'r', InputOption::VALUE_NONE, 'Print var_export-style return values (for non-interactive input)'),

new InputOption('self-update', 'u', InputOption::VALUE_NONE, 'Update to the latest version'),

new InputOption('yolo', null, InputOption::VALUE_NONE, 'Run PsySH with minimal input validation. You probably don\'t want this.'),
];
}











public function init()
{

$this->hasReadline = \function_exists('readline');
$this->hasPcntl = ProcessForker::isSupported();

if ($configFile = $this->getConfigFile()) {
$this->loadConfigFile($configFile);
}

if (!$this->configFile && $localConfig = $this->getLocalConfigFile()) {
$this->loadConfigFile($localConfig);
}

$this->configPaths->overrideDirs([
'configDir' => $this->configDir,
'dataDir' => $this->dataDir,
'runtimeDir' => $this->runtimeDir,
]);
}















public function getConfigFile()
{
if (isset($this->configFile)) {
return $this->configFile;
}

$files = $this->configPaths->configFiles(['config.php', 'rc.php']);

if (!empty($files)) {
if ($this->warnOnMultipleConfigs && \count($files) > 1) {
$msg = \sprintf('Multiple configuration files found: %s. Using %s', \implode(', ', $files), $files[0]);
\trigger_error($msg, \E_USER_NOTICE);
}

return $files[0];
}
}









public function getLocalConfigFile()
{
$localConfig = \getcwd().'/.psysh.php';

if (@\is_file($localConfig)) {
return $localConfig;
}
}






public function loadConfig(array $options)
{
foreach (self::$AVAILABLE_OPTIONS as $option) {
if (isset($options[$option])) {
$method = 'set'.\ucfirst($option);
$this->$method($options[$option]);
}
}


if (isset($options['tabCompletion'])) {
$msg = '`tabCompletion` is deprecated; use `useTabCompletion` instead.';
@\trigger_error($msg, \E_USER_DEPRECATED);

$this->setUseTabCompletion($options['tabCompletion']);
}

foreach (['commands', 'matchers', 'casters'] as $option) {
if (isset($options[$option])) {
$method = 'add'.\ucfirst($option);
$this->$method($options[$option]);
}
}


if (isset($options['tabCompletionMatchers'])) {
$msg = '`tabCompletionMatchers` is deprecated; use `matchers` instead.';
@\trigger_error($msg, \E_USER_DEPRECATED);

$this->addMatchers($options['tabCompletionMatchers']);
}
}












public function loadConfigFile(string $file)
{
if (!\is_file($file)) {
throw new \InvalidArgumentException(\sprintf('Invalid configuration file specified, %s does not exist', $file));
}

$__psysh_config_file__ = $file;
$load = function ($config) use ($__psysh_config_file__) {
$result = require $__psysh_config_file__;
if ($result !== 1) {
return $result;
}
};
$result = $load($this);

if (!empty($result)) {
if (\is_array($result)) {
$this->loadConfig($result);
} else {
throw new \InvalidArgumentException('Psy Shell configuration must return an array of options');
}
}
}






public function setDefaultIncludes(array $includes = [])
{
$this->defaultIncludes = $includes;
}






public function getDefaultIncludes(): array
{
return $this->defaultIncludes ?: [];
}






public function setConfigDir(string $dir)
{
$this->configDir = (string) $dir;

$this->configPaths->overrideDirs([
'configDir' => $this->configDir,
'dataDir' => $this->dataDir,
'runtimeDir' => $this->runtimeDir,
]);
}






public function getConfigDir()
{
return $this->configDir;
}






public function setDataDir(string $dir)
{
$this->dataDir = (string) $dir;

$this->configPaths->overrideDirs([
'configDir' => $this->configDir,
'dataDir' => $this->dataDir,
'runtimeDir' => $this->runtimeDir,
]);
}






public function getDataDir()
{
return $this->dataDir;
}






public function setRuntimeDir(string $dir)
{
$this->runtimeDir = (string) $dir;

$this->configPaths->overrideDirs([
'configDir' => $this->configDir,
'dataDir' => $this->dataDir,
'runtimeDir' => $this->runtimeDir,
]);
}









public function getRuntimeDir(): string
{
$runtimeDir = $this->configPaths->runtimeDir();

if (!\is_dir($runtimeDir)) {
if (!@\mkdir($runtimeDir, 0700, true)) {
throw new RuntimeException(\sprintf('Unable to create PsySH runtime directory. Make sure PHP is able to write to %s in order to continue.', \dirname($runtimeDir)));
}
}

return $runtimeDir;
}






public function setHistoryFile(string $file)
{
$this->historyFile = ConfigPaths::touchFileWithMkdir($file);
}







public function getHistoryFile(): string
{
if (isset($this->historyFile)) {
return $this->historyFile;
}

$files = $this->configPaths->configFiles(['psysh_history', 'history']);

if (!empty($files)) {
if ($this->warnOnMultipleConfigs && \count($files) > 1) {
$msg = \sprintf('Multiple history files found: %s. Using %s', \implode(', ', $files), $files[0]);
\trigger_error($msg, \E_USER_NOTICE);
}

$this->setHistoryFile($files[0]);
} else {

$this->setHistoryFile($this->configPaths->currentConfigDir().'/psysh_history');
}

return $this->historyFile;
}






public function setHistorySize(int $value)
{
$this->historySize = (int) $value;
}






public function getHistorySize()
{
return $this->historySize;
}






public function setEraseDuplicates(bool $value)
{
$this->eraseDuplicates = (bool) $value;
}






public function getEraseDuplicates()
{
return $this->eraseDuplicates;
}













public function getTempFile(string $type, int $pid): string
{
return \tempnam($this->getRuntimeDir(), $type.'_'.$pid.'_');
}











public function getPipe(string $type, int $pid): string
{
return \sprintf('%s/%s_%s', $this->getRuntimeDir(), $type, $pid);
}






public function hasReadline(): bool
{
return $this->hasReadline;
}






public function setUseReadline(bool $useReadline)
{
$this->useReadline = (bool) $useReadline;
}









public function useReadline(): bool
{
return isset($this->useReadline) ? ($this->hasReadline && $this->useReadline) : $this->hasReadline;
}






public function setReadline(Readline\Readline $readline)
{
$this->readline = $readline;
}












public function getReadline(): Readline\Readline
{
if (!isset($this->readline)) {
$className = $this->getReadlineClass();
$this->readline = new $className(
$this->getHistoryFile(),
$this->getHistorySize(),
$this->getEraseDuplicates()
);
}

return $this->readline;
}






private function getReadlineClass(): string
{
if ($this->useReadline()) {
if (Readline\GNUReadline::isSupported()) {
return Readline\GNUReadline::class;
} elseif (Readline\Libedit::isSupported()) {
return Readline\Libedit::class;
}
}

if (Readline\Userland::isSupported()) {
return Readline\Userland::class;
}

return Readline\Transient::class;
}








public function setUseBracketedPaste(bool $useBracketedPaste)
{
$this->useBracketedPaste = (bool) $useBracketedPaste;
}
















public function useBracketedPaste(): bool
{
$readlineClass = $this->getReadlineClass();

return $this->useBracketedPaste && $readlineClass::supportsBracketedPaste();



}






public function hasPcntl(): bool
{
return $this->hasPcntl;
}






public function setUsePcntl(bool $usePcntl)
{
$this->usePcntl = (bool) $usePcntl;
}









public function usePcntl(): bool
{
if (!isset($this->usePcntl)) {


if (\function_exists('xdebug_is_debugger_active') && \xdebug_is_debugger_active()) {
return false;
}

return $this->hasPcntl;
}

return $this->hasPcntl && $this->usePcntl;
}









public function rawOutput(): bool
{
return $this->rawOutput;
}






public function setRawOutput(bool $rawOutput)
{
$this->rawOutput = (bool) $rawOutput;
}








public function setRequireSemicolons(bool $requireSemicolons)
{
$this->requireSemicolons = (bool) $requireSemicolons;
}








public function requireSemicolons(): bool
{
return $this->requireSemicolons;
}




public function setStrictTypes($strictTypes)
{
$this->strictTypes = (bool) $strictTypes;
}




public function strictTypes(): bool
{
return $this->strictTypes;
}









public function setUseUnicode(bool $useUnicode)
{
$this->useUnicode = (bool) $useUnicode;
}







public function useUnicode(): bool
{
if (isset($this->useUnicode)) {
return $this->useUnicode;
}


return true;
}








public function setErrorLoggingLevel($errorLoggingLevel)
{
$this->errorLoggingLevel = (\E_ALL | \E_STRICT) & $errorLoggingLevel;
}













public function errorLoggingLevel(): int
{
return $this->errorLoggingLevel;
}






public function setCodeCleaner(CodeCleaner $cleaner)
{
$this->cleaner = $cleaner;
}






public function getCodeCleaner(): CodeCleaner
{
if (!isset($this->cleaner)) {
$this->cleaner = new CodeCleaner(null, null, null, $this->yolo(), $this->strictTypes());
}

return $this->cleaner;
}






public function setYolo($yolo)
{
$this->yolo = (bool) $yolo;
}




public function yolo(): bool
{
return $this->yolo;
}






public function setUseTabCompletion(bool $useTabCompletion)
{
$this->useTabCompletion = (bool) $useTabCompletion;
}






public function setTabCompletion(bool $useTabCompletion)
{
@\trigger_error('`setTabCompletion` is deprecated; call `setUseTabCompletion` instead.', \E_USER_DEPRECATED);

$this->setUseTabCompletion($useTabCompletion);
}









public function useTabCompletion(): bool
{
return isset($this->useTabCompletion) ? ($this->hasReadline && $this->useTabCompletion) : $this->hasReadline;
}




public function getTabCompletion(): bool
{
@\trigger_error('`getTabCompletion` is deprecated; call `useTabCompletion` instead.', \E_USER_DEPRECATED);

return $this->useTabCompletion();
}






public function setOutput(ShellOutput $output)
{
$this->output = $output;
$this->pipedOutput = null; 

if (isset($this->theme)) {
$output->setTheme($this->theme);
}

$this->applyFormatterStyles();
}










public function getOutput(): ShellOutput
{
if (!isset($this->output)) {
$this->setOutput(new ShellOutput(
$this->getOutputVerbosity(),
null,
null,
$this->getPager() ?: null,
$this->theme()
));




$decorated = $this->getOutputDecorated();
if ($decorated !== null) {
$this->output->setDecorated($decorated);
}
}

return $this->output;
}






public function getOutputDecorated()
{
switch ($this->colorMode()) {
case self::COLOR_MODE_FORCED:
return true;
case self::COLOR_MODE_DISABLED:
return false;
case self::COLOR_MODE_AUTO:
default:
return $this->outputIsPiped() ? false : null;
}
}




public function getInputInteractive(): bool
{
switch ($this->interactiveMode()) {
case self::INTERACTIVE_MODE_FORCED:
return true;
case self::INTERACTIVE_MODE_DISABLED:
return false;
case self::INTERACTIVE_MODE_AUTO:
default:
return !$this->inputIsPiped();
}
}













public function setPager($pager)
{
if ($pager === null || $pager === false || $pager === 'cat') {
$pager = false;
}

if ($pager !== false && !\is_string($pager) && !$pager instanceof OutputPager) {
throw new \InvalidArgumentException('Unexpected pager instance');
}

$this->pager = $pager;
}









public function getPager()
{
if (!isset($this->pager) && $this->usePcntl()) {
if (\getenv('TERM') === 'dumb') {
return false;
}

if ($pager = \ini_get('cli.pager')) {

$this->pager = $pager;
} elseif ($less = $this->configPaths->which('less')) {

$this->pager = $less.' -R -F -X';
}
}

return $this->pager;
}






public function setAutoCompleter(AutoCompleter $autoCompleter)
{
$this->autoCompleter = $autoCompleter;
}




public function getAutoCompleter(): AutoCompleter
{
if (!isset($this->autoCompleter)) {
$this->autoCompleter = new AutoCompleter();
}

return $this->autoCompleter;
}




public function getTabCompletionMatchers(): array
{
@\trigger_error('`getTabCompletionMatchers` is no longer used.', \E_USER_DEPRECATED);

return [];
}











public function addMatchers(array $matchers)
{
$this->newMatchers = \array_merge($this->newMatchers, $matchers);
if (isset($this->shell)) {
$this->doAddMatchers();
}
}





private function doAddMatchers()
{
if (!empty($this->newMatchers)) {
$this->shell->addMatchers($this->newMatchers);
$this->newMatchers = [];
}
}






public function addTabCompletionMatchers(array $matchers)
{
@\trigger_error('`addTabCompletionMatchers` is deprecated; call `addMatchers` instead.', \E_USER_DEPRECATED);

$this->addMatchers($matchers);
}











public function addCommands(array $commands)
{
$this->newCommands = \array_merge($this->newCommands, $commands);
if (isset($this->shell)) {
$this->doAddCommands();
}
}





private function doAddCommands()
{
if (!empty($this->newCommands)) {
$this->shell->addCommands($this->newCommands);
$this->newCommands = [];
}
}






public function setShell(Shell $shell)
{
$this->shell = $shell;
$this->doAddCommands();
$this->doAddMatchers();
}









public function setManualDbFile(string $filename)
{
$this->manualDbFile = (string) $filename;
}






public function getManualDbFile()
{
if (isset($this->manualDbFile)) {
return $this->manualDbFile;
}

$files = $this->configPaths->dataFiles(['php_manual.sqlite']);
if (!empty($files)) {
if ($this->warnOnMultipleConfigs && \count($files) > 1) {
$msg = \sprintf('Multiple manual database files found: %s. Using %s', \implode(', ', $files), $files[0]);
\trigger_error($msg, \E_USER_NOTICE);
}

return $this->manualDbFile = $files[0];
}
}






public function getManualDb()
{
if (!isset($this->manualDb)) {
$dbFile = $this->getManualDbFile();
if ($dbFile !== null && \is_file($dbFile)) {
try {
$this->manualDb = new \PDO('sqlite:'.$dbFile);
} catch (\PDOException $e) {
if ($e->getMessage() === 'could not find driver') {
throw new RuntimeException('SQLite PDO driver not found', 0, $e);
} else {
throw $e;
}
}
}
}

return $this->manualDb;
}






public function addCasters(array $casters)
{
$this->getPresenter()->addCasters($casters);
}




public function getPresenter(): Presenter
{
if (!isset($this->presenter)) {
$this->presenter = new Presenter($this->getOutput()->getFormatter(), $this->forceArrayIndexes());
}

return $this->presenter;
}








public function setWarnOnMultipleConfigs(bool $warnOnMultipleConfigs)
{
$this->warnOnMultipleConfigs = (bool) $warnOnMultipleConfigs;
}











public function warnOnMultipleConfigs(): bool
{
return $this->warnOnMultipleConfigs;
}








public function setColorMode(string $colorMode)
{
$validColorModes = [
self::COLOR_MODE_AUTO,
self::COLOR_MODE_FORCED,
self::COLOR_MODE_DISABLED,
];

if (!\in_array($colorMode, $validColorModes)) {
throw new \InvalidArgumentException('Invalid color mode: '.$colorMode);
}

$this->colorMode = $colorMode;
}




public function colorMode(): string
{
return $this->colorMode;
}








public function setInteractiveMode(string $interactiveMode)
{
$validInteractiveModes = [
self::INTERACTIVE_MODE_AUTO,
self::INTERACTIVE_MODE_FORCED,
self::INTERACTIVE_MODE_DISABLED,
];

if (!\in_array($interactiveMode, $validInteractiveModes)) {
throw new \InvalidArgumentException('Invalid interactive mode: '.$interactiveMode);
}

$this->interactiveMode = $interactiveMode;
}




public function interactiveMode(): string
{
return $this->interactiveMode;
}






public function setChecker(Checker $checker)
{
$this->checker = $checker;
}






public function getChecker(): Checker
{
if (!isset($this->checker)) {
$interval = $this->getUpdateCheck();
switch ($interval) {
case Checker::ALWAYS:
$this->checker = new GitHubChecker();
break;

case Checker::DAILY:
case Checker::WEEKLY:
case Checker::MONTHLY:
$checkFile = $this->getUpdateCheckCacheFile();
if ($checkFile === false) {
$this->checker = new NoopChecker();
} else {
$this->checker = new IntervalChecker($checkFile, $interval);
}
break;

case Checker::NEVER:
$this->checker = new NoopChecker();
break;
}
}

return $this->checker;
}







public function getUpdateCheck(): string
{
return isset($this->updateCheck) ? $this->updateCheck : Checker::WEEKLY;
}








public function setUpdateCheck(string $interval)
{
$validIntervals = [
Checker::ALWAYS,
Checker::DAILY,
Checker::WEEKLY,
Checker::MONTHLY,
Checker::NEVER,
];

if (!\in_array($interval, $validIntervals)) {
throw new \InvalidArgumentException('Invalid update check interval: '.$interval);
}

$this->updateCheck = $interval;
}






public function getUpdateCheckCacheFile()
{
return ConfigPaths::touchFileWithMkdir($this->configPaths->currentConfigDir().'/update_check.json');
}






public function setStartupMessage(string $message)
{
$this->startupMessage = $message;
}






public function getStartupMessage()
{
return $this->startupMessage;
}










public function setPrompt(string $prompt)
{
$this->prompt = $prompt;

if (isset($this->theme)) {
$this->theme->setPrompt($prompt);
}
}






public function getPrompt()
{
return $this->prompt;
}




public function forceArrayIndexes(): bool
{
return $this->forceArrayIndexes;
}






public function setForceArrayIndexes(bool $forceArrayIndexes)
{
$this->forceArrayIndexes = $forceArrayIndexes;
}






public function setTheme($theme)
{
if (!$theme instanceof Theme) {
$theme = new Theme($theme);
}

$this->theme = $theme;

if (isset($this->prompt)) {
$this->theme->setPrompt($this->prompt);
}

if (isset($this->output)) {
$this->output->setTheme($theme);
$this->applyFormatterStyles();
}
}




public function theme(): Theme
{
if (!isset($this->theme)) {

$this->theme = $this->prompt ? new Theme('classic') : new Theme();
}

if (isset($this->prompt)) {
$this->theme->setPrompt($this->prompt);
}

return $this->theme;
}

















public function setFormatterStyles(array $formatterStyles)
{
foreach ($formatterStyles as $name => $style) {
$this->formatterStyles[$name] = new OutputFormatterStyle(...$style);
}

if (isset($this->output)) {
$this->applyFormatterStyles();
}
}











private function applyFormatterStyles()
{
$formatter = $this->output->getFormatter();
foreach ($this->formatterStyles as $name => $style) {
$formatter->setStyle($name, $style);
}

$errorFormatter = $this->output->getErrorOutput()->getFormatter();
foreach (Theme::ERROR_STYLES as $name) {
if (isset($this->formatterStyles[$name])) {
$errorFormatter->setStyle($name, $this->formatterStyles[$name]);
}
}
}




public function verbosity(): string
{
return $this->verbosity;
}










public function setVerbosity(string $verbosity)
{
$validVerbosityLevels = [
self::VERBOSITY_QUIET,
self::VERBOSITY_NORMAL,
self::VERBOSITY_VERBOSE,
self::VERBOSITY_VERY_VERBOSE,
self::VERBOSITY_DEBUG,
];

if (!\in_array($verbosity, $validVerbosityLevels)) {
throw new \InvalidArgumentException('Invalid verbosity level: '.$verbosity);
}

$this->verbosity = $verbosity;

if (isset($this->output)) {
$this->output->setVerbosity($this->getOutputVerbosity());
}
}






public function getOutputVerbosity(): int
{
switch ($this->verbosity()) {
case self::VERBOSITY_QUIET:
return OutputInterface::VERBOSITY_QUIET;
case self::VERBOSITY_VERBOSE:
return OutputInterface::VERBOSITY_VERBOSE;
case self::VERBOSITY_VERY_VERBOSE:
return OutputInterface::VERBOSITY_VERY_VERBOSE;
case self::VERBOSITY_DEBUG:
return OutputInterface::VERBOSITY_DEBUG;
case self::VERBOSITY_NORMAL:
default:
return OutputInterface::VERBOSITY_NORMAL;
}
}






public function inputIsPiped(): bool
{
if ($this->pipedInput === null) {
$this->pipedInput = \defined('STDIN') && self::looksLikeAPipe(\STDIN);
}

return $this->pipedInput;
}






public function outputIsPiped(): bool
{
if ($this->pipedOutput === null) {
$this->pipedOutput = self::looksLikeAPipe($this->getOutput()->getStream());
}

return $this->pipedOutput;
}






private static function looksLikeAPipe($stream): bool
{
if (\function_exists('posix_isatty')) {
return !\posix_isatty($stream);
}

$stat = \fstat($stream);
$mode = $stat['mode'] & 0170000;

return $mode === 0010000 || $mode === 0040000 || $mode === 0100000 || $mode === 0120000;
}
}
<?php










namespace Psy\Command;

use PhpParser\Parser;
use Psy\Exception\ParseErrorException;
use Psy\ParserFactory;




class CodeArgumentParser
{
private $parser;

public function __construct(Parser $parser = null)
{
$this->parser = $parser ?? (new ParserFactory())->createParser();
}










public function parse(string $code): array
{
$code = '<?php '.$code;

try {
return $this->parser->parse($code);
} catch (\PhpParser\Error $e) {
if (\strpos($e->getMessage(), 'unexpected EOF') === false) {
throw ParseErrorException::fromParseError($e);
}


try {
return $this->parser->parse($code.';');
} catch (\PhpParser\Error $_e) {

throw ParseErrorException::fromParseError($e);
}
}
}
}
<?php










namespace Psy\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;






class ClearCommand extends Command
{



protected function configure()
{
$this
->setName('clear')
->setDefinition([])
->setDescription('Clear the Psy Shell screen.')
->setHelp(
<<<'HELP'
Clear the Psy Shell screen.

Pro Tip: If your PHP has readline support, you should be able to use ctrl+l too!
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->write(\sprintf('%c[2J%c[0;0f', 27, 27));

return 0;
}
}
<?php










namespace Psy\Command;

use Psy\Context;
use Psy\ContextAware;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class EditCommand extends Command implements ContextAware
{



private $runtimeDir = '';




private $context;









public function __construct($runtimeDir, $name = null)
{
parent::__construct($name);

$this->runtimeDir = $runtimeDir;
}

protected function configure()
{
$this
->setName('edit')
->setDefinition([
new InputArgument('file', InputArgument::OPTIONAL, 'The file to open for editing. If this is not given, edits a temporary file.', null),
new InputOption(
'exec',
'e',
InputOption::VALUE_NONE,
'Execute the file content after editing. This is the default when a file name argument is not given.',
null
),
new InputOption(
'no-exec',
'E',
InputOption::VALUE_NONE,
'Do not execute the file content after editing. This is the default when a file name argument is given.',
null
),
])
->setDescription('Open an external editor. Afterwards, get produced code in input buffer.')
->setHelp('Set the EDITOR environment variable to something you\'d like to use.');
}










protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($input->getOption('exec') &&
$input->getOption('no-exec')) {
throw new \InvalidArgumentException('The --exec and --no-exec flags are mutually exclusive');
}

$filePath = $this->extractFilePath($input->getArgument('file'));

$execute = $this->shouldExecuteFile(
$input->getOption('exec'),
$input->getOption('no-exec'),
$filePath
);

$shouldRemoveFile = false;

if ($filePath === null) {
$filePath = \tempnam($this->runtimeDir, 'psysh-edit-command');
$shouldRemoveFile = true;
}

$editedContent = $this->editFile($filePath, $shouldRemoveFile);

if ($execute) {
$this->getApplication()->addInput($editedContent);
}

return 0;
}






private function shouldExecuteFile(bool $execOption, bool $noExecOption, string $filePath = null): bool
{
if ($execOption) {
return true;
}

if ($noExecOption) {
return false;
}


return $filePath === null;
}








private function extractFilePath(string $fileArgument = null)
{

if ($fileArgument !== null &&
$fileArgument !== '' &&
$fileArgument[0] === '$') {
$fileArgument = $this->context->get(\preg_replace('/^\$/', '', $fileArgument));
}

return $fileArgument;
}







private function editFile(string $filePath, bool $shouldRemoveFile): string
{
$escapedFilePath = \escapeshellarg($filePath);
$editor = (isset($_SERVER['EDITOR']) && $_SERVER['EDITOR']) ? $_SERVER['EDITOR'] : 'nano';

$pipes = [];
$proc = \proc_open("{$editor} {$escapedFilePath}", [\STDIN, \STDOUT, \STDERR], $pipes);
\proc_close($proc);

$editedContent = @\file_get_contents($filePath);

if ($shouldRemoveFile) {
@\unlink($filePath);
}

if ($editedContent === false) {
throw new \UnexpectedValueException("Reading {$filePath} returned false");
}

return $editedContent;
}






public function setContext(Context $context)
{
$this->context = $context;
}
}
<?php










namespace Psy\Command;

use Psy\Formatter\TraceFormatter;
use Psy\Input\FilterOptions;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class TraceCommand extends Command
{
protected $filter;




public function __construct($name = null)
{
$this->filter = new FilterOptions();

parent::__construct($name);
}




protected function configure()
{
list($grep, $insensitive, $invert) = FilterOptions::getOptions();

$this
->setName('trace')
->setDefinition([
new InputOption('include-psy', 'p', InputOption::VALUE_NONE, 'Include Psy in the call stack.'),
new InputOption('num', 'n', InputOption::VALUE_REQUIRED, 'Only include NUM lines.'),

$grep,
$insensitive,
$invert,
])
->setDescription('Show the current call stack.')
->setHelp(
<<<'HELP'
Show the current call stack.

Optionally, include PsySH in the call stack by passing the <info>--include-psy</info> option.

e.g.
<return>> trace -n10</return>
<return>> trace --include-psy</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->filter->bind($input);
$trace = $this->getBacktrace(new \Exception(), $input->getOption('num'), $input->getOption('include-psy'));
$output->page($trace, ShellOutput::NUMBER_LINES);

return 0;
}













protected function getBacktrace(\Throwable $e, int $count = null, bool $includePsy = true): array
{
return TraceFormatter::formatTrace($e, $this->filter, $count, $includePsy);
}
}
<?php










namespace Psy\Command;

use Psy\Exception\RuntimeException;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






class BufferCommand extends Command
{



protected function configure()
{
$this
->setName('buffer')
->setAliases(['buf'])
->setDefinition([
new InputOption('clear', '', InputOption::VALUE_NONE, 'Clear the current buffer.'),
])
->setDescription('Show (or clear) the contents of the code input buffer.')
->setHelp(
<<<'HELP'
Show the contents of the code buffer for the current multi-line expression.

Optionally, clear the buffer by passing the <info>--clear</info> option.
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$app = $this->getApplication();
if (!$app instanceof \Psy\Shell) {
throw new RuntimeException('Buffer command requires a \Psy\Shell application');
}

$buf = $app->getCodeBuffer();
if ($input->getOption('clear')) {
$app->resetCodeBuffer();
$output->writeln($this->formatLines($buf, 'urgent'), ShellOutput::NUMBER_LINES);
} else {
$output->writeln($this->formatLines($buf), ShellOutput::NUMBER_LINES);
}

return 0;
}









protected function formatLines(array $lines, string $type = 'return'): array
{
$template = \sprintf('<%s>%%s</%s>', $type, $type);

return \array_map(function ($line) use ($template) {
return \sprintf($template, $line);
}, $lines);
}
}
<?php










namespace Psy\Command;

use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Input\CodeArgument;
use Psy\Readline\Readline;
use Psy\Sudo\SudoVisitor;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;




class SudoCommand extends Command
{
private $readline;
private $parser;
private $traverser;
private $printer;




public function __construct($name = null)
{
$this->parser = new CodeArgumentParser();

$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new SudoVisitor());

$this->printer = new Printer();

parent::__construct($name);
}






public function setReadline(Readline $readline)
{
$this->readline = $readline;
}




protected function configure()
{
$this
->setName('sudo')
->setDefinition([
new CodeArgument('code', CodeArgument::REQUIRED, 'Code to execute.'),
])
->setDescription('Evaluate PHP code, bypassing visibility restrictions.')
->setHelp(
<<<'HELP'
Evaluate PHP code, bypassing visibility restrictions.

e.g.
<return>>>> $sekret->whisper("hi")</return>
<return>PHP error:  Call to private method Sekret::whisper() from context '' on line 1</return>

<return>>>> sudo $sekret->whisper("hi")</return>
<return>=> "hi"</return>

<return>>>> $sekret->word</return>
<return>PHP error:  Cannot access private property Sekret::$word on line 1</return>

<return>>>> sudo $sekret->word</return>
<return>=> "hi"</return>

<return>>>> $sekret->word = "please"</return>
<return>PHP error:  Cannot access private property Sekret::$word on line 1</return>

<return>>>> sudo $sekret->word = "please"</return>
<return>=> "please"</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');


if ($code === '!!') {
$history = $this->readline->listHistory();
if (\count($history) < 2) {
throw new \InvalidArgumentException('No previous command to replay');
}
$code = $history[\count($history) - 2];
}

$nodes = $this->traverser->traverse($this->parser->parse($code));

$sudoCode = $this->printer->prettyPrint($nodes);
$shell = $this->getApplication();
$shell->addCode($sudoCode, !$shell->hasCode());

return 0;
}
}
<?php










namespace Psy\Command;

use Psy\Context;
use Psy\ContextAware;
use Psy\Input\FilterOptions;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class WtfCommand extends TraceCommand implements ContextAware
{





protected $context;






public function setContext(Context $context)
{
$this->context = $context;
}




protected function configure()
{
list($grep, $insensitive, $invert) = FilterOptions::getOptions();

$this
->setName('wtf')
->setAliases(['last-exception', 'wtf?'])
->setDefinition([
new InputArgument('incredulity', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Number of lines to show.'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show entire backtrace.'),

$grep,
$insensitive,
$invert,
])
->setDescription('Show the backtrace of the most recent exception.')
->setHelp(
<<<'HELP'
Shows a few lines of the backtrace of the most recent exception.

If you want to see more lines, add more question marks or exclamation marks:

e.g.
<return>>>> wtf ?</return>
<return>>>> wtf ?!???!?!?</return>

To see the entire backtrace, pass the -a/--all flag:

e.g.
<return>>>> wtf -a</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->filter->bind($input);

$incredulity = \implode('', $input->getArgument('incredulity'));
if (\strlen(\preg_replace('/[\\?!]/', '', $incredulity))) {
throw new \InvalidArgumentException('Incredulity must include only "?" and "!"');
}

$exception = $this->context->getLastException();
$count = $input->getOption('all') ? \PHP_INT_MAX : \max(3, \pow(2, \strlen($incredulity) + 1));

$shell = $this->getApplication();

if ($output instanceof ShellOutput) {
$output->startPaging();
}

do {
$traceCount = \count($exception->getTrace());
$showLines = $count;

if ($traceCount < \max($count * 1.2, $count + 2)) {
$showLines = \PHP_INT_MAX;
}

$trace = $this->getBacktrace($exception, $showLines);
$moreLines = $traceCount - \count($trace);

$output->writeln($shell->formatException($exception));
$output->writeln('--');
$output->write($trace, true, ShellOutput::NUMBER_LINES);
$output->writeln('');

if ($moreLines > 0) {
$output->writeln(\sprintf(
'<aside>Use <return>wtf -a</return> to see %d more lines</aside>',
$moreLines
));
$output->writeln('');
}
} while ($exception = $exception->getPrevious());

if ($output instanceof ShellOutput) {
$output->stopPaging();
}

return 0;
}
}
<?php










namespace Psy\Command;

use Psy\Formatter\DocblockFormatter;
use Psy\Formatter\SignatureFormatter;
use Psy\Input\CodeArgument;
use Psy\Output\ShellOutput;
use Psy\Reflection\ReflectionConstant;
use Psy\Reflection\ReflectionLanguageConstruct;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class DocCommand extends ReflectingCommand
{
const INHERIT_DOC_TAG = '{@inheritdoc}';




protected function configure()
{
$this
->setName('doc')
->setAliases(['rtfm', 'man'])
->setDefinition([
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show documentation for superclasses as well as the current class.'),
new CodeArgument('target', CodeArgument::REQUIRED, 'Function, class, instance, constant, method or property to document.'),
])
->setDescription('Read the documentation for an object, class, constant, method or property.')
->setHelp(
<<<HELP
Read the documentation for an object, class, constant, method or property.

It's awesome for well-documented code, not quite as awesome for poorly documented code.

e.g.
<return>>>> doc preg_replace</return>
<return>>>> doc Psy\Shell</return>
<return>>>> doc Psy\Shell::debug</return>
<return>>>> \$s = new Psy\Shell</return>
<return>>>> doc \$s->run</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$value = $input->getArgument('target');
if (ReflectionLanguageConstruct::isLanguageConstruct($value)) {
$reflector = new ReflectionLanguageConstruct($value);
$doc = $this->getManualDocById($value);
} else {
list($target, $reflector) = $this->getTargetAndReflector($value);
$doc = $this->getManualDoc($reflector) ?: DocblockFormatter::format($reflector);
}

$db = $this->getApplication()->getManualDb();

if ($output instanceof ShellOutput) {
$output->startPaging();
}


if ($reflector instanceof \ReflectionMethod || $reflector instanceof \ReflectionProperty) {
$output->writeln(SignatureFormatter::format($reflector->getDeclaringClass()));
}

$output->writeln(SignatureFormatter::format($reflector));
$output->writeln('');

if (empty($doc) && !$db) {
$output->writeln('<warning>PHP manual not found</warning>');
$output->writeln('    To document core PHP functionality, download the PHP reference manual:');
$output->writeln('    https://github.com/bobthecow/psysh/wiki/PHP-manual');
} else {
$output->writeln($doc);
}


if ($input->getOption('all') || \stripos($doc, self::INHERIT_DOC_TAG) !== false) {
$parent = $reflector;
foreach ($this->getParentReflectors($reflector) as $parent) {
$output->writeln('');
$output->writeln('---');
$output->writeln('');


if ($parent instanceof \ReflectionMethod || $parent instanceof \ReflectionProperty) {
$output->writeln(SignatureFormatter::format($parent->getDeclaringClass()));
}

$output->writeln(SignatureFormatter::format($parent));
$output->writeln('');

if ($doc = $this->getManualDoc($parent) ?: DocblockFormatter::format($parent)) {
$output->writeln($doc);
}
}
}

if ($output instanceof ShellOutput) {
$output->stopPaging();
}


$this->setCommandScopeVariables($reflector);

return 0;
}

private function getManualDoc($reflector)
{
switch (\get_class($reflector)) {
case \ReflectionClass::class:
case \ReflectionObject::class:
case \ReflectionFunction::class:
$id = $reflector->name;
break;

case \ReflectionMethod::class:
$id = $reflector->class.'::'.$reflector->name;
break;

case \ReflectionProperty::class:
$id = $reflector->class.'::$'.$reflector->name;
break;

case \ReflectionClassConstant::class:



$id = $reflector->class.'::'.$reflector->name;
break;

case ReflectionConstant::class:
$id = $reflector->name;
break;

default:
return false;
}

return $this->getManualDocById($id);
}











private function getParentReflectors($reflector): \Generator
{
$seenClasses = [];

switch (\get_class($reflector)) {
case \ReflectionClass::class:
case \ReflectionObject::class:
foreach ($reflector->getTraits() as $trait) {
if (!\in_array($trait->getName(), $seenClasses)) {
$seenClasses[] = $trait->getName();
yield $trait;
}
}

foreach ($reflector->getInterfaces() as $interface) {
if (!\in_array($interface->getName(), $seenClasses)) {
$seenClasses[] = $interface->getName();
yield $interface;
}
}

while ($reflector = $reflector->getParentClass()) {
yield $reflector;

foreach ($reflector->getTraits() as $trait) {
if (!\in_array($trait->getName(), $seenClasses)) {
$seenClasses[] = $trait->getName();
yield $trait;
}
}

foreach ($reflector->getInterfaces() as $interface) {
if (!\in_array($interface->getName(), $seenClasses)) {
$seenClasses[] = $interface->getName();
yield $interface;
}
}
}

return;

case \ReflectionMethod::class:
foreach ($this->getParentReflectors($reflector->getDeclaringClass()) as $parent) {
if ($parent->hasMethod($reflector->getName())) {
$parentMethod = $parent->getMethod($reflector->getName());
if (!\in_array($parentMethod->getDeclaringClass()->getName(), $seenClasses)) {
$seenClasses[] = $parentMethod->getDeclaringClass()->getName();
yield $parentMethod;
}
}
}

return;

case \ReflectionProperty::class:
foreach ($this->getParentReflectors($reflector->getDeclaringClass()) as $parent) {
if ($parent->hasProperty($reflector->getName())) {
$parentProperty = $parent->getProperty($reflector->getName());
if (!\in_array($parentProperty->getDeclaringClass()->getName(), $seenClasses)) {
$seenClasses[] = $parentProperty->getDeclaringClass()->getName();
yield $parentProperty;
}
}
}
break;
}
}

private function getManualDocById($id)
{
if ($db = $this->getApplication()->getManualDb()) {
$result = $db->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)));
if ($result !== false) {
return $result->fetchColumn(0);
}
}
}
}
<?php










namespace Psy\Command\ListCommand;

use Psy\Context;
use Psy\VarDumper\Presenter;
use Symfony\Component\Console\Input\InputInterface;




class VariableEnumerator extends Enumerator
{

private static $specialNames = [
'_', '_e', '__out', '__function', '__method', '__class', '__namespace', '__file', '__line', '__dir',
];

private $context;










public function __construct(Presenter $presenter, Context $context)
{
$this->context = $context;
parent::__construct($presenter);
}




protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if ($reflector !== null || $target !== null) {
return [];
}


if (!$input->getOption('vars')) {
return [];
}

$showAll = $input->getOption('all');
$variables = $this->prepareVariables($this->getVariables($showAll));

if (empty($variables)) {
return [];
}

return [
'Variables' => $variables,
];
}








protected function getVariables(bool $showAll): array
{
$scopeVars = $this->context->getAll();
\uksort($scopeVars, function ($a, $b) {
$aIndex = \array_search($a, self::$specialNames);
$bIndex = \array_search($b, self::$specialNames);

if ($aIndex !== false) {
if ($bIndex !== false) {
return $aIndex - $bIndex;
}

return 1;
}

if ($bIndex !== false) {
return -1;
}

return \strnatcasecmp($a, $b);
});

$ret = [];
foreach ($scopeVars as $name => $val) {
if (!$showAll && \in_array($name, self::$specialNames)) {
continue;
}

$ret[$name] = $val;
}

return $ret;
}








protected function prepareVariables(array $variables): array
{

$ret = [];
foreach ($variables as $name => $val) {
if ($this->showItem($name)) {
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => \in_array($name, self::$specialNames) ? self::IS_PRIVATE : self::IS_PUBLIC,
'value' => $this->presentRef($val),
];
}
}

return $ret;
}
}
<?php










namespace Psy\Command\ListCommand;

use Symfony\Component\Console\Input\InputInterface;




class MethodEnumerator extends Enumerator
{



protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if ($reflector === null) {
return [];
}


if (!$reflector instanceof \ReflectionClass) {
return [];
}


if (!$input->getOption('methods')) {
return [];
}

$showAll = $input->getOption('all');
$noInherit = $input->getOption('no-inherit');
$methods = $this->prepareMethods($this->getMethods($showAll, $reflector, $noInherit));

if (empty($methods)) {
return [];
}

$ret = [];
$ret[$this->getKindLabel($reflector)] = $methods;

return $ret;
}










protected function getMethods(bool $showAll, \ReflectionClass $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();

$methods = [];
foreach ($reflector->getMethods() as $name => $method) {


if (($noInherit || $method->isPrivate()) && $method->getDeclaringClass()->getName() !== $className) {
continue;
}

if ($showAll || $method->isPublic()) {
$methods[$method->getName()] = $method;
}
}

\ksort($methods, \SORT_NATURAL | \SORT_FLAG_CASE);

return $methods;
}








protected function prepareMethods(array $methods): array
{

$ret = [];

foreach ($methods as $name => $method) {
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => $this->getVisibilityStyle($method),
'value' => $this->presentSignature($method),
];
}
}

return $ret;
}






protected function getKindLabel(\ReflectionClass $reflector): string
{
if ($reflector->isInterface()) {
return 'Interface Methods';
} elseif (\method_exists($reflector, 'isTrait') && $reflector->isTrait()) {
return 'Trait Methods';
} else {
return 'Class Methods';
}
}






private function getVisibilityStyle(\ReflectionMethod $method): string
{
if ($method->isPublic()) {
return self::IS_PUBLIC;
} elseif ($method->isProtected()) {
return self::IS_PROTECTED;
} else {
return self::IS_PRIVATE;
}
}
}
<?php










namespace Psy\Command\ListCommand;

use Psy\Reflection\ReflectionNamespace;
use Symfony\Component\Console\Input\InputInterface;




class ClassEnumerator extends Enumerator
{



protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
return [];
}

$internal = $input->getOption('internal');
$user = $input->getOption('user');
$prefix = $reflector === null ? null : \strtolower($reflector->getName()).'\\';

$ret = [];



if ($input->getOption('classes')) {
$ret = \array_merge($ret, $this->filterClasses('Classes', \get_declared_classes(), $internal, $user, $prefix));
}

if ($input->getOption('interfaces')) {
$ret = \array_merge($ret, $this->filterClasses('Interfaces', \get_declared_interfaces(), $internal, $user, $prefix));
}

if ($input->getOption('traits')) {
$ret = \array_merge($ret, $this->filterClasses('Traits', \get_declared_traits(), $internal, $user, $prefix));
}

return \array_map([$this, 'prepareClasses'], \array_filter($ret));
}















protected function filterClasses(string $key, array $classes, bool $internal, bool $user, string $prefix = null): array
{
$ret = [];

if ($internal) {
$ret['Internal '.$key] = \array_filter($classes, function ($class) use ($prefix) {
if ($prefix !== null && \strpos(\strtolower($class), $prefix) !== 0) {
return false;
}

$refl = new \ReflectionClass($class);

return $refl->isInternal();
});
}

if ($user) {
$ret['User '.$key] = \array_filter($classes, function ($class) use ($prefix) {
if ($prefix !== null && \strpos(\strtolower($class), $prefix) !== 0) {
return false;
}

$refl = new \ReflectionClass($class);

return !$refl->isInternal();
});
}

if (!$user && !$internal) {
$ret[$key] = \array_filter($classes, function ($class) use ($prefix) {
return $prefix === null || \strpos(\strtolower($class), $prefix) === 0;
});
}

return $ret;
}








protected function prepareClasses(array $classes): array
{
\natcasesort($classes);


$ret = [];

foreach ($classes as $name) {
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => self::IS_CLASS,
'value' => $this->presentSignature($name),
];
}
}

return $ret;
}
}
<?php










namespace Psy\Command\ListCommand;

use Symfony\Component\Console\Input\InputInterface;




class ClassConstantEnumerator extends Enumerator
{



protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if ($reflector === null) {
return [];
}


if (!$reflector instanceof \ReflectionClass) {

return [];
}


if (!$input->getOption('constants')) {
return [];
}

$noInherit = $input->getOption('no-inherit');
$constants = $this->prepareConstants($this->getConstants($reflector, $noInherit));

if (empty($constants)) {
return [];
}

$ret = [];
$ret[$this->getKindLabel($reflector)] = $constants;

return $ret;
}









protected function getConstants(\ReflectionClass $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();

$constants = [];
foreach ($reflector->getConstants() as $name => $constant) {
$constReflector = new \ReflectionClassConstant($reflector->name, $name);

if ($noInherit && $constReflector->getDeclaringClass()->getName() !== $className) {
continue;
}

$constants[$name] = $constReflector;
}

\ksort($constants, \SORT_NATURAL | \SORT_FLAG_CASE);

return $constants;
}








protected function prepareConstants(array $constants): array
{

$ret = [];

foreach ($constants as $name => $constant) {
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => self::IS_CONSTANT,
'value' => $this->presentRef($constant->getValue()),
];
}
}

return $ret;
}






protected function getKindLabel(\ReflectionClass $reflector): string
{
if ($reflector->isInterface()) {
return 'Interface Constants';
} else {
return 'Class Constants';
}
}
}
<?php










namespace Psy\Command\ListCommand;

use Psy\Formatter\SignatureFormatter;
use Psy\Input\FilterOptions;
use Psy\Util\Mirror;
use Psy\VarDumper\Presenter;
use Symfony\Component\Console\Input\InputInterface;




abstract class Enumerator
{

const IS_PUBLIC = 'public';
const IS_PROTECTED = 'protected';
const IS_PRIVATE = 'private';
const IS_GLOBAL = 'global';
const IS_CONSTANT = 'const';
const IS_CLASS = 'class';
const IS_FUNCTION = 'function';

private $filter;
private $presenter;






public function __construct(Presenter $presenter)
{
$this->filter = new FilterOptions();
$this->presenter = $presenter;
}










public function enumerate(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
$this->filter->bind($input);

return $this->listItems($input, $reflector, $target);
}






















abstract protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array;

protected function showItem($name)
{
return $this->filter->match($name);
}

protected function presentRef($value)
{
return $this->presenter->presentRef($value);
}

protected function presentSignature($target)
{

if (!$target instanceof \Reflector) {
$target = Mirror::get($target);
}

return SignatureFormatter::format($target);
}
}
<?php










namespace Psy\Command\ListCommand;

use Psy\Reflection\ReflectionNamespace;
use Symfony\Component\Console\Input\InputInterface;




class FunctionEnumerator extends Enumerator
{



protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
return [];
}


if (!$input->getOption('functions')) {
return [];
}

if ($input->getOption('user')) {
$label = 'User Functions';
$functions = $this->getFunctions('user');
} elseif ($input->getOption('internal')) {
$label = 'Internal Functions';
$functions = $this->getFunctions('internal');
} else {
$label = 'Functions';
$functions = $this->getFunctions();
}

$prefix = $reflector === null ? null : \strtolower($reflector->getName()).'\\';
$functions = $this->prepareFunctions($functions, $prefix);

if (empty($functions)) {
return [];
}

$ret = [];
$ret[$label] = $functions;

return $ret;
}










protected function getFunctions(string $type = null): array
{
$funcs = \get_defined_functions();

if ($type) {
return $funcs[$type];
} else {
return \array_merge($funcs['internal'], $funcs['user']);
}
}









protected function prepareFunctions(array $functions, string $prefix = null): array
{
\natcasesort($functions);


$ret = [];

foreach ($functions as $name) {
if ($prefix !== null && \strpos(\strtolower($name), $prefix) !== 0) {
continue;
}

if ($this->showItem($name)) {
try {
$ret[$name] = [
'name' => $name,
'style' => self::IS_FUNCTION,
'value' => $this->presentSignature($name),
];
} catch (\Throwable $e) {

}
}
}

return $ret;
}
}
<?php










namespace Psy\Command\ListCommand;

use Symfony\Component\Console\Input\InputInterface;




class GlobalVariableEnumerator extends Enumerator
{



protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if ($reflector !== null || $target !== null) {
return [];
}


if (!$input->getOption('globals')) {
return [];
}

$globals = $this->prepareGlobals($this->getGlobals());

if (empty($globals)) {
return [];
}

return [
'Global Variables' => $globals,
];
}






protected function getGlobals(): array
{
global $GLOBALS;

$names = \array_keys($GLOBALS);
\natcasesort($names);

$ret = [];
foreach ($names as $name) {
$ret[$name] = $GLOBALS[$name];
}

return $ret;
}








protected function prepareGlobals(array $globals): array
{

$ret = [];

foreach ($globals as $name => $value) {
if ($this->showItem($name)) {
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => self::IS_GLOBAL,
'value' => $this->presentRef($value),
];
}
}

return $ret;
}
}
<?php










namespace Psy\Command\ListCommand;

use Psy\Reflection\ReflectionNamespace;
use Symfony\Component\Console\Input\InputInterface;




class ConstantEnumerator extends Enumerator
{

private static $categoryLabels = [
'libxml' => 'libxml',
'openssl' => 'OpenSSL',
'pcre' => 'PCRE',
'sqlite3' => 'SQLite3',
'curl' => 'cURL',
'dom' => 'DOM',
'ftp' => 'FTP',
'gd' => 'GD',
'gmp' => 'GMP',
'iconv' => 'iconv',
'json' => 'JSON',
'ldap' => 'LDAP',
'mbstring' => 'mbstring',
'odbc' => 'ODBC',
'pcntl' => 'PCNTL',
'pgsql' => 'pgsql',
'posix' => 'POSIX',
'mysqli' => 'mysqli',
'soap' => 'SOAP',
'exif' => 'EXIF',
'sysvmsg' => 'sysvmsg',
'xml' => 'XML',
'xsl' => 'XSL',
];




protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{

if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
return [];
}


if (!$input->getOption('constants')) {
return [];
}

$user = $input->getOption('user');
$internal = $input->getOption('internal');
$category = $input->getOption('category');

if ($category) {
$category = \strtolower($category);

if ($category === 'internal') {
$internal = true;
$category = null;
} elseif ($category === 'user') {
$user = true;
$category = null;
}
}

$ret = [];

if ($user) {
$ret['User Constants'] = $this->getConstants('user');
}

if ($internal) {
$ret['Internal Constants'] = $this->getConstants('internal');
}

if ($category) {
$caseCategory = \array_key_exists($category, self::$categoryLabels) ? self::$categoryLabels[$category] : \ucfirst($category);
$label = $caseCategory.' Constants';
$ret[$label] = $this->getConstants($category);
}

if (!$user && !$internal && !$category) {
$ret['Constants'] = $this->getConstants();
}

if ($reflector !== null) {
$prefix = \strtolower($reflector->getName()).'\\';

foreach ($ret as $key => $names) {
foreach (\array_keys($names) as $name) {
if (\strpos(\strtolower($name), $prefix) !== 0) {
unset($ret[$key][$name]);
}
}
}
}

return \array_map([$this, 'prepareConstants'], \array_filter($ret));
}











protected function getConstants(string $category = null): array
{
if (!$category) {
return \get_defined_constants();
}

$consts = \get_defined_constants(true);

if ($category === 'internal') {
unset($consts['user']);

return \array_merge(...\array_values($consts));
}

foreach ($consts as $key => $value) {
if (\strtolower($key) === $category) {
return $value;
}
}

return [];
}








protected function prepareConstants(array $constants): array
{

$ret = [];

$names = \array_keys($constants);
\natcasesort($names);

foreach ($names as $name) {
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => self::IS_CONSTANT,
'value' => $this->presentRef($constants[$name]),
];
}
}

return $ret;
}
}
<?php










namespace Psy\Command\ListCommand;

use Symfony\Component\Console\Input\InputInterface;




class PropertyEnumerator extends Enumerator
{



protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{


if ($reflector === null) {
return [];
}


if (!$reflector instanceof \ReflectionClass) {
return [];
}


if (!$input->getOption('properties')) {
return [];
}

$showAll = $input->getOption('all');
$noInherit = $input->getOption('no-inherit');
$properties = $this->prepareProperties($this->getProperties($showAll, $reflector, $noInherit), $target);

if (empty($properties)) {
return [];
}

$ret = [];
$ret[$this->getKindLabel($reflector)] = $properties;

return $ret;
}










protected function getProperties(bool $showAll, \ReflectionClass $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();

$properties = [];
foreach ($reflector->getProperties() as $property) {
if ($noInherit && $property->getDeclaringClass()->getName() !== $className) {
continue;
}

if ($showAll || $property->isPublic()) {
$properties[$property->getName()] = $property;
}
}

\ksort($properties, \SORT_NATURAL | \SORT_FLAG_CASE);

return $properties;
}








protected function prepareProperties(array $properties, $target = null): array
{

$ret = [];

foreach ($properties as $name => $property) {
if ($this->showItem($name)) {
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => $this->getVisibilityStyle($property),
'value' => $this->presentValue($property, $target),
];
}
}

return $ret;
}






protected function getKindLabel(\ReflectionClass $reflector): string
{
if (\method_exists($reflector, 'isTrait') && $reflector->isTrait()) {
return 'Trait Properties';
} else {
return 'Class Properties';
}
}






private function getVisibilityStyle(\ReflectionProperty $property): string
{
if ($property->isPublic()) {
return self::IS_PUBLIC;
} elseif ($property->isProtected()) {
return self::IS_PROTECTED;
} else {
return self::IS_PRIVATE;
}
}







protected function presentValue(\ReflectionProperty $property, $target): string
{
if (!$target) {
return '';
}



if (!\is_object($target)) {
try {
$refl = new \ReflectionClass($target);
$props = $refl->getDefaultProperties();
if (\array_key_exists($property->name, $props)) {
$suffix = $property->isStatic() ? '' : ' <aside>(default)</aside>';

return $this->presentRef($props[$property->name]).$suffix;
}
} catch (\Throwable $e) {

}

return '';
}

$property->setAccessible(true);
$value = $property->getValue($target);

return $this->presentRef($value);
}
}
<?php










namespace Psy\Command;

use Psy\Command\ListCommand\ClassConstantEnumerator;
use Psy\Command\ListCommand\ClassEnumerator;
use Psy\Command\ListCommand\ConstantEnumerator;
use Psy\Command\ListCommand\FunctionEnumerator;
use Psy\Command\ListCommand\GlobalVariableEnumerator;
use Psy\Command\ListCommand\MethodEnumerator;
use Psy\Command\ListCommand\PropertyEnumerator;
use Psy\Command\ListCommand\VariableEnumerator;
use Psy\Exception\RuntimeException;
use Psy\Input\CodeArgument;
use Psy\Input\FilterOptions;
use Psy\Output\ShellOutput;
use Psy\VarDumper\Presenter;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class ListCommand extends ReflectingCommand implements PresenterAware
{
protected $presenter;
protected $enumerators;






public function setPresenter(Presenter $presenter)
{
$this->presenter = $presenter;
}




protected function configure()
{
list($grep, $insensitive, $invert) = FilterOptions::getOptions();

$this
->setName('ls')
->setAliases(['dir'])
->setDefinition([
new CodeArgument('target', CodeArgument::OPTIONAL, 'A target class or object to list.'),

new InputOption('vars', '', InputOption::VALUE_NONE, 'Display variables.'),
new InputOption('constants', 'c', InputOption::VALUE_NONE, 'Display defined constants.'),
new InputOption('functions', 'f', InputOption::VALUE_NONE, 'Display defined functions.'),
new InputOption('classes', 'k', InputOption::VALUE_NONE, 'Display declared classes.'),
new InputOption('interfaces', 'I', InputOption::VALUE_NONE, 'Display declared interfaces.'),
new InputOption('traits', 't', InputOption::VALUE_NONE, 'Display declared traits.'),

new InputOption('no-inherit', '', InputOption::VALUE_NONE, 'Exclude inherited methods, properties and constants.'),

new InputOption('properties', 'p', InputOption::VALUE_NONE, 'Display class or object properties (public properties by default).'),
new InputOption('methods', 'm', InputOption::VALUE_NONE, 'Display class or object methods (public methods by default).'),

$grep,
$insensitive,
$invert,

new InputOption('globals', 'g', InputOption::VALUE_NONE, 'Include global variables.'),
new InputOption('internal', 'n', InputOption::VALUE_NONE, 'Limit to internal functions and classes.'),
new InputOption('user', 'u', InputOption::VALUE_NONE, 'Limit to user-defined constants, functions and classes.'),
new InputOption('category', 'C', InputOption::VALUE_REQUIRED, 'Limit to constants in a specific category (e.g. "date").'),

new InputOption('all', 'a', InputOption::VALUE_NONE, 'Include private and protected methods and properties.'),
new InputOption('long', 'l', InputOption::VALUE_NONE, 'List in long format: includes class names and method signatures.'),
])
->setDescription('List local, instance or class variables, methods and constants.')
->setHelp(
<<<'HELP'
List variables, constants, classes, interfaces, traits, functions, methods,
and properties.

Called without options, this will return a list of variables currently in scope.

If a target object is provided, list properties, constants and methods of that
target. If a class, interface or trait name is passed instead, list constants
and methods on that class.

e.g.
<return>>>> ls</return>
<return>>>> ls $foo</return>
<return>>>> ls -k --grep mongo -i</return>
<return>>>> ls -al ReflectionClass</return>
<return>>>> ls --constants --category date</return>
<return>>>> ls -l --functions --grep /^array_.*/</return>
<return>>>> ls -l --properties new DateTime()</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->validateInput($input);
$this->initEnumerators();

$method = $input->getOption('long') ? 'writeLong' : 'write';

if ($target = $input->getArgument('target')) {
list($target, $reflector) = $this->getTargetAndReflector($target);
} else {
$reflector = null;
}


if ($output instanceof ShellOutput && $input->getOption('long')) {
$output->startPaging();
}

foreach ($this->enumerators as $enumerator) {
$this->$method($output, $enumerator->enumerate($input, $reflector, $target));
}

if ($output instanceof ShellOutput && $input->getOption('long')) {
$output->stopPaging();
}


if ($reflector !== null) {
$this->setCommandScopeVariables($reflector);
}

return 0;
}




protected function initEnumerators()
{
if (!isset($this->enumerators)) {
$mgr = $this->presenter;

$this->enumerators = [
new ClassConstantEnumerator($mgr),
new ClassEnumerator($mgr),
new ConstantEnumerator($mgr),
new FunctionEnumerator($mgr),
new GlobalVariableEnumerator($mgr),
new PropertyEnumerator($mgr),
new MethodEnumerator($mgr),
new VariableEnumerator($mgr, $this->context),
];
}
}







protected function write(OutputInterface $output, array $result)
{
if (\count($result) === 0) {
return;
}

foreach ($result as $label => $items) {
$names = \array_map([$this, 'formatItemName'], $items);
$output->writeln(\sprintf('<strong>%s</strong>: %s', $label, \implode(', ', $names)));
}
}









protected function writeLong(OutputInterface $output, array $result)
{
if (\count($result) === 0) {
return;
}

$table = $this->getTable($output);

foreach ($result as $label => $items) {
$output->writeln('');
$output->writeln(\sprintf('<strong>%s:</strong>', $label));

$table->setRows([]);
foreach ($items as $item) {
$table->addRow([$this->formatItemName($item), $item['value']]);
}

$table->render();
}
}






private function formatItemName(array $item): string
{
return \sprintf('<%s>%s</%s>', $item['style'], OutputFormatter::escape($item['name']), $item['style']);
}








private function validateInput(InputInterface $input)
{
if (!$input->getArgument('target')) {

foreach (['properties', 'methods', 'no-inherit'] as $option) {
if ($input->getOption($option)) {
throw new RuntimeException('--'.$option.' does not make sense without a specified target');
}
}

foreach (['globals', 'vars', 'constants', 'functions', 'classes', 'interfaces', 'traits'] as $option) {
if ($input->getOption($option)) {
return;
}
}


$input->setOption('vars', true);
} else {

foreach (['vars', 'globals'] as $option) {
if ($input->getOption($option)) {
throw new RuntimeException('--'.$option.' does not make sense with a specified target');
}
}


foreach (['constants', 'properties', 'methods', 'functions', 'classes', 'interfaces', 'traits'] as $option) {
if ($input->getOption($option)) {
return;
}
}


$input->setOption('constants', true);
$input->setOption('properties', true);
$input->setOption('methods', true);
}
}
}
<?php










namespace Psy\Command;

use PhpParser\Node\Arg;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Throw_;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Exception\ThrowUpException;
use Psy\Input\CodeArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;




class ThrowUpCommand extends Command
{
private $parser;
private $printer;




public function __construct($name = null)
{
$this->parser = new CodeArgumentParser();
$this->printer = new Printer();

parent::__construct($name);
}




protected function configure()
{
$this
->setName('throw-up')
->setDefinition([
new CodeArgument('exception', CodeArgument::OPTIONAL, 'Exception or Error to throw.'),
])
->setDescription('Throw an exception or error out of the Psy Shell.')
->setHelp(
<<<'HELP'
Throws an exception or error out of the current the Psy Shell instance.

By default it throws the most recent exception.

e.g.
<return>>>> throw-up</return>
<return>>>> throw-up $e</return>
<return>>>> throw-up new Exception('WHEEEEEE!')</return>
<return>>>> throw-up "bye!"</return>
HELP
);
}








protected function execute(InputInterface $input, OutputInterface $output): int
{
$args = $this->prepareArgs($input->getArgument('exception'));
$throwStmt = new Expression(new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args)));
$throwCode = $this->printer->prettyPrint([$throwStmt]);

$shell = $this->getApplication();
$shell->addCode($throwCode, !$shell->hasCode());

return 0;
}












private function prepareArgs(string $code = null): array
{
if (!$code) {

return [new Arg(new Variable('_e'))];
}

$nodes = $this->parser->parse($code);
if (\count($nodes) !== 1) {
throw new \InvalidArgumentException('No idea how to throw this');
}

$node = $nodes[0];
$expr = $node->expr;

$args = [new Arg($expr, false, false, $node->getAttributes())];


if ($expr instanceof String_) {
return [new New_(new FullyQualifiedName(\Exception::class), $args)];
}

return $args;
}
}
<?php










namespace Psy\Command;

use Psy\Output\ShellOutput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;






class HelpCommand extends Command
{
private $command;




protected function configure()
{
$this
->setName('help')
->setAliases(['?'])
->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name.', null),
])
->setDescription('Show a list of commands. Type `help [foo]` for information about [foo].')
->setHelp('My. How meta.');
}






public function setCommand(Command $command)
{
$this->command = $command;
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($this->command !== null) {

$output->page($this->command->asText());
$this->command = null;
} elseif ($name = $input->getArgument('command_name')) {

$output->page($this->getApplication()->get($name)->asText());
} else {

$commands = $this->getApplication()->all();

$table = $this->getTable($output);

foreach ($commands as $name => $command) {
if ($name !== $command->getName()) {
continue;
}

if ($command->getAliases()) {
$aliases = \sprintf('<comment>Aliases:</comment> %s', \implode(', ', $command->getAliases()));
} else {
$aliases = '';
}

$table->addRow([
\sprintf('<info>%s</info>', $name),
$command->getDescription(),
$aliases,
]);
}

if ($output instanceof ShellOutput) {
$output->startPaging();
}

$table->render();

if ($output instanceof ShellOutput) {
$output->stopPaging();
}
}

return 0;
}
}
<?php










namespace Psy\Command;

use Psy\Formatter\CodeFormatter;
use Psy\Output\ShellOutput;
use Psy\Shell;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class WhereamiCommand extends Command
{
private $backtrace;

public function __construct()
{
$this->backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);

parent::__construct();
}




protected function configure()
{
$this
->setName('whereami')
->setDefinition([
new InputOption('num', 'n', InputOption::VALUE_OPTIONAL, 'Number of lines before and after.', '5'),
new InputOption('file', 'f|a', InputOption::VALUE_NONE, 'Show the full source for the current file.'),
])
->setDescription('Show where you are in the code.')
->setHelp(
<<<'HELP'
Show where you are in the code.

Optionally, include the number of lines before and after you want to display,
or --file for the whole file.

e.g.
<return>> whereami </return>
<return>> whereami -n10</return>
<return>> whereami --file</return>
HELP
);
}






protected function trace(): array
{
foreach (\array_reverse($this->backtrace) as $stackFrame) {
if ($this->isDebugCall($stackFrame)) {
return $stackFrame;
}
}

return \end($this->backtrace);
}

private static function isDebugCall(array $stackFrame): bool
{
$class = isset($stackFrame['class']) ? $stackFrame['class'] : null;
$function = isset($stackFrame['function']) ? $stackFrame['function'] : null;

return ($class === null && $function === 'Psy\\debug') ||
($class === Shell::class && \in_array($function, ['__construct', 'debug']));
}






protected function fileInfo(): array
{
$stackFrame = $this->trace();
if (\preg_match('/eval\(/', $stackFrame['file'])) {
\preg_match_all('/([^\(]+)\((\d+)/', $stackFrame['file'], $matches);
$file = $matches[1][0];
$line = (int) $matches[2][0];
} else {
$file = $stackFrame['file'];
$line = $stackFrame['line'];
}

return \compact('file', 'line');
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$info = $this->fileInfo();
$num = $input->getOption('num');
$lineNum = $info['line'];
$startLine = \max($lineNum - $num, 1);
$endLine = $lineNum + $num;
$code = \file_get_contents($info['file']);

if ($input->getOption('file')) {
$startLine = 1;
$endLine = null;
}

if ($output instanceof ShellOutput) {
$output->startPaging();
}

$output->writeln(\sprintf('From <info>%s:%s</info>:', $this->replaceCwd($info['file']), $lineNum));
$output->write(CodeFormatter::formatCode($code, $startLine, $endLine, $lineNum), false);

if ($output instanceof ShellOutput) {
$output->stopPaging();
}

return 0;
}






private function replaceCwd(string $file): string
{
$cwd = \getcwd();
if ($cwd === false) {
return $file;
}

$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;

return \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file);
}
}
<?php










namespace Psy\Command;

use Psy\Exception\BreakException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;






class ExitCommand extends Command
{



protected function configure()
{
$this
->setName('exit')
->setAliases(['quit', 'q'])
->setDefinition([])
->setDescription('End the current session and return to caller.')
->setHelp(
<<<'HELP'
End the current session and return to caller.

e.g.
<return>>>> exit</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
throw new BreakException('Goodbye');
}
}
<?php










namespace Psy\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;




class PsyVersionCommand extends Command
{



protected function configure()
{
$this
->setName('version')
->setDefinition([])
->setDescription('Show Psy Shell version.')
->setHelp('Show Psy Shell version.');
}




protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln($this->getApplication()->getVersion());

return 0;
}
}
<?php










namespace Psy\Command;

use Psy\Exception\RuntimeException;
use Psy\Exception\UnexpectedTargetException;
use Psy\Formatter\CodeFormatter;
use Psy\Formatter\SignatureFormatter;
use Psy\Input\CodeArgument;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class ShowCommand extends ReflectingCommand
{
private $lastException;
private $lastExceptionIndex;




protected function configure()
{
$this
->setName('show')
->setDefinition([
new CodeArgument('target', CodeArgument::OPTIONAL, 'Function, class, instance, constant, method or property to show.'),
new InputOption('ex', null, InputOption::VALUE_OPTIONAL, 'Show last exception context. Optionally specify a stack index.', 1),
])
->setDescription('Show the code for an object, class, constant, method or property.')
->setHelp(
<<<HELP
Show the code for an object, class, constant, method or property, or the context
of the last exception.

<return>cat --ex</return> defaults to showing the lines surrounding the location of the last
exception. Invoking it more than once travels up the exception's stack trace,
and providing a number shows the context of the given index of the trace.

e.g.
<return>>>> show \$myObject</return>
<return>>>> show Psy\Shell::debug</return>
<return>>>> show --ex</return>
<return>>>> show --ex 3</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{









$opts = $input->getOptions();





if ($opts['ex'] !== 1) {
if ($input->getArgument('target')) {
throw new \InvalidArgumentException('Too many arguments (supply either "target" or "--ex")');
}

$this->writeExceptionContext($input, $output);

return 0;
}

if ($input->getArgument('target')) {
$this->writeCodeContext($input, $output);

return 0;
}

throw new RuntimeException('Not enough arguments (missing: "target")');
}

private function writeCodeContext(InputInterface $input, OutputInterface $output)
{
try {
list($target, $reflector) = $this->getTargetAndReflector($input->getArgument('target'));
} catch (UnexpectedTargetException $e) {

$target = $e->getTarget();
if (\is_string($target) && \is_file($target) && $code = @\file_get_contents($target)) {
$file = \realpath($target);
if ($file !== $this->context->get('__file')) {
$this->context->setCommandScopeVariables([
'__file' => $file,
'__dir' => \dirname($file),
]);
}

$output->page(CodeFormatter::formatCode($code));

return;
} else {
throw $e;
}
}


$this->setCommandScopeVariables($reflector);

try {
$output->page(CodeFormatter::format($reflector));
} catch (RuntimeException $e) {
$output->writeln(SignatureFormatter::format($reflector));
throw $e;
}
}

private function writeExceptionContext(InputInterface $input, OutputInterface $output)
{
$exception = $this->context->getLastException();
if ($exception !== $this->lastException) {
$this->lastException = null;
$this->lastExceptionIndex = null;
}

$opts = $input->getOptions();
if ($opts['ex'] === null) {
if ($this->lastException && $this->lastExceptionIndex !== null) {
$index = $this->lastExceptionIndex + 1;
} else {
$index = 0;
}
} else {
$index = \max(0, (int) $input->getOption('ex') - 1);
}

$trace = $exception->getTrace();
\array_unshift($trace, [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
]);

if ($index >= \count($trace)) {
$index = 0;
}

$this->lastException = $exception;
$this->lastExceptionIndex = $index;

$output->writeln($this->getApplication()->formatException($exception));
$output->writeln('--');
$this->writeTraceLine($output, $trace, $index);
$this->writeTraceCodeSnippet($output, $trace, $index);

$this->setCommandScopeVariablesFromContext($trace[$index]);
}

private function writeTraceLine(OutputInterface $output, array $trace, $index)
{
$file = isset($trace[$index]['file']) ? $this->replaceCwd($trace[$index]['file']) : 'n/a';
$line = isset($trace[$index]['line']) ? $trace[$index]['line'] : 'n/a';

$output->writeln(\sprintf(
'From <info>%s:%d</info> at <strong>level %d</strong> of backtrace (of %d):',
OutputFormatter::escape($file),
OutputFormatter::escape($line),
$index + 1,
\count($trace)
));
}

private function replaceCwd(string $file): string
{
if ($cwd = \getcwd()) {
$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
}

if ($cwd === false) {
return $file;
} else {
return \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file);
}
}

private function writeTraceCodeSnippet(OutputInterface $output, array $trace, $index)
{
if (!isset($trace[$index]['file'])) {
return;
}

$file = $trace[$index]['file'];
if ($fileAndLine = $this->extractEvalFileAndLine($file)) {
list($file, $line) = $fileAndLine;
} else {
if (!isset($trace[$index]['line'])) {
return;
}

$line = $trace[$index]['line'];
}

if (\is_file($file)) {
$code = @\file_get_contents($file);
}

if (empty($code)) {
return;
}

$startLine = \max($line - 5, 0);
$endLine = $line + 5;

$output->write(CodeFormatter::formatCode($code, $startLine, $endLine, $line), false);
}

private function setCommandScopeVariablesFromContext(array $context)
{
$vars = [];

if (isset($context['class'])) {
$vars['__class'] = $context['class'];
if (isset($context['function'])) {
$vars['__method'] = $context['function'];
}

try {
$refl = new \ReflectionClass($context['class']);
if ($namespace = $refl->getNamespaceName()) {
$vars['__namespace'] = $namespace;
}
} catch (\Throwable $e) {

}
} elseif (isset($context['function'])) {
$vars['__function'] = $context['function'];

try {
$refl = new \ReflectionFunction($context['function']);
if ($namespace = $refl->getNamespaceName()) {
$vars['__namespace'] = $namespace;
}
} catch (\Throwable $e) {

}
}

if (isset($context['file'])) {
$file = $context['file'];
if ($fileAndLine = $this->extractEvalFileAndLine($file)) {
list($file, $line) = $fileAndLine;
} elseif (isset($context['line'])) {
$line = $context['line'];
}

if (\is_file($file)) {
$vars['__file'] = $file;
if (isset($line)) {
$vars['__line'] = $line;
}
$vars['__dir'] = \dirname($file);
}
}

$this->context->setCommandScopeVariables($vars);
}

private function extractEvalFileAndLine(string $file)
{
if (\preg_match('/(.*)\\((\\d+)\\) : eval\\(\\)\'d code$/', $file, $matches)) {
return [$matches[1], $matches[2]];
}
}
}
<?php










namespace Psy\Command;

use PhpParser\Node;
use Psy\Context;
use Psy\ContextAware;
use Psy\Input\CodeArgument;
use Psy\ParserFactory;
use Psy\VarDumper\Presenter;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Caster\Caster;




class ParseCommand extends Command implements ContextAware, PresenterAware
{





protected $context;

private $presenter;
private $parser;




public function __construct($name = null)
{
$this->parser = (new ParserFactory())->createParser();

parent::__construct($name);
}






public function setContext(Context $context)
{
$this->context = $context;
}






public function setPresenter(Presenter $presenter)
{
$this->presenter = clone $presenter;
$this->presenter->addCasters([
Node::class => function (Node $node, array $a) {
$a = [
Caster::PREFIX_VIRTUAL.'type' => $node->getType(),
Caster::PREFIX_VIRTUAL.'attributes' => $node->getAttributes(),
];

foreach ($node->getSubNodeNames() as $name) {
$a[Caster::PREFIX_VIRTUAL.$name] = $node->$name;
}

return $a;
},
]);
}




protected function configure()
{
$this
->setName('parse')
->setDefinition([
new CodeArgument('code', CodeArgument::REQUIRED, 'PHP code to parse.'),
new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse.', 10),
])
->setDescription('Parse PHP code and show the abstract syntax tree.')
->setHelp(
<<<'HELP'
Parse PHP code and show the abstract syntax tree.

This command is used in the development of PsySH. Given a string of PHP code,
it pretty-prints the PHP Parser parse tree.

See https://github.com/nikic/PHP-Parser

It prolly won't be super useful for most of you, but it's here if you want to play.
HELP
);
}




protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');
$parserKind = $input->getOption('kind');
$depth = $input->getOption('depth');

$nodes = $this->parser->parse($code);
$output->page($this->presenter->present($nodes, $depth));

$this->context->setReturnValue($nodes);

return 0;
}
}
<?php










namespace Psy\Command;

use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\CodeCleaner\NoReturnValue;
use Psy\Context;
use Psy\ContextAware;
use Psy\Exception\ErrorException;
use Psy\Exception\RuntimeException;
use Psy\Exception\UnexpectedTargetException;
use Psy\Reflection\ReflectionConstant;
use Psy\Sudo\SudoVisitor;
use Psy\Util\Mirror;




abstract class ReflectingCommand extends Command implements ContextAware
{
const CLASS_OR_FUNC = '/^[\\\\\w]+$/';
const CLASS_MEMBER = '/^([\\\\\w]+)::(\w+)$/';
const CLASS_STATIC = '/^([\\\\\w]+)::\$(\w+)$/';
const INSTANCE_MEMBER = '/^(\$\w+)(::|->)(\w+)$/';






protected $context;

private $parser;
private $traverser;
private $printer;




public function __construct($name = null)
{
$this->parser = new CodeArgumentParser();

$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new SudoVisitor());

$this->printer = new Printer();

parent::__construct($name);
}






public function setContext(Context $context)
{
$this->context = $context;
}










protected function getTarget(string $valueName): array
{
$valueName = \trim($valueName);
$matches = [];
switch (true) {
case \preg_match(self::CLASS_OR_FUNC, $valueName, $matches):
return [$this->resolveName($matches[0], true), null, 0];

case \preg_match(self::CLASS_MEMBER, $valueName, $matches):
return [$this->resolveName($matches[1]), $matches[2], Mirror::CONSTANT | Mirror::METHOD];

case \preg_match(self::CLASS_STATIC, $valueName, $matches):
return [$this->resolveName($matches[1]), $matches[2], Mirror::STATIC_PROPERTY | Mirror::PROPERTY];

case \preg_match(self::INSTANCE_MEMBER, $valueName, $matches):
if ($matches[2] === '->') {
$kind = Mirror::METHOD | Mirror::PROPERTY;
} else {
$kind = Mirror::CONSTANT | Mirror::METHOD;
}

return [$this->resolveObject($matches[1]), $matches[3], $kind];

default:
return [$this->resolveObject($valueName), null, 0];
}
}









protected function resolveName(string $name, bool $includeFunctions = false): string
{
$shell = $this->getApplication();


if (\in_array(\strtolower($name), ['self', 'static'])) {
if ($boundClass = $shell->getBoundClass()) {
return $boundClass;
}

if ($boundObject = $shell->getBoundObject()) {
return \get_class($boundObject);
}

$msg = \sprintf('Cannot use "%s" when no class scope is active', \strtolower($name));
throw new ErrorException($msg, 0, \E_USER_ERROR, "eval()'d code", 1);
}

if (\substr($name, 0, 1) === '\\') {
return $name;
}


if (self::couldBeClassName($name)) {
try {
$name = $this->resolveCode($name.'::class');
} catch (RuntimeException $e) {

}
}

if ($namespace = $shell->getNamespace()) {
$fullName = $namespace.'\\'.$name;

if (\class_exists($fullName) || \interface_exists($fullName) || ($includeFunctions && \function_exists($fullName))) {
return $fullName;
}
}

return $name;
}




protected function couldBeClassName(string $name): bool
{

return \preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*(\\\\[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)*$/', $name) === 1;
}








protected function getTargetAndReflector(string $valueName): array
{
list($value, $member, $kind) = $this->getTarget($valueName);

return [$value, Mirror::get($value, $member, $kind)];
}










protected function resolveCode(string $code)
{
try {

$nodes = $this->traverser->traverse($this->parser->parse($code));
$sudoCode = $this->printer->prettyPrint($nodes);
$value = $this->getApplication()->execute($sudoCode, true);
} catch (\Throwable $e) {

}

if (!isset($value) || $value instanceof NoReturnValue) {
throw new RuntimeException('Unknown target: '.$code);
}

return $value;
}










private function resolveObject(string $code)
{
$value = $this->resolveCode($code);

if (!\is_object($value)) {
throw new UnexpectedTargetException($value, 'Unable to inspect a non-object');
}

return $value;
}








protected function getScopeVariable(string $name)
{
return $this->context->get($name);
}






protected function getScopeVariables(): array
{
return $this->context->getAll();
}








protected function setCommandScopeVariables(\Reflector $reflector)
{
$vars = [];

switch (\get_class($reflector)) {
case \ReflectionClass::class:
case \ReflectionObject::class:
$vars['__class'] = $reflector->name;
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}
break;

case \ReflectionMethod::class:
$vars['__method'] = \sprintf('%s::%s', $reflector->class, $reflector->name);
$vars['__class'] = $reflector->class;
$classReflector = $reflector->getDeclaringClass();
if ($classReflector->inNamespace()) {
$vars['__namespace'] = $classReflector->getNamespaceName();
}
break;

case \ReflectionFunction::class:
$vars['__function'] = $reflector->name;
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}
break;

case \ReflectionGenerator::class:
$funcReflector = $reflector->getFunction();
$vars['__function'] = $funcReflector->name;
if ($funcReflector->inNamespace()) {
$vars['__namespace'] = $funcReflector->getNamespaceName();
}
if ($fileName = $reflector->getExecutingFile()) {
$vars['__file'] = $fileName;
$vars['__line'] = $reflector->getExecutingLine();
$vars['__dir'] = \dirname($fileName);
}
break;

case \ReflectionProperty::class:
case \ReflectionClassConstant::class:
$classReflector = $reflector->getDeclaringClass();
$vars['__class'] = $classReflector->name;
if ($classReflector->inNamespace()) {
$vars['__namespace'] = $classReflector->getNamespaceName();
}

if ($fileName = $reflector->getDeclaringClass()->getFileName()) {
$vars['__file'] = $fileName;
$vars['__dir'] = \dirname($fileName);
}
break;

case ReflectionConstant::class:
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}
break;
}

if ($reflector instanceof \ReflectionClass || $reflector instanceof \ReflectionFunctionAbstract) {
if ($fileName = $reflector->getFileName()) {
$vars['__file'] = $fileName;
$vars['__line'] = $reflector->getStartLine();
$vars['__dir'] = \dirname($fileName);
}
}

$this->context->setCommandScopeVariables($vars);
}
}
<?php










namespace Psy\Command\TimeitCommand;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeVisitorAbstract;
use Psy\CodeCleaner\NoReturnValue;
use Psy\Command\TimeitCommand;







class TimeitVisitor extends NodeVisitorAbstract
{
private $functionDepth;






public function beforeTraverse(array $nodes)
{
$this->functionDepth = 0;
}






public function enterNode(Node $node)
{


if ($node instanceof FunctionLike) {
$this->functionDepth++;

return;
}


if ($this->functionDepth === 0 && $node instanceof Return_) {
return new Return_($this->getEndCall($node->expr), $node->getAttributes());
}
}






public function leaveNode(Node $node)
{
if ($node instanceof FunctionLike) {
$this->functionDepth--;
}
}






public function afterTraverse(array $nodes)
{

\array_unshift($nodes, new Expression($this->getStartCall(), []));


$last = $nodes[\count($nodes) - 1];
if ($last instanceof Expr) {
\array_pop($nodes);
$nodes[] = $this->getEndCall($last);
} elseif ($last instanceof Expression) {
\array_pop($nodes);
$nodes[] = new Expression($this->getEndCall($last->expr), $last->getAttributes());
} elseif ($last instanceof Return_) {

} else {
$nodes[] = new Expression($this->getEndCall(), []);
}

return $nodes;
}






private function getStartCall(): StaticCall
{
return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markStart');
}








private function getEndCall(Expr $arg = null): StaticCall
{
if ($arg === null) {
$arg = NoReturnValue::create();
}

return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markEnd', [new Arg($arg)]);
}
}
<?php










namespace Psy\Command;

use Psy\Input\CodeArgument;
use Psy\VarDumper\Presenter;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






class DumpCommand extends ReflectingCommand implements PresenterAware
{
private $presenter;






public function setPresenter(Presenter $presenter)
{
$this->presenter = $presenter;
}




protected function configure()
{
$this
->setName('dump')
->setDefinition([
new CodeArgument('target', CodeArgument::REQUIRED, 'A target object or primitive to dump.'),
new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse.', 10),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Include private and protected methods and properties.'),
])
->setDescription('Dump an object or primitive.')
->setHelp(
<<<'HELP'
Dump an object or primitive.

This is like var_dump but <strong>way</strong> awesomer.

e.g.
<return>>>> dump $_</return>
<return>>>> dump $someVar</return>
<return>>>> dump $stuff->getAll()</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$depth = $input->getOption('depth');
$target = $this->resolveCode($input->getArgument('target'));
$output->page($this->presenter->present($target, $depth, $input->getOption('all') ? Presenter::VERBOSE : 0));

if (\is_object($target)) {
$this->setCommandScopeVariables(new \ReflectionObject($target));
}

return 0;
}
}
<?php










namespace Psy\Command;

use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Command\TimeitCommand\TimeitVisitor;
use Psy\Input\CodeArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;




class TimeitCommand extends Command
{
const RESULT_MSG = '<info>Command took %.6f seconds to complete.</info>';
const AVG_RESULT_MSG = '<info>Command took %.6f seconds on average (%.6f median; %.6f total) to complete.</info>';


private static $start = null;
private static $times = [];

private $parser;
private $traverser;
private $printer;




public function __construct($name = null)
{
$this->parser = new CodeArgumentParser();

$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new TimeitVisitor());

$this->printer = new Printer();

parent::__construct($name);
}




protected function configure()
{
$this
->setName('timeit')
->setDefinition([
new InputOption('num', 'n', InputOption::VALUE_REQUIRED, 'Number of iterations.'),
new CodeArgument('code', CodeArgument::REQUIRED, 'Code to execute.'),
])
->setDescription('Profiles with a timer.')
->setHelp(
<<<'HELP'
Time profiling for functions and commands.

e.g.
<return>>>> timeit sleep(1)</return>
<return>>>> timeit -n1000 $closure()</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');
$num = (int) ($input->getOption('num') ?: 1);
$shell = $this->getApplication();

$instrumentedCode = $this->instrumentCode($code);

self::$times = [];

do {
$_ = $shell->execute($instrumentedCode);
$this->ensureEndMarked();
} while (\count(self::$times) < $num);

$shell->writeReturnValue($_);

$times = self::$times;
self::$times = [];

if ($num === 1) {
$output->writeln(\sprintf(self::RESULT_MSG, $times[0] / 1e+9));
} else {
$total = \array_sum($times);
\rsort($times);
$median = $times[\round($num / 2)];

$output->writeln(\sprintf(self::AVG_RESULT_MSG, ($total / $num) / 1e+9, $median / 1e+9, $total / 1e+9));
}

return 0;
}








public static function markStart()
{
self::$start = \hrtime(true);
}















public static function markEnd($ret = null)
{
self::$times[] = \hrtime(true) - self::$start;
self::$start = null;

return $ret;
}







private function ensureEndMarked()
{
if (self::$start !== null) {
self::markEnd();
}
}







private function instrumentCode(string $code): string
{
return $this->printer->prettyPrint($this->traverser->traverse($this->parser->parse($code)));
}
}
<?php










namespace Psy\Command;

use Psy\Input\FilterOptions;
use Psy\Output\ShellOutput;
use Psy\Readline\Readline;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;






class HistoryCommand extends Command
{
private $filter;
private $readline;




public function __construct($name = null)
{
$this->filter = new FilterOptions();

parent::__construct($name);
}






public function setReadline(Readline $readline)
{
$this->readline = $readline;
}




protected function configure()
{
list($grep, $insensitive, $invert) = FilterOptions::getOptions();

$this
->setName('history')
->setAliases(['hist'])
->setDefinition([
new InputOption('show', 's', InputOption::VALUE_REQUIRED, 'Show the given range of lines.'),
new InputOption('head', 'H', InputOption::VALUE_REQUIRED, 'Display the first N items.'),
new InputOption('tail', 'T', InputOption::VALUE_REQUIRED, 'Display the last N items.'),

$grep,
$insensitive,
$invert,

new InputOption('no-numbers', 'N', InputOption::VALUE_NONE, 'Omit line numbers.'),

new InputOption('save', '', InputOption::VALUE_REQUIRED, 'Save history to a file.'),
new InputOption('replay', '', InputOption::VALUE_NONE, 'Replay.'),
new InputOption('clear', '', InputOption::VALUE_NONE, 'Clear the history.'),
])
->setDescription('Show the Psy Shell history.')
->setHelp(
<<<'HELP'
Show, search, save or replay the Psy Shell history.

e.g.
<return>>>> history --grep /[bB]acon/</return>
<return>>>> history --show 0..10 --replay</return>
<return>>>> history --clear</return>
<return>>>> history --tail 1000 --save somefile.txt</return>
HELP
);
}






protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->validateOnlyOne($input, ['show', 'head', 'tail']);
$this->validateOnlyOne($input, ['save', 'replay', 'clear']);

$history = $this->getHistorySlice(
$input->getOption('show'),
$input->getOption('head'),
$input->getOption('tail')
);
$highlighted = false;

$this->filter->bind($input);
if ($this->filter->hasFilter()) {
$matches = [];
$highlighted = [];
foreach ($history as $i => $line) {
if ($this->filter->match($line, $matches)) {
if (isset($matches[0])) {
$chunks = \explode($matches[0], $history[$i]);
$chunks = \array_map([__CLASS__, 'escape'], $chunks);
$glue = \sprintf('<urgent>%s</urgent>', self::escape($matches[0]));

$highlighted[$i] = \implode($glue, $chunks);
}
} else {
unset($history[$i]);
}
}
}

if ($save = $input->getOption('save')) {
$output->writeln(\sprintf('Saving history in %s...', $save));
\file_put_contents($save, \implode(\PHP_EOL, $history).\PHP_EOL);
$output->writeln('<info>History saved.</info>');
} elseif ($input->getOption('replay')) {
if (!($input->getOption('show') || $input->getOption('head') || $input->getOption('tail'))) {
throw new \InvalidArgumentException('You must limit history via --head, --tail or --show before replaying');
}

$count = \count($history);
$output->writeln(\sprintf('Replaying %d line%s of history', $count, ($count !== 1) ? 's' : ''));
$this->getApplication()->addInput($history);
} elseif ($input->getOption('clear')) {
$this->clearHistory();
$output->writeln('<info>History cleared.</info>');
} else {
$type = $input->getOption('no-numbers') ? 0 : ShellOutput::NUMBER_LINES;
if (!$highlighted) {
$type = $type | OutputInterface::OUTPUT_RAW;
}

$output->page($highlighted ?: $history, $type);
}

return 0;
}








private function extractRange(string $range): array
{
if (\preg_match('/^\d+$/', $range)) {
return [$range, $range + 1];
}

$matches = [];
if ($range !== '..' && \preg_match('/^(\d*)\.\.(\d*)$/', $range, $matches)) {
$start = $matches[1] ? (int) $matches[1] : 0;
$end = $matches[2] ? (int) $matches[2] + 1 : \PHP_INT_MAX;

return [$start, $end];
}

throw new \InvalidArgumentException('Unexpected range: '.$range);
}










private function getHistorySlice($show, $head, $tail): array
{
$history = $this->readline->listHistory();


\array_pop($history);

if ($show) {
list($start, $end) = $this->extractRange($show);
$length = $end - $start;
} elseif ($head) {
if (!\preg_match('/^\d+$/', $head)) {
throw new \InvalidArgumentException('Please specify an integer argument for --head');
}

$start = 0;
$length = (int) $head;
} elseif ($tail) {
if (!\preg_match('/^\d+$/', $tail)) {
throw new \InvalidArgumentException('Please specify an integer argument for --tail');
}

$start = \count($history) - $tail;
$length = (int) $tail + 1;
} else {
return $history;
}

return \array_slice($history, $start, $length, true);
}







private function validateOnlyOne(InputInterface $input, array $options)
{
$count = 0;
foreach ($options as $opt) {
if ($input->getOption($opt)) {
$count++;
}
}

if ($count > 1) {
throw new \InvalidArgumentException('Please specify only one of --'.\implode(', --', $options));
}
}




private function clearHistory()
{
$this->readline->clearHistory();
}

public static function escape(string $string): string
{
return OutputFormatter::escape($string);
}
}
<?php










namespace Psy\Command;

use Psy\Shell;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command as BaseCommand;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Output\OutputInterface;




abstract class Command extends BaseCommand
{







public function setApplication(Application $application = null): void
{
if ($application !== null && !$application instanceof Shell) {
throw new \InvalidArgumentException('PsySH Commands require an instance of Psy\Shell');
}

parent::setApplication($application);
}




public function asText(): string
{
$messages = [
'<comment>Usage:</comment>',
' '.$this->getSynopsis(),
'',
];

if ($this->getAliases()) {
$messages[] = $this->aliasesAsText();
}

if ($this->getArguments()) {
$messages[] = $this->argumentsAsText();
}

if ($this->getOptions()) {
$messages[] = $this->optionsAsText();
}

if ($help = $this->getProcessedHelp()) {
$messages[] = '<comment>Help:</comment>';
$messages[] = ' '.\str_replace("\n", "\n ", $help)."\n";
}

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




private function getArguments(): array
{
$hidden = $this->getHiddenArguments();

return \array_filter($this->getNativeDefinition()->getArguments(), function ($argument) use ($hidden) {
return !\in_array($argument->getName(), $hidden);
});
}






protected function getHiddenArguments(): array
{
return ['command'];
}




private function getOptions(): array
{
$hidden = $this->getHiddenOptions();

return \array_filter($this->getNativeDefinition()->getOptions(), function ($option) use ($hidden) {
return !\in_array($option->getName(), $hidden);
});
}






protected function getHiddenOptions(): array
{
return ['verbose'];
}




private function aliasesAsText(): string
{
return '<comment>Aliases:</comment> <info>'.\implode(', ', $this->getAliases()).'</info>'.\PHP_EOL;
}




private function argumentsAsText(): string
{
$max = $this->getMaxWidth();
$messages = [];

$arguments = $this->getArguments();
if (!empty($arguments)) {
$messages[] = '<comment>Arguments:</comment>';
foreach ($arguments as $argument) {
if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
$default = \sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($argument->getDefault()));
} else {
$default = '';
}

$description = \str_replace("\n", "\n".\str_pad('', $max + 2, ' '), $argument->getDescription());

$messages[] = \sprintf(" <info>%-{$max}s</info> %s%s", $argument->getName(), $description, $default);
}

$messages[] = '';
}

return \implode(\PHP_EOL, $messages);
}




private function optionsAsText(): string
{
$max = $this->getMaxWidth();
$messages = [];

$options = $this->getOptions();
if ($options) {
$messages[] = '<comment>Options:</comment>';

foreach ($options as $option) {
if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
$default = \sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($option->getDefault()));
} else {
$default = '';
}

$multiple = $option->isArray() ? '<comment> (multiple values allowed)</comment>' : '';
$description = \str_replace("\n", "\n".\str_pad('', $max + 2, ' '), $option->getDescription());

$optionMax = $max - \strlen($option->getName()) - 2;
$messages[] = \sprintf(
" <info>%s</info> %-{$optionMax}s%s%s%s",
'--'.$option->getName(),
$option->getShortcut() ? \sprintf('(-%s) ', $option->getShortcut()) : '',
$description,
$default,
$multiple
);
}

$messages[] = '';
}

return \implode(\PHP_EOL, $messages);
}




private function getMaxWidth(): int
{
$max = 0;

foreach ($this->getOptions() as $option) {
$nameLength = \strlen($option->getName()) + 2;
if ($option->getShortcut()) {
$nameLength += \strlen($option->getShortcut()) + 3;
}

$max = \max($max, $nameLength);
}

foreach ($this->getArguments() as $argument) {
$max = \max($max, \strlen($argument->getName()));
}

return ++$max;
}






private function formatDefaultValue($default): string
{
if (\is_array($default) && $default === \array_values($default)) {
return \sprintf("['%s']", \implode("', '", $default));
}

return \str_replace("\n", '', \var_export($default, true));
}






protected function getTable(OutputInterface $output)
{
$style = new TableStyle();


if (\method_exists($style, 'setVerticalBorderChars')) {
$style->setVerticalBorderChars(' ');
$style->setHorizontalBorderChars('');
$style->setCrossingChars('', '', '', '', '', '', '', '', '');
} else {
$style->setVerticalBorderChar(' ');
$style->setHorizontalBorderChar('');
$style->setCrossingChar('');
}

$table = new Table($output);

return $table
->setRows([])
->setStyle($style);
}
}
<?php










namespace Psy\Util;

use Psy\Exception\RuntimeException;
use Psy\Reflection\ReflectionConstant;
use Psy\Reflection\ReflectionNamespace;




class Mirror
{
const CONSTANT = 1;
const METHOD = 2;
const STATIC_PROPERTY = 4;
const PROPERTY = 8;


















public static function get($value, string $member = null, int $filter = 15): \Reflector
{
if ($member === null && \is_string($value)) {
if (\function_exists($value)) {
return new \ReflectionFunction($value);
} elseif (\defined($value) || ReflectionConstant::isMagicConstant($value)) {
return new ReflectionConstant($value);
}
}

$class = self::getClass($value);

if ($member === null) {
return $class;
} elseif ($filter & self::CONSTANT && $class->hasConstant($member)) {
return new \ReflectionClassConstant($value, $member);
} elseif ($filter & self::METHOD && $class->hasMethod($member)) {
return $class->getMethod($member);
} elseif ($filter & self::PROPERTY && $class->hasProperty($member)) {
return $class->getProperty($member);
} elseif ($filter & self::STATIC_PROPERTY && $class->hasProperty($member) && $class->getProperty($member)->isStatic()) {
return $class->getProperty($member);
} else {
throw new RuntimeException(\sprintf('Unknown member %s on class %s', $member, \is_object($value) ? \get_class($value) : $value));
}
}










private static function getClass($value)
{
if (\is_object($value)) {
return new \ReflectionObject($value);
}

if (!\is_string($value)) {
throw new \InvalidArgumentException('Mirror expects an object or class');
}

if (\class_exists($value) || \interface_exists($value) || \trait_exists($value)) {
return new \ReflectionClass($value);
}

$namespace = \preg_replace('/(^\\\\|\\\\$)/', '', $value);
if (self::namespaceExists($namespace)) {
return new ReflectionNamespace($namespace);
}

throw new \InvalidArgumentException('Unknown namespace, class or function: '.$value);
}




private static function namespaceExists(string $value): bool
{
return \in_array(\strtolower($value), self::getDeclaredNamespaces());
}







private static function getDeclaredNamespaces(): array
{
$functions = \get_defined_functions();

$allNames = \array_merge(
$functions['internal'],
$functions['user'],
\get_declared_classes(),
\get_declared_interfaces(),
\get_declared_traits(),
\array_keys(\get_defined_constants())
);

$namespaces = [];
foreach ($allNames as $name) {
$chunks = \explode('\\', \strtolower($name));


\array_pop($chunks);

while (!empty($chunks)) {
$namespaces[\implode('\\', $chunks)] = true;
\array_pop($chunks);
}
}

$namespaceNames = \array_keys($namespaces);

\sort($namespaceNames);

return $namespaceNames;
}
}
<?php










namespace Psy\Util;






class Str
{
const UNVIS_RX = <<<'EOS'
/
    \\(?:
        ((?:040)|s)
        | (240)
        | (?: M-(.) )
        | (?: M\^(.) )
        | (?: \^(.) )
    )
/xS
EOS;




























public static function unvis(string $input): string
{
$output = \preg_replace_callback(self::UNVIS_RX, [self::class, 'unvisReplace'], $input);

return \stripcslashes($output);
}






protected static function unvisReplace(array $match): string
{

if (!empty($match[1])) {
return "\x20";
}

if (!empty($match[2])) {
return "\xa0";
}

if (isset($match[3]) && $match[3] !== '') {
$chr = $match[3];

$cp = 0200;
$cp |= \ord($chr);

return \chr($cp);
}

if (isset($match[4]) && $match[4] !== '') {
$chr = $match[4];

$cp = 0200;
$cp |= ($chr === '?') ? 0177 : \ord($chr) & 037;

return \chr($cp);
}

if (isset($match[5]) && $match[5] !== '') {
$chr = $match[5];

$cp = 0;
$cp |= ($chr === '?') ? 0177 : \ord($chr) & 037;

return \chr($cp);
}
}
}
<?php










namespace Psy\Util;




class Json
{






public static function encode($val, int $opt = 0): string
{
$opt |= \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE;

return \json_encode($val, $opt);
}
}
<?php










namespace Psy\Util;











class Docblock
{







public static $vectors = [
'throws' => ['type', 'desc'],
'param' => ['type', 'var', 'desc'],
'return' => ['type', 'desc'],
];

protected $reflector;






public $desc;













public $tags;






public $comment;






public function __construct(\Reflector $reflector)
{
$this->reflector = $reflector;

if ($reflector instanceof \ReflectionClass || $reflector instanceof \ReflectionClassConstant || $reflector instanceof \ReflectionFunctionAbstract || $reflector instanceof \ReflectionProperty) {
$this->setComment($reflector->getDocComment());
}
}






protected function setComment(string $comment)
{
$this->desc = '';
$this->tags = [];
$this->comment = $comment;

$this->parseComment($comment);
}








protected static function prefixLength(array $lines): int
{

$lines = \array_filter($lines, function ($line) {
return \substr($line, \strspn($line, "* \t\n\r\0\x0B"));
});


\sort($lines);

$first = \reset($lines);
$last = \end($lines);


if (\count($lines) === 1) {
return \strspn($first, "* \t\n\r\0\x0B");
}


$count = \min(\strlen($first), \strlen($last));
for ($i = 0; $i < $count; $i++) {
if ($first[$i] !== $last[$i]) {
return $i;
}
}

return $count;
}






protected function parseComment(string $comment)
{

$comment = \substr($comment, 3, -2);


$comment = \array_filter(\preg_split('/\r?\n\r?/', $comment));


$prefixLength = self::prefixLength($comment);
$comment = \array_map(function ($line) use ($prefixLength) {
return \rtrim(\substr($line, $prefixLength));
}, $comment);


$blocks = [];
$b = -1;
foreach ($comment as $line) {
if (self::isTagged($line)) {
$b++;
$blocks[] = [];
} elseif ($b === -1) {
$b = 0;
$blocks[] = [];
}
$blocks[$b][] = $line;
}


foreach ($blocks as $block => $body) {
$body = \trim(\implode("\n", $body));

if ($block === 0 && !self::isTagged($body)) {

$this->desc = $body;
} else {

$tag = \substr(self::strTag($body), 1);
$body = \ltrim(\substr($body, \strlen($tag) + 2));

if (isset(self::$vectors[$tag])) {

$count = \count(self::$vectors[$tag]);
if ($body) {
$parts = \preg_split('/\s+/', $body, $count);
} else {
$parts = [];
}


$parts = \array_pad($parts, $count, null);


$this->tags[$tag][] = \array_combine(self::$vectors[$tag], $parts);
} else {

$this->tags[$tag][] = $body;
}
}
}
}






public function hasTag(string $tag): bool
{
return \is_array($this->tags) && \array_key_exists($tag, $this->tags);
}








public function tag(string $tag): ?array
{
return $this->hasTag($tag) ? $this->tags[$tag] : null;
}






public static function isTagged(string $str): bool
{
return isset($str[1]) && $str[0] === '@' && !\preg_match('/[^A-Za-z]/', $str[1]);
}








public static function strTag(string $str)
{
if (\preg_match('/^@[a-z0-9_]+/', $str, $matches)) {
return $matches[0];
}
}
}
<?php










namespace Psy\Exception;

class UnexpectedTargetException extends RuntimeException
{
private $target;







public function __construct($target, string $message = '', int $code = 0, \Throwable $previous = null)
{
$this->target = $target;
parent::__construct($message, $code, $previous);
}




public function getTarget()
{
return $this->target;
}
}
<?php










namespace Psy\Exception;




class FatalErrorException extends \ErrorException implements Exception
{
private $rawMessage;











public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, \Throwable $previous = null)
{

if ($lineno === -1) {
$lineno = null;
}

$this->rawMessage = $message;
$message = \sprintf('PHP Fatal error:  %s in %s on line %d', $message, $filename ?: "eval()'d code", $lineno);
parent::__construct($message, $code, $severity, $filename, $lineno, $previous);
}




public function getRawMessage(): string
{
return $this->rawMessage;
}
}
<?php










namespace Psy\Exception;




class DeprecatedException extends RuntimeException
{

}
<?php










namespace Psy\Exception;




interface Exception
{







public function getRawMessage();
}
<?php










namespace Psy\Exception;




class RuntimeException extends \RuntimeException implements Exception
{
private $rawMessage;








public function __construct(string $message = '', int $code = 0, \Throwable $previous = null)
{
$this->rawMessage = $message;
parent::__construct($message, $code, $previous);
}




public function getRawMessage(): string
{
return $this->rawMessage;
}
}
<?php










namespace Psy\Exception;




class ParseErrorException extends \PhpParser\Error implements Exception
{







public function __construct(string $message = '', $attributes = [])
{
$message = \sprintf('PHP Parse error: %s', $message);

if (!\is_array($attributes)) {
$attributes = ['startLine' => $attributes];
}

parent::__construct($message, $attributes);
}






public static function fromParseError(\PhpParser\Error $e): self
{
return new self($e->getRawMessage(), $e->getAttributes());
}
}
<?php










namespace Psy\Exception;




class ErrorException extends \ErrorException implements Exception
{
private $rawMessage;











public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, \Throwable $previous = null)
{
$this->rawMessage = $message;

if (!empty($filename) && \preg_match('{Psy[/\\\\]ExecutionLoop}', $filename)) {
$filename = '';
}

switch ($severity) {
case \E_STRICT:
$type = 'Strict error';
break;

case \E_NOTICE:
case \E_USER_NOTICE:
$type = 'Notice';
break;

case \E_WARNING:
case \E_CORE_WARNING:
case \E_COMPILE_WARNING:
case \E_USER_WARNING:
$type = 'Warning';
break;

case \E_DEPRECATED:
case \E_USER_DEPRECATED:
$type = 'Deprecated';
break;

case \E_RECOVERABLE_ERROR:
$type = 'Recoverable fatal error';
break;

default:
$type = 'Error';
break;
}

$message = \sprintf('PHP %s:  %s%s on line %d', $type, $message, $filename ? ' in '.$filename : '', $lineno);
parent::__construct($message, $code, $severity, $filename, $lineno, $previous);
}




public function getRawMessage(): string
{
return $this->rawMessage;
}















public static function throwException($errno, $errstr, $errfile, $errline)
{
throw new self($errstr, 0, $errno, $errfile, $errline);
}








public static function fromError(\Error $e)
{
@\trigger_error('PsySH no longer wraps Errors', \E_USER_DEPRECATED);
}
}
<?php










namespace Psy\Exception;




class BreakException extends \Exception implements Exception
{
private $rawMessage;




public function __construct($message = '', $code = 0, \Throwable $previous = null)
{
$this->rawMessage = $message;
parent::__construct(\sprintf('Exit:  %s', $message), $code, $previous);
}




public function getRawMessage(): string
{
return $this->rawMessage;
}








public static function exitShell()
{
throw new self('Goodbye');
}
}
<?php










namespace Psy\Exception;




class ThrowUpException extends \Exception implements Exception
{



public function __construct(\Throwable $throwable)
{
$message = \sprintf("Throwing %s with message '%s'", \get_class($throwable), $throwable->getMessage());
parent::__construct($message, $throwable->getCode(), $throwable);
}




public function getRawMessage(): string
{
return $this->getPrevious()->getMessage();
}








public static function fromThrowable($throwable)
{
@\trigger_error('PsySH no longer wraps Throwables', \E_USER_DEPRECATED);
}
}
<?php










namespace Psy;







interface ContextAware
{





public function setContext(Context $context);
}
<?php










namespace Psy;




class ConfigPaths
{
private $configDir;
private $dataDir;
private $runtimeDir;
private $env;











public function __construct(array $overrides = [], EnvInterface $env = null)
{
$this->overrideDirs($overrides);

$this->env = $env ?: (\PHP_SAPI === 'cli-server' ? new SystemEnv() : new SuperglobalsEnv());
}









public function overrideDirs(array $overrides)
{
if (\array_key_exists('configDir', $overrides)) {
$this->configDir = $overrides['configDir'] ?: null;
}

if (\array_key_exists('dataDir', $overrides)) {
$this->dataDir = $overrides['dataDir'] ?: null;
}

if (\array_key_exists('runtimeDir', $overrides)) {
$this->runtimeDir = $overrides['runtimeDir'] ?: null;
}
}






public function homeDir()
{
if ($homeDir = $this->getEnv('HOME') ?: $this->windowsHomeDir()) {
return \strtr($homeDir, '\\', '/');
}

return null;
}

private function windowsHomeDir()
{
if (\defined('PHP_WINDOWS_VERSION_MAJOR')) {
$homeDrive = $this->getEnv('HOMEDRIVE');
$homePath = $this->getEnv('HOMEPATH');
if ($homeDrive && $homePath) {
return $homeDrive.'/'.$homePath;
}
}

return null;
}

private function homeConfigDir()
{
if ($homeConfigDir = $this->getEnv('XDG_CONFIG_HOME')) {
return $homeConfigDir;
}

$homeDir = $this->homeDir();

return $homeDir === '/' ? $homeDir.'.config' : $homeDir.'/.config';
}











public function configDirs(): array
{
if ($this->configDir !== null) {
return [$this->configDir];
}

$configDirs = $this->getEnvArray('XDG_CONFIG_DIRS') ?: ['/etc/xdg'];

return $this->allDirNames(\array_merge([$this->homeConfigDir()], $configDirs));
}











public function currentConfigDir(): string
{
if ($this->configDir !== null) {
return $this->configDir;
}

$configDirs = $this->allDirNames([$this->homeConfigDir()]);

foreach ($configDirs as $configDir) {
if (@\is_dir($configDir)) {
return $configDir;
}
}

return $configDirs[0];
}








public function configFiles(array $names): array
{
return $this->allRealFiles($this->configDirs(), $names);
}













public function dataDirs(): array
{
if ($this->dataDir !== null) {
return [$this->dataDir];
}

$homeDataDir = $this->getEnv('XDG_DATA_HOME') ?: $this->homeDir().'/.local/share';
$dataDirs = $this->getEnvArray('XDG_DATA_DIRS') ?: ['/usr/local/share', '/usr/share'];

return $this->allDirNames(\array_merge([$homeDataDir], $dataDirs));
}








public function dataFiles(array $names): array
{
return $this->allRealFiles($this->dataDirs(), $names);
}






public function runtimeDir(): string
{
if ($this->runtimeDir !== null) {
return $this->runtimeDir;
}


$runtimeDir = $this->getEnv('XDG_RUNTIME_DIR') ?: \sys_get_temp_dir();

return \strtr($runtimeDir, '\\', '/').'/psysh';
}








public function pathDirs(): array
{
return $this->getEnvArray('PATH') ?: ['/usr/sbin', '/usr/bin', '/sbin', '/bin'];
}











public function which($command)
{
foreach ($this->pathDirs() as $path) {
$fullpath = $path.\DIRECTORY_SEPARATOR.$command;
if (@\is_file($fullpath) && @\is_executable($fullpath)) {
return $fullpath;
}
}

return null;
}












private function allDirNames(array $baseDirs): array
{
$dirs = \array_map(function ($dir) {
return \strtr($dir, '\\', '/').'/psysh';
}, $baseDirs);


if ($home = $this->getEnv('HOME')) {
$dirs[] = \strtr($home, '\\', '/').'/.psysh';
}


if (\defined('PHP_WINDOWS_VERSION_MAJOR')) {
if ($appData = $this->getEnv('APPDATA')) {

\array_unshift($dirs, \strtr($appData, '\\', '/').'/PsySH');
}

if ($windowsHomeDir = $this->windowsHomeDir()) {
$dir = \strtr($windowsHomeDir, '\\', '/').'/.psysh';
if (!\in_array($dir, $dirs)) {
$dirs[] = $dir;
}
}
}

return $dirs;
}







private function allRealFiles(array $dirNames, array $fileNames): array
{
$files = [];
foreach ($dirNames as $dir) {
foreach ($fileNames as $name) {
$file = $dir.'/'.$name;
if (@\is_file($file)) {
$files[] = $file;
}
}
}

return $files;
}










public static function ensureDir(string $dir): bool
{
if (!\is_dir($dir)) {

@\mkdir($dir, 0700, true);
}

if (!\is_dir($dir) || !\is_writable($dir)) {
\trigger_error(\sprintf('Writing to directory %s is not allowed.', $dir), \E_USER_NOTICE);

return false;
}

return true;
}










public static function touchFileWithMkdir(string $file)
{
if (\file_exists($file)) {
if (\is_writable($file)) {
return $file;
}

\trigger_error(\sprintf('Writing to %s is not allowed.', $file), \E_USER_NOTICE);

return false;
}

if (!self::ensureDir(\dirname($file))) {
return false;
}

\touch($file);

return $file;
}

private function getEnv($key)
{
return $this->env->get($key);
}

private function getEnvArray($key)
{
if ($value = $this->getEnv($key)) {
return \explode(\PATH_SEPARATOR, $value);
}

return null;
}
}
<?php










namespace Psy\Input;








class SilentInput
{
private $inputString;






public function __construct(string $inputString)
{
$this->inputString = $inputString;
}




public function __toString(): string
{
return $this->inputString;
}
}
<?php










namespace Psy\Input;

use Psy\Exception\ErrorException;
use Psy\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;




class FilterOptions
{
private $filter = false;
private $pattern;
private $insensitive;
private $invert;






public static function getOptions(): array
{
return [
new InputOption('grep', 'G', InputOption::VALUE_REQUIRED, 'Limit to items matching the given pattern (string or regex).'),
new InputOption('insensitive', 'i', InputOption::VALUE_NONE, 'Case-insensitive search (requires --grep).'),
new InputOption('invert', 'v', InputOption::VALUE_NONE, 'Inverted search (requires --grep).'),
];
}






public function bind(InputInterface $input)
{
$this->validateInput($input);

if (!$pattern = $input->getOption('grep')) {
$this->filter = false;

return;
}

if (!$this->stringIsRegex($pattern)) {
$pattern = '/'.\preg_quote($pattern, '/').'/';
}

if ($insensitive = $input->getOption('insensitive')) {
$pattern .= 'i';
}

$this->validateRegex($pattern);

$this->filter = true;
$this->pattern = $pattern;
$this->insensitive = $insensitive;
$this->invert = $input->getOption('invert');
}




public function hasFilter(): bool
{
return $this->filter;
}







public function match(string $string, array &$matches = null): bool
{
return $this->filter === false || (\preg_match($this->pattern, $string, $matches) xor $this->invert);
}








private function validateInput(InputInterface $input)
{
if (!$input->getOption('grep')) {
foreach (['invert', 'insensitive'] as $option) {
if ($input->getOption($option)) {
throw new RuntimeException('--'.$option.' does not make sense without --grep');
}
}
}
}






private function stringIsRegex(string $string): bool
{
return \substr($string, 0, 1) === '/' && \substr($string, -1) === '/' && \strlen($string) >= 3;
}








private function validateRegex(string $pattern)
{
\set_error_handler([ErrorException::class, 'throwException']);
try {
\preg_match($pattern, '');
} catch (ErrorException $e) {
throw new RuntimeException(\str_replace('preg_match(): ', 'Invalid regular expression: ', $e->getRawMessage()));
} finally {
\restore_error_handler();
}
}
}
<?php










namespace Psy\Input;

use Symfony\Component\Console\Input\InputArgument;















class CodeArgument extends InputArgument
{










public function __construct(string $name, int $mode = null, string $description = '', $default = null)
{
if ($mode & InputArgument::IS_ARRAY) {
throw new \InvalidArgumentException('Argument mode IS_ARRAY is not valid');
}

parent::__construct($name, $mode, $description, $default);
}
}
<?php










namespace Psy\Input;

use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\StringInput;




class ShellInput extends StringInput
{
public const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';

private $hasCodeArgument = false;





private $tokenPairs;
private $parsed;






public function __construct(string $input)
{
parent::__construct($input);

$this->tokenPairs = $this->tokenize($input);
}






public function bind(InputDefinition $definition): void
{
$hasCodeArgument = false;

if ($definition->getArgumentCount() > 0) {
$args = $definition->getArguments();
$lastArg = \array_pop($args);
foreach ($args as $arg) {
if ($arg instanceof CodeArgument) {
$msg = \sprintf('Unexpected CodeArgument before the final position: %s', $arg->getName());
throw new \InvalidArgumentException($msg);
}
}

if ($lastArg instanceof CodeArgument) {
$hasCodeArgument = true;
}
}

$this->hasCodeArgument = $hasCodeArgument;

parent::bind($definition);
}













private function tokenize(string $input): array
{
$tokens = [];
$length = \strlen($input);
$cursor = 0;
while ($cursor < $length) {
if (\preg_match('/\s+/A', $input, $match, 0, $cursor)) {
} elseif (\preg_match('/([^="\'\s]+?)(=?)('.StringInput::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) {
$tokens[] = [
$match[1].$match[2].\stripcslashes(\str_replace(['"\'', '\'"', '\'\'', '""'], '', \substr($match[3], 1, \strlen($match[3]) - 2))),
\stripcslashes(\substr($input, $cursor)),
];
} elseif (\preg_match('/'.StringInput::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$tokens[] = [
\stripcslashes(\substr($match[0], 1, \strlen($match[0]) - 2)),
\stripcslashes(\substr($input, $cursor)),
];
} elseif (\preg_match('/'.self::REGEX_STRING.'/A', $input, $match, 0, $cursor)) {
$tokens[] = [
\stripcslashes($match[1]),
\stripcslashes(\substr($input, $cursor)),
];
} else {


throw new \InvalidArgumentException(\sprintf('Unable to parse input near "... %s ..."', \substr($input, $cursor, 10)));

}

$cursor += \strlen($match[0]);
}

return $tokens;
}




protected function parse(): void
{
$parseOptions = true;
$this->parsed = $this->tokenPairs;
while (null !== $tokenPair = \array_shift($this->parsed)) {


list($token, $rest) = $tokenPair;

if ($parseOptions && '' === $token) {
$this->parseShellArgument($token, $rest);
} elseif ($parseOptions && '--' === $token) {
$parseOptions = false;
} elseif ($parseOptions && 0 === \strpos($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseShellArgument($token, $rest);
}
}
}









private function parseShellArgument(string $token, string $rest)
{
$c = \count($this->arguments);


if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);

if ($arg instanceof CodeArgument) {


$this->parsed = [];
$this->arguments[$arg->getName()] = $rest;
} else {
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
}

return;
}






if ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
$arg = $this->definition->getArgument($c - 1);
$this->arguments[$arg->getName()][] = $token;

return;
}


$all = $this->definition->getArguments();
if (\count($all)) {
throw new \RuntimeException(\sprintf('Too many arguments, expected arguments "%s".', \implode('" "', \array_keys($all))));
}

throw new \RuntimeException(\sprintf('No arguments expected, got "%s".', $token));

}









private function parseShortOption(string $token)
{
$name = \substr($token, 1);

if (\strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {

$this->addShortOption($name[0], \substr($name, 1));
} else {
$this->parseShortOptionSet($name);
}
} else {
$this->addShortOption($name, null);
}
}








private function parseShortOptionSet(string $name)
{
$len = \strlen($name);
for ($i = 0; $i < $len; $i++) {
if (!$this->definition->hasShortcut($name[$i])) {
throw new \RuntimeException(\sprintf('The "-%s" option does not exist.', $name[$i]));
}

$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptValue()) {
$this->addLongOption($option->getName(), $i === $len - 1 ? null : \substr($name, $i + 1));

break;
} else {
$this->addLongOption($option->getName(), null);
}
}
}






private function parseLongOption(string $token)
{
$name = \substr($token, 2);

if (false !== $pos = \strpos($name, '=')) {
if (($value = \substr($name, $pos + 1)) === '') {
\array_unshift($this->parsed, [$value, null]);
}
$this->addLongOption(\substr($name, 0, $pos), $value);
} else {
$this->addLongOption($name, null);
}
}









private function addShortOption(string $shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new \RuntimeException(\sprintf('The "-%s" option does not exist.', $shortcut));
}

$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}









private function addLongOption(string $name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \RuntimeException(\sprintf('The "--%s" option does not exist.', $name));
}

$option = $this->definition->getOption($name);

if (null !== $value && !$option->acceptValue()) {
throw new \RuntimeException(\sprintf('The "--%s" option does not accept a value.', $name));
}

if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {


$next = \array_shift($this->parsed);
$nextToken = $next[0];
if ((isset($nextToken[0]) && '-' !== $nextToken[0]) || \in_array($nextToken, ['', null], true)) {
$value = $nextToken;
} else {
\array_unshift($this->parsed, $next);
}
}

if ($value === null) {
if ($option->isValueRequired()) {
throw new \RuntimeException(\sprintf('The "--%s" option requires a value.', $name));
}

if (!$option->isArray() && !$option->isValueOptional()) {
$value = true;
}
}

if ($option->isArray()) {
$this->options[$name][] = $value;
} else {
$this->options[$name] = $value;
}
}


}
<?php










namespace Psy\Output;

use Symfony\Component\Console\Output\StreamOutput;









class ProcOutputPager extends StreamOutput implements OutputPager
{
private $proc;
private $pipe;
private $stream;
private $cmd;







public function __construct(StreamOutput $output, string $cmd = 'less -R -F -X')
{
$this->stream = $output->getStream();
$this->cmd = $cmd;
}









public function doWrite($message, $newline): void
{
$pipe = $this->getPipe();
if (false === @\fwrite($pipe, $message.($newline ? \PHP_EOL : ''))) {


$this->close();
throw new \RuntimeException('Unable to write output');

}

\fflush($pipe);
}




public function close()
{
if (isset($this->pipe)) {
\fclose($this->pipe);
}

if (isset($this->proc)) {
$exit = \proc_close($this->proc);
if ($exit !== 0) {
throw new \RuntimeException('Error closing output stream');
}
}

$this->pipe = null;
$this->proc = null;
}






private function getPipe()
{
if (!isset($this->pipe) || !isset($this->proc)) {
$desc = [['pipe', 'r'], $this->stream, \fopen('php://stderr', 'w')];
$this->proc = \proc_open($this->cmd, $desc, $pipes);

if (!\is_resource($this->proc)) {
throw new \RuntimeException('Error opening output stream');
}

$this->pipe = $pipes[0];
}

return $this->pipe;
}
}
<?php










namespace Psy\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;




class Theme
{
const MODERN_THEME = []; 

const COMPACT_THEME = [
'compact' => true,
];

const CLASSIC_THEME = [
'compact' => true,

'prompt' => '>>> ',
'bufferPrompt' => '... ',
'replayPrompt' => '--> ',
'returnValue' => '=>  ',
];

const DEFAULT_STYLES = [
'info' => ['white', 'blue', ['bold']],
'warning' => ['black', 'yellow'],
'error' => ['white', 'red', ['bold']],
'whisper' => ['gray'],

'aside' => ['blue'],
'strong' => [null, null, ['bold']],
'return' => ['cyan'],
'urgent' => ['red'],
'hidden' => ['black'],


'public' => [null, null, ['bold']],
'protected' => ['yellow'],
'private' => ['red'],
'global' => ['cyan', null, ['bold']],
'const' => ['cyan'],
'class' => ['blue', null, ['underscore']],
'function' => [null],
'default' => [null],


'number' => ['magenta'],
'integer' => ['magenta'],
'float' => ['yellow'],
'string' => ['green'],
'bool' => ['cyan'],
'keyword' => ['yellow'],
'comment' => ['blue'],
'code_comment' => ['gray'],
'object' => ['blue'],
'resource' => ['yellow'],


'inline_html' => ['cyan'],
];

const ERROR_STYLES = ['info', 'warning', 'error', 'whisper', 'class'];

private $compact = false;

private $prompt = '> ';
private $bufferPrompt = '. ';
private $replayPrompt = '- ';
private $returnValue = '= ';

private $grayFallback = 'blue';

private $styles = [];




public function __construct($config = 'modern')
{
if (\is_string($config)) {
switch ($config) {
case 'modern':
$config = static::MODERN_THEME;
break;

case 'compact':
$config = static::COMPACT_THEME;
break;

case 'classic':
$config = static::CLASSIC_THEME;
break;

default:
\trigger_error(\sprintf('Unknown theme: %s', $config), \E_USER_NOTICE);
$config = static::MODERN_THEME;
break;
}
}

if (!\is_array($config)) {
throw new \InvalidArgumentException('Invalid theme config');
}

foreach ($config as $name => $value) {
switch ($name) {
case 'compact':
$this->setCompact($value);
break;

case 'prompt':
$this->setPrompt($value);
break;

case 'bufferPrompt':
$this->setBufferPrompt($value);
break;

case 'replayPrompt':
$this->setReplayPrompt($value);
break;

case 'returnValue':
$this->setReturnValue($value);
break;

case 'grayFallback':
$this->setGrayFallback($value);
break;
}
}

$this->setStyles($config['styles'] ?? []);
}




public function setCompact(bool $compact)
{
$this->compact = $compact;
}




public function compact(): bool
{
return $this->compact;
}




public function setPrompt(string $prompt)
{
$this->prompt = $prompt;
}




public function prompt(): string
{
return $this->prompt;
}




public function setBufferPrompt(string $bufferPrompt)
{
$this->bufferPrompt = $bufferPrompt;
}




public function bufferPrompt(): string
{
return $this->bufferPrompt;
}




public function setReplayPrompt(string $replayPrompt)
{
$this->replayPrompt = $replayPrompt;
}




public function replayPrompt(): string
{
return $this->replayPrompt;
}




public function setReturnValue(string $returnValue)
{
$this->returnValue = $returnValue;
}




public function returnValue(): string
{
return $this->returnValue;
}




public function setGrayFallback(string $grayFallback)
{
$this->grayFallback = $grayFallback;
}













public function setStyles(array $styles)
{
foreach (\array_keys(static::DEFAULT_STYLES) as $name) {
$this->styles[$name] = $styles[$name] ?? static::DEFAULT_STYLES[$name];
}
}




public function applyStyles(OutputFormatterInterface $formatter, bool $useGrayFallback)
{
foreach (\array_keys(static::DEFAULT_STYLES) as $name) {
$formatter->setStyle($name, new OutputFormatterStyle(...$this->getStyle($name, $useGrayFallback)));
}
}




public function applyErrorStyles(OutputFormatterInterface $errorFormatter, bool $useGrayFallback)
{
foreach (static::ERROR_STYLES as $name) {
$errorFormatter->setStyle($name, new OutputFormatterStyle(...$this->getStyle($name, $useGrayFallback)));
}
}

private function getStyle(string $name, bool $useGrayFallback): array
{
return \array_map(function ($style) use ($useGrayFallback) {
return ($useGrayFallback && $style === 'gray') ? $this->grayFallback : $style;
}, $this->styles[$name]);
}
}
<?php










namespace Psy\Output;

use Symfony\Component\Console\Output\StreamOutput;





class PassthruPager extends StreamOutput implements OutputPager
{





public function __construct(StreamOutput $output)
{
parent::__construct($output->getStream());
}




public function close()
{

}
}
<?php










namespace Psy\Output;

use Symfony\Component\Console\Output\OutputInterface;





interface OutputPager extends OutputInterface
{



public function close();
}
<?php










namespace Psy\Output;

use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Output\ConsoleOutput;




class ShellOutput extends ConsoleOutput
{
const NUMBER_LINES = 128;

private $paging = 0;


private $pager;


private $theme;









public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null, $pager = null, $theme = null)
{
parent::__construct($verbosity, $decorated, $formatter);

$this->theme = $theme ?? new Theme('modern');
$this->initFormatters();

if ($pager === null) {
$this->pager = new PassthruPager($this);
} elseif (\is_string($pager)) {
$this->pager = new ProcOutputPager($this, $pager);
} elseif ($pager instanceof OutputPager) {
$this->pager = $pager;
} else {
throw new \InvalidArgumentException('Unexpected pager parameter: '.$pager);
}
}














public function page($messages, int $type = 0)
{
if (\is_string($messages)) {
$messages = (array) $messages;
}

if (!\is_array($messages) && !\is_callable($messages)) {
throw new \InvalidArgumentException('Paged output requires a string, array or callback');
}

$this->startPaging();

if (\is_callable($messages)) {
$messages($this);
} else {
$this->write($messages, true, $type);
}

$this->stopPaging();
}




public function startPaging()
{
$this->paging++;
}




public function stopPaging()
{
$this->paging--;
$this->closePager();
}













public function write($messages, $newline = false, $type = 0): void
{
if ($this->getVerbosity() === self::VERBOSITY_QUIET) {
return;
}

$messages = (array) $messages;

if ($type & self::NUMBER_LINES) {
$pad = \strlen((string) \count($messages));
$template = $this->isDecorated() ? "<aside>%{$pad}s</aside>: %s" : "%{$pad}s: %s";

if ($type & self::OUTPUT_RAW) {
$messages = \array_map([OutputFormatter::class, 'escape'], $messages);
}

foreach ($messages as $i => $line) {
$messages[$i] = \sprintf($template, $i, $line);
}


$type = $type & ~self::NUMBER_LINES & ~self::OUTPUT_RAW;
}

parent::write($messages, $newline, $type);
}









public function doWrite($message, $newline): void
{
if ($this->paging > 0) {
$this->pager->doWrite($message, $newline);
} else {
parent::doWrite($message, $newline);
}
}




public function setTheme(Theme $theme)
{
$this->theme = $theme;
$this->initFormatters();
}




private function closePager()
{
if ($this->paging <= 0) {
$this->pager->close();
}
}




private function initFormatters()
{
$useGrayFallback = !$this->grayExists();
$this->theme->applyStyles($this->getFormatter(), $useGrayFallback);
$this->theme->applyErrorStyles($this->getErrorOutput()->getFormatter(), $useGrayFallback);
}




private function grayExists(): bool
{
try {
$this->write('<fg=gray></>');
} catch (\InvalidArgumentException $e) {
return false;
}

return true;
}
}
<?php

declare(strict_types=1);

namespace Jean85;

class Version
{
private const SHORT_COMMIT_LENGTH = 7;


private $packageName;


private $prettyVersion;


private $reference;


private $versionIsTagged;

public const NO_VERSION_TEXT = '{no version}';
public const NO_REFERENCE_TEXT = '{no reference}';

public function __construct(string $packageName, ?string $prettyVersion = null, ?string $reference = null)
{
$this->packageName = $packageName;
$this->prettyVersion = $prettyVersion ?? self::NO_VERSION_TEXT;
$this->reference = $reference ?? self::NO_REFERENCE_TEXT;
$this->versionIsTagged = preg_match('/[^v\d.]/', $this->getShortVersion()) === 0;
}

public function getPrettyVersion(): string
{
if ($this->versionIsTagged) {
return $this->prettyVersion;
}

return $this->getVersionWithShortReference();
}

public function getFullVersion(): string
{
return $this->prettyVersion . '@' . $this->getReference();
}




public function getVersionWithShortCommit(): string
{
return $this->getVersionWithShortReference();
}

public function getVersionWithShortReference(): string
{
return $this->prettyVersion . '@' . $this->getShortReference();
}

public function getPackageName(): string
{
return $this->packageName;
}

public function getShortVersion(): string
{
return $this->prettyVersion;
}




public function getCommitHash(): string
{
return $this->getReference();
}

public function getReference(): string
{
return $this->reference;
}




public function getShortCommitHash(): string
{
return $this->getShortReference();
}

public function getShortReference(): string
{
return substr($this->reference, 0, self::SHORT_COMMIT_LENGTH);
}

public function __toString(): string
{
return $this->getPrettyVersion();
}
}
<?php

declare(strict_types=1);

namespace Jean85;

use Composer\InstalledVersions;
use Jean85\Exception\ProvidedPackageException;
use Jean85\Exception\ReplacedPackageException;
use Jean85\Exception\VersionMissingExceptionInterface;

class PrettyVersions
{



public static function getVersion(string $packageName): Version
{
self::checkProvidedPackages($packageName);

self::checkReplacedPackages($packageName);

return new Version(
$packageName,
InstalledVersions::getPrettyVersion($packageName),
InstalledVersions::getReference($packageName)
);
}

public static function getRootPackageName(): string
{
return InstalledVersions::getRootPackage()['name'];
}

public static function getRootPackageVersion(): Version
{
return new Version(
self::getRootPackageName(),
InstalledVersions::getRootPackage()['pretty_version'],
InstalledVersions::getRootPackage()['reference']
);
}

protected static function checkProvidedPackages(string $packageName): void
{
if (! method_exists(InstalledVersions::class, 'getAllRawData')) {
if (isset(InstalledVersions::getRawData()['versions'][$packageName]['provided'])) {
throw ProvidedPackageException::create($packageName);
}

return;
}

foreach (InstalledVersions::getAllRawData() as $installed) {
if (isset($installed['versions'][$packageName]['provided'])) {
throw ProvidedPackageException::create($packageName);
}
}
}

protected static function checkReplacedPackages(string $packageName): void
{
if (! method_exists(InstalledVersions::class, 'getAllRawData')) {
if (isset(InstalledVersions::getRawData()['versions'][$packageName]['replaced'])) {
throw ReplacedPackageException::create($packageName);
}

return;
}

foreach (InstalledVersions::getAllRawData() as $installed) {
if (isset($installed['versions'][$packageName]['replaced'])) {
throw ReplacedPackageException::create($packageName);
}
}
}
}
<?php

declare(strict_types=1);

namespace Jean85\Exception;

interface VersionMissingExceptionInterface extends \Throwable
{
public static function create(string $packageName): self;
}
<?php

declare(strict_types=1);

namespace Jean85\Exception;

class ReplacedPackageException extends \Exception implements VersionMissingExceptionInterface
{
public static function create(string $packageName): VersionMissingExceptionInterface
{
return new self('Cannot retrieve a version for package ' . $packageName . ' since it is replaced by some other package');
}
}
<?php

declare(strict_types=1);

namespace Jean85\Exception;

class ProvidedPackageException extends \Exception implements VersionMissingExceptionInterface
{
public static function create(string $packageName): VersionMissingExceptionInterface
{
return new self('Cannot retrieve a version for package ' . $packageName . ' since it is provided, probably a metapackage');
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Interactions\Touch\WebDriverTouchAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;

class WebDriverUpAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;





public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}

public function perform()
{
$this->touchScreen->up($this->x, $this->y);
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;




class WebDriverTimeouts
{



protected $executor;



protected $isW3cCompliant;

public function __construct(ExecuteMethod $executor, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->isW3cCompliant = $isW3cCompliant;
}







public function implicitlyWait($seconds)
{
if ($this->isW3cCompliant) {
$this->executor->execute(
DriverCommand::IMPLICITLY_WAIT,
['implicit' => $seconds * 1000]
);

return $this;
}

$this->executor->execute(
DriverCommand::IMPLICITLY_WAIT,
['ms' => $seconds * 1000]
);

return $this;
}







public function setScriptTimeout($seconds)
{
if ($this->isW3cCompliant) {
$this->executor->execute(
DriverCommand::SET_SCRIPT_TIMEOUT,
['script' => $seconds * 1000]
);

return $this;
}

$this->executor->execute(
DriverCommand::SET_SCRIPT_TIMEOUT,
['ms' => $seconds * 1000]
);

return $this;
}







public function pageLoadTimeout($seconds)
{
if ($this->isW3cCompliant) {
$this->executor->execute(
DriverCommand::SET_SCRIPT_TIMEOUT,
['pageLoad' => $seconds * 1000]
);

return $this;
}

$this->executor->execute(DriverCommand::SET_TIMEOUT, [
'type' => 'page load',
'ms' => $seconds * 1000,
]);

return $this;
}
}
<?php

namespace Facebook\WebDriver\Interactions;

use Facebook\WebDriver\Interactions\Internal\WebDriverButtonReleaseAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverClickAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverClickAndHoldAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverContextClickAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverDoubleClickAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverKeyDownAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverKeyUpAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverMouseMoveAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverMoveToOffsetAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverSendKeysAction;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverHasInputDevices;




class WebDriverActions
{
protected $driver;
protected $keyboard;
protected $mouse;
protected $action;

public function __construct(WebDriverHasInputDevices $driver)
{
$this->driver = $driver;
$this->keyboard = $driver->getKeyboard();
$this->mouse = $driver->getMouse();
$this->action = new WebDriverCompositeAction();
}




public function perform()
{
$this->action->perform();
}







public function click(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverClickAction($this->mouse, $element)
);

return $this;
}







public function clickAndHold(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $element)
);

return $this;
}







public function contextClick(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverContextClickAction($this->mouse, $element)
);

return $this;
}







public function doubleClick(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverDoubleClickAction($this->mouse, $element)
);

return $this;
}






public function dragAndDrop(WebDriverElement $source, WebDriverElement $target)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $source)
);
$this->action->addAction(
new WebDriverMouseMoveAction($this->mouse, $target)
);
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, $target)
);

return $this;
}








public function dragAndDropBy(WebDriverElement $source, $x_offset, $y_offset)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $source)
);
$this->action->addAction(
new WebDriverMoveToOffsetAction($this->mouse, null, $x_offset, $y_offset)
);
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, null)
);

return $this;
}








public function moveByOffset($x_offset, $y_offset)
{
$this->action->addAction(
new WebDriverMoveToOffsetAction($this->mouse, null, $x_offset, $y_offset)
);

return $this;
}










public function moveToElement(WebDriverElement $element, $x_offset = null, $y_offset = null)
{
$this->action->addAction(new WebDriverMoveToOffsetAction(
$this->mouse,
$element,
$x_offset,
$y_offset
));

return $this;
}







public function release(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, $element)
);

return $this;
}









public function keyDown(WebDriverElement $element = null, $key = null)
{
$this->action->addAction(
new WebDriverKeyDownAction($this->keyboard, $this->mouse, $element, $key)
);

return $this;
}









public function keyUp(WebDriverElement $element = null, $key = null)
{
$this->action->addAction(
new WebDriverKeyUpAction($this->keyboard, $this->mouse, $element, $key)
);

return $this;
}









public function sendKeys(WebDriverElement $element = null, $keys = null)
{
$this->action->addAction(
new WebDriverSendKeysAction(
$this->keyboard,
$this->mouse,
$element,
$keys
)
);

return $this;
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\WebDriverAction;

class WebDriverDoubleClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->doubleClick($this->getActionLocation());
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\WebDriverPoint;




class WebDriverCoordinates
{




private $onScreen;



private $inViewPort;



private $onPage;



private $auxiliary;





public function __construct($on_screen, callable $in_view_port, callable $on_page, $auxiliary)
{
$this->onScreen = $on_screen;
$this->inViewPort = $in_view_port;
$this->onPage = $on_page;
$this->auxiliary = $auxiliary;
}





public function onScreen()
{
throw new UnsupportedOperationException(
'onScreen is planned but not yet supported by Selenium'
);
}




public function inViewPort()
{
return call_user_func($this->inViewPort);
}




public function onPage()
{
return call_user_func($this->onPage);
}




public function getAuxiliary()
{
return $this->auxiliary;
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\WebDriverAction;

class WebDriverClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->click($this->getActionLocation());
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverMouse;




abstract class WebDriverKeysRelatedAction
{



protected $keyboard;



protected $mouse;



protected $locationProvider;

public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null
) {
$this->keyboard = $keyboard;
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
}

protected function focusOnElement()
{
if ($this->locationProvider) {
$this->mouse->click($this->locationProvider->getCoordinates());
}
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverKeys;
use Facebook\WebDriver\WebDriverMouse;

abstract class WebDriverSingleKeyAction extends WebDriverKeysRelatedAction implements WebDriverAction
{
public const MODIFIER_KEYS = [
WebDriverKeys::SHIFT,
WebDriverKeys::LEFT_SHIFT,
WebDriverKeys::RIGHT_SHIFT,
WebDriverKeys::CONTROL,
WebDriverKeys::LEFT_CONTROL,
WebDriverKeys::RIGHT_CONTROL,
WebDriverKeys::ALT,
WebDriverKeys::LEFT_ALT,
WebDriverKeys::RIGHT_ALT,
WebDriverKeys::META,
WebDriverKeys::RIGHT_META,
WebDriverKeys::COMMAND,
];


protected $key;





public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$key = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);

if (!in_array($key, self::MODIFIER_KEYS, true)) {
throw LogicException::forError(
sprintf(
'keyDown / keyUp actions can only be used for modifier keys, but "%s" was given',
$key
)
);
}
$this->key = $key;
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\WebDriverAction;




class WebDriverContextClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->contextClick($this->getActionLocation());
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverMouse;




class WebDriverMouseAction
{



protected $mouse;



protected $locationProvider;

public function __construct(WebDriverMouse $mouse, WebDriverLocatable $location_provider = null)
{
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
}




protected function getActionLocation()
{
if ($this->locationProvider !== null) {
return $this->locationProvider->getCoordinates();
}

return null;
}

protected function moveToLocation()
{
$this->mouse->mouseMove($this->locationProvider);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\WebDriverAction;




class WebDriverClickAndHoldAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseDown($this->getActionLocation());
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\WebDriverAction;




class WebDriverButtonReleaseAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseUp($this->getActionLocation());
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

class WebDriverKeyDownAction extends WebDriverSingleKeyAction
{
public function perform()
{
$this->focusOnElement();
$this->keyboard->pressKey($this->key);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverMouse;

class WebDriverSendKeysAction extends WebDriverKeysRelatedAction implements WebDriverAction
{



private $keys = '';




public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$keys = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
$this->keys = $keys;
}

public function perform()
{
$this->focusOnElement();
$this->keyboard->sendKeys($this->keys);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

class WebDriverKeyUpAction extends WebDriverSingleKeyAction
{
public function perform()
{
$this->focusOnElement();
$this->keyboard->releaseKey($this->key);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverMouse;

class WebDriverMoveToOffsetAction extends WebDriverMouseAction implements WebDriverAction
{



private $xOffset;



private $yOffset;





public function __construct(
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$x_offset = null,
$y_offset = null
) {
parent::__construct($mouse, $location_provider);
$this->xOffset = $x_offset;
$this->yOffset = $y_offset;
}

public function perform()
{
$this->mouse->mouseMove(
$this->getActionLocation(),
$this->xOffset,
$this->yOffset
);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Internal;

use Facebook\WebDriver\WebDriverAction;

class WebDriverMouseMoveAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseMove($this->getActionLocation());
}
}
<?php

namespace Facebook\WebDriver\Interactions;

use Facebook\WebDriver\Interactions\Touch\WebDriverDoubleTapAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverDownAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverFlickAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverFlickFromElementAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverLongPressAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverMoveAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverScrollAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverScrollFromElementAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverTapAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;
use Facebook\WebDriver\WebDriver;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverUpAction;




class WebDriverTouchActions extends WebDriverActions
{



protected $touchScreen;

public function __construct(WebDriver $driver)
{
parent::__construct($driver);
$this->touchScreen = $driver->getTouch();
}




public function tap(WebDriverElement $element)
{
$this->action->addAction(
new WebDriverTapAction($this->touchScreen, $element)
);

return $this;
}






public function down($x, $y)
{
$this->action->addAction(
new WebDriverDownAction($this->touchScreen, $x, $y)
);

return $this;
}






public function up($x, $y)
{
$this->action->addAction(
new WebDriverUpAction($this->touchScreen, $x, $y)
);

return $this;
}






public function move($x, $y)
{
$this->action->addAction(
new WebDriverMoveAction($this->touchScreen, $x, $y)
);

return $this;
}






public function scroll($x, $y)
{
$this->action->addAction(
new WebDriverScrollAction($this->touchScreen, $x, $y)
);

return $this;
}






public function scrollFromElement(WebDriverElement $element, $x, $y)
{
$this->action->addAction(
new WebDriverScrollFromElementAction($this->touchScreen, $element, $x, $y)
);

return $this;
}




public function doubleTap(WebDriverElement $element)
{
$this->action->addAction(
new WebDriverDoubleTapAction($this->touchScreen, $element)
);

return $this;
}




public function longPress(WebDriverElement $element)
{
$this->action->addAction(
new WebDriverLongPressAction($this->touchScreen, $element)
);

return $this;
}






public function flick($x, $y)
{
$this->action->addAction(
new WebDriverFlickAction($this->touchScreen, $x, $y)
);

return $this;
}







public function flickFromElement(WebDriverElement $element, $x, $y, $speed)
{
$this->action->addAction(
new WebDriverFlickFromElementAction(
$this->touchScreen,
$element,
$x,
$y,
$speed
)
);

return $this;
}
}
<?php

namespace Facebook\WebDriver\Interactions;

use Facebook\WebDriver\WebDriverAction;




class WebDriverCompositeAction implements WebDriverAction
{



private $actions = [];






public function addAction(WebDriverAction $action)
{
$this->actions[] = $action;

return $this;
}






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




public function perform()
{
foreach ($this->actions as $action) {
$action->perform();
}
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverElement;

class WebDriverScrollFromElementAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;





public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverElement $element,
$x,
$y
) {
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen, $element);
}

public function perform()
{
$this->touchScreen->scrollFromElement(
$this->locationProvider,
$this->x,
$this->y
);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverMoveAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;





public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}

public function perform()
{
$this->touchScreen->move($this->x, $this->y);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverDownAction extends WebDriverTouchAction implements WebDriverAction
{



private $x;



private $y;





public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}

public function perform()
{
$this->touchScreen->down($this->x, $this->y);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverDoubleTapAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->doubleTap($this->locationProvider);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverFlickAction extends WebDriverTouchAction implements WebDriverAction
{



private $x;



private $y;





public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}

public function perform()
{
$this->touchScreen->flick($this->x, $this->y);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverScrollAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;





public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}

public function perform()
{
$this->touchScreen->scroll($this->x, $this->y);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverElement;




interface WebDriverTouchScreen
{





public function tap(WebDriverElement $element);






public function doubleTap(WebDriverElement $element);








public function down($x, $y);









public function flick($xspeed, $yspeed);










public function flickFromElement(
WebDriverElement $element,
$xoffset,
$yoffset,
$speed
);






public function longPress(WebDriverElement $element);








public function move($x, $y);









public function scroll($xoffset, $yoffset);









public function scrollFromElement(
WebDriverElement $element,
$xoffset,
$yoffset
);








public function up($x, $y);
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverElement;

class WebDriverFlickFromElementAction extends WebDriverTouchAction implements WebDriverAction
{



private $x;



private $y;



private $speed;






public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverElement $element,
$x,
$y,
$speed
) {
$this->x = $x;
$this->y = $y;
$this->speed = $speed;
parent::__construct($touch_screen, $element);
}

public function perform()
{
$this->touchScreen->flickFromElement(
$this->locationProvider,
$this->x,
$this->y,
$this->speed
);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
use Facebook\WebDriver\Internal\WebDriverLocatable;




abstract class WebDriverTouchAction
{



protected $touchScreen;



protected $locationProvider;

public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverLocatable $location_provider = null
) {
$this->touchScreen = $touch_screen;
$this->locationProvider = $location_provider;
}




protected function getActionLocation()
{
return $this->locationProvider !== null
? $this->locationProvider->getCoordinates() : null;
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverLongPressAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->longPress($this->locationProvider);
}
}
<?php

namespace Facebook\WebDriver\Interactions\Touch;

use Facebook\WebDriver\WebDriverAction;

class WebDriverTapAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->tap($this->locationProvider);
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Support\Events\EventFiringWebDriver;

class WebDriverDispatcher
{



protected $listeners = [];



protected $driver;







public function setDefaultDriver(EventFiringWebDriver $driver)
{
$this->driver = $driver;

return $this;
}




public function getDefaultDriver()
{
return $this->driver;
}




public function register(WebDriverEventListener $listener)
{
$this->listeners[] = $listener;

return $this;
}




public function unregister(WebDriverEventListener $listener)
{
$key = array_search($listener, $this->listeners, true);
if ($key !== false) {
unset($this->listeners[$key]);
}

return $this;
}






public function dispatch($method, $arguments)
{
foreach ($this->listeners as $listener) {
call_user_func_array([$listener, $method], $arguments);
}

return $this;
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\TimeoutException;






class WebDriverWait
{



protected $driver;



protected $timeout;



protected $interval;

public function __construct(WebDriver $driver, $timeout_in_second = null, $interval_in_millisecond = null)
{
$this->driver = $driver;
$this->timeout = $timeout_in_second ?? 30;
$this->interval = $interval_in_millisecond ?: 250;
}












public function until($func_or_ec, $message = '')
{
$end = microtime(true) + $this->timeout;
$last_exception = null;

while ($end > microtime(true)) {
try {
if ($func_or_ec instanceof WebDriverExpectedCondition) {
$ret_val = call_user_func($func_or_ec->getApply(), $this->driver);
} else {
$ret_val = call_user_func($func_or_ec, $this->driver);
}
if ($ret_val) {
return $ret_val;
}
} catch (NoSuchElementException $e) {
$last_exception = $e;
}
usleep($this->interval * 1000);
}

if ($last_exception) {
throw $last_exception;
}

throw new TimeoutException($message);
}
}
<?php

namespace Facebook\WebDriver;






class WebDriverPlatform
{
public const ANDROID = 'ANDROID';

public const ANY = 'ANY';
public const LINUX = 'LINUX';
public const MAC = 'MAC';
public const UNIX = 'UNIX';
public const VISTA = 'VISTA';
public const WINDOWS = 'WINDOWS';
public const XP = 'XP';

private function __construct()
{
}
}
<?php

namespace Facebook\WebDriver;




interface WebDriverAction
{
public function perform();
}
<?php

namespace Facebook\WebDriver\Local;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;





abstract class LocalWebDriver extends RemoteWebDriver
{

public static function create(
$selenium_server_url = 'http://localhost:4444/wd/hub',
$desired_capabilities = null,
$connection_timeout_in_ms = null,
$request_timeout_in_ms = null,
$http_proxy = null,
$http_proxy_port = null,
DesiredCapabilities $required_capabilities = null
) {
throw LogicException::forError('Use start() method to start local WebDriver.');
}


public static function createBySessionID(
$session_id,
$selenium_server_url = 'http://localhost:4444/wd/hub',
$connection_timeout_in_ms = null,
$request_timeout_in_ms = null
) {
throw LogicException::forError('Use start() method to start local WebDriver.');
}
}
<?php

namespace Facebook\WebDriver;

interface WebDriverKeyboard
{






public function sendKeys($keys);








public function pressKey($key);








public function releaseKey($key);
}
<?php

namespace Facebook\WebDriver;

interface WebDriverCapabilities
{



public function getBrowserName();





public function getCapability($name);




public function getPlatform();




public function getVersion();





public function is($capability_name);






public function isJavascriptEnabled();






}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\IndexOutOfBoundsException;
use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;




class WebDriverWindow
{



protected $executor;



protected $isW3cCompliant;

public function __construct(ExecuteMethod $executor, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->isW3cCompliant = $isW3cCompliant;
}







public function getPosition()
{
$position = $this->executor->execute(
DriverCommand::GET_WINDOW_POSITION,
[':windowHandle' => 'current']
);

return new WebDriverPoint(
$position['x'],
$position['y']
);
}







public function getSize()
{
$size = $this->executor->execute(
DriverCommand::GET_WINDOW_SIZE,
[':windowHandle' => 'current']
);

return new WebDriverDimension(
$size['width'],
$size['height']
);
}






public function minimize()
{
if (!$this->isW3cCompliant) {
throw new UnsupportedOperationException('Minimize window is only supported in W3C mode');
}

$this->executor->execute(DriverCommand::MINIMIZE_WINDOW, []);

return $this;
}






public function maximize()
{
if ($this->isW3cCompliant) {
$this->executor->execute(DriverCommand::MAXIMIZE_WINDOW, []);
} else {
$this->executor->execute(
DriverCommand::MAXIMIZE_WINDOW,
[':windowHandle' => 'current']
);
}

return $this;
}






public function fullscreen()
{
if (!$this->isW3cCompliant) {
throw new UnsupportedOperationException('The Fullscreen window command is only supported in W3C mode');
}

$this->executor->execute(DriverCommand::FULLSCREEN_WINDOW, []);

return $this;
}







public function setSize(WebDriverDimension $size)
{
$params = [
'width' => $size->getWidth(),
'height' => $size->getHeight(),
':windowHandle' => 'current',
];
$this->executor->execute(DriverCommand::SET_WINDOW_SIZE, $params);

return $this;
}







public function setPosition(WebDriverPoint $position)
{
$params = [
'x' => $position->getX(),
'y' => $position->getY(),
':windowHandle' => 'current',
];
$this->executor->execute(DriverCommand::SET_WINDOW_POSITION, $params);

return $this;
}






public function getScreenOrientation()
{
return $this->executor->execute(DriverCommand::GET_SCREEN_ORIENTATION);
}









public function setScreenOrientation($orientation)
{
$orientation = mb_strtoupper($orientation);
if (!in_array($orientation, ['PORTRAIT', 'LANDSCAPE'], true)) {
throw LogicException::forError('Orientation must be either PORTRAIT, or LANDSCAPE');
}

$this->executor->execute(
DriverCommand::SET_SCREEN_ORIENTATION,
['orientation' => $orientation]
);

return $this;
}
}
/*
 * Imported from WebdriverIO project.
 * https://github.com/webdriverio/webdriverio/blob/main/packages/webdriverio/src/scripts/isElementDisplayed.ts
 *
 * Copyright (C) 2017 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * check if element is visible
 * @param  {HTMLElement} elem  element to check
 * @return {Boolean}           true if element is within viewport
 */
function isElementDisplayed(element) {
    function nodeIsElement(node) {
        if (!node) {
            return false;
        }

        switch (node.nodeType) {
            case Node.ELEMENT_NODE:
            case Node.DOCUMENT_NODE:
            case Node.DOCUMENT_FRAGMENT_NODE:
                return true;
            default:
                return false;
        }
    }
    function parentElementForElement(element) {
        if (!element) {
            return null;
        }
        return enclosingNodeOrSelfMatchingPredicate(element.parentNode, nodeIsElement);
    }
    function enclosingNodeOrSelfMatchingPredicate(targetNode, predicate) {
        for (let node = targetNode; node && node !== targetNode.ownerDocument; node = node.parentNode)
            if (predicate(node)) {
                return node;
            }
        return null;
    }
    function enclosingElementOrSelfMatchingPredicate(targetElement, predicate) {
        for (let element = targetElement; element && element !== targetElement.ownerDocument; element = parentElementForElement(element))
            if (predicate(element)) {
                return element;
            }
        return null;
    }
    function cascadedStylePropertyForElement(element, property) {
        if (!element || !property) {
            return null;
        }
        // if document-fragment, skip it and use element.host instead. This happens
        // when the element is inside a shadow root.
        // window.getComputedStyle errors on document-fragment.
        if (element instanceof ShadowRoot) {
            element = element.host;
        }
        let computedStyle = window.getComputedStyle(element);
        let computedStyleProperty = computedStyle.getPropertyValue(property);
        if (computedStyleProperty && computedStyleProperty !== 'inherit') {
            return computedStyleProperty;
        }
        // Ideally getPropertyValue would return the 'used' or 'actual' value, but
        // it doesn't for legacy reasons. So we need to do our own poor man's cascade.
        // Fall back to the first non-'inherit' value found in an ancestor.
        // In any case, getPropertyValue will not return 'initial'.
        // FIXME: will this incorrectly inherit non-inheritable CSS properties?
        // I think all important non-inheritable properties (width, height, etc.)
        // for our purposes here are specially resolved, so this may not be an issue.
        // Specification is here: https://drafts.csswg.org/cssom/#resolved-values
        let parentElement = parentElementForElement(element);
        return cascadedStylePropertyForElement(parentElement, property);
    }
    function elementSubtreeHasNonZeroDimensions(element) {
        let boundingBox = element.getBoundingClientRect();
        if (boundingBox.width > 0 && boundingBox.height > 0) {
            return true;
        }
        // Paths can have a zero width or height. Treat them as shown if the stroke width is positive.
        if (element.tagName.toUpperCase() === 'PATH' && boundingBox.width + boundingBox.height > 0) {
            let strokeWidth = cascadedStylePropertyForElement(element, 'stroke-width');
            return !!strokeWidth && (parseInt(strokeWidth, 10) > 0);
        }
        let cascadedOverflow = cascadedStylePropertyForElement(element, 'overflow');
        if (cascadedOverflow === 'hidden') {
            return false;
        }
        // If the container's overflow is not hidden and it has zero size, consider the
        // container to have non-zero dimensions if a child node has non-zero dimensions.
        return Array.from(element.childNodes).some((childNode) => {
            if (childNode.nodeType === Node.TEXT_NODE) {
                return true;
            }
            if (nodeIsElement(childNode)) {
                return elementSubtreeHasNonZeroDimensions(childNode);
            }
            return false;
        });
    }
    function elementOverflowsContainer(element) {
        let cascadedOverflow = cascadedStylePropertyForElement(element, 'overflow');
        if (cascadedOverflow !== 'hidden') {
            return false;
        }
        // FIXME: this needs to take into account the scroll position of the element,
        // the display modes of it and its ancestors, and the container it overflows.
        // See Selenium's bot.dom.getOverflowState atom for an exhaustive list of edge cases.
        return true;
    }
    function isElementSubtreeHiddenByOverflow(element) {
        if (!element) {
            return false;
        }
        if (!elementOverflowsContainer(element)) {
            return false;
        }
        if (!element.childNodes.length) {
            return false;
        }
        // This element's subtree is hidden by overflow if all child subtrees are as well.
        return Array.from(element.childNodes).every((childNode) => {
            // Returns true if the child node is overflowed or otherwise hidden.
            // Base case: not an element, has zero size, scrolled out, or doesn't overflow container.
            // Visibility of text nodes is controlled by parent
            if (childNode.nodeType === Node.TEXT_NODE) {
                return false;
            }
            if (!nodeIsElement(childNode)) {
                return true;
            }
            if (!elementSubtreeHasNonZeroDimensions(childNode)) {
                return true;
            }
            // Recurse.
            return isElementSubtreeHiddenByOverflow(childNode);
        });
    }
    // walk up the tree testing for a shadow root
    function isElementInsideShadowRoot(element) {
        if (!element) {
            return false;
        }
        if (element.parentNode && element.parentNode.host) {
            return true;
        }
        return isElementInsideShadowRoot(element.parentNode);
    }
    // This is a partial reimplementation of Selenium's "element is displayed" algorithm.
    // When the W3C specification's algorithm stabilizes, we should implement that.
    // If this command is misdirected to the wrong document (and is NOT inside a shadow root), treat it as not shown.
    if (!isElementInsideShadowRoot(element) && !document.contains(element)) {
        return false;
    }
    // Special cases for specific tag names.
    switch (element.tagName.toUpperCase()) {
        case 'BODY':
            return true;
        case 'SCRIPT':
        case 'NOSCRIPT':
            return false;
        case 'OPTGROUP':
        case 'OPTION': {
            // Option/optgroup are considered shown if the containing <select> is shown.
            let enclosingSelectElement = enclosingNodeOrSelfMatchingPredicate(element, (e) => e.tagName.toUpperCase() === 'SELECT');
            return isElementDisplayed(enclosingSelectElement);
        }
        case 'INPUT':
            // <input type="hidden"> is considered not shown.
            if (element.type === 'hidden') {
                return false;
            }
            break;
        // case 'MAP':
        // FIXME: Selenium has special handling for <map> elements. We don't do anything now.
        default:
            break;
    }
    if (cascadedStylePropertyForElement(element, 'visibility') !== 'visible') {
        return false;
    }
    let hasAncestorWithZeroOpacity = !!enclosingElementOrSelfMatchingPredicate(element, (e) => {
        return Number(cascadedStylePropertyForElement(e, 'opacity')) === 0;
    });
    let hasAncestorWithDisplayNone = !!enclosingElementOrSelfMatchingPredicate(element, (e) => {
        return cascadedStylePropertyForElement(e, 'display') === 'none';
    });
    if (hasAncestorWithZeroOpacity || hasAncestorWithDisplayNone) {
        return false;
    }
    if (!elementSubtreeHasNonZeroDimensions(element)) {
        return false;
    }
    if (isElementSubtreeHiddenByOverflow(element)) {
        return false;
    }
    return true;
}

<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\InvalidElementStateException;
use Facebook\WebDriver\Exception\UnsupportedOperationException;




class WebDriverRadios extends AbstractWebDriverCheckboxOrRadio
{
public function __construct(WebDriverElement $element)
{
parent::__construct($element);

$this->type = $element->getAttribute('type');
if ($this->type !== 'radio') {
throw new InvalidElementStateException('The input must be of type "radio".');
}
}

public function isMultiple()
{
return false;
}

public function deselectAll()
{
throw new UnsupportedOperationException('You cannot deselect radio buttons');
}

public function deselectByIndex($index)
{
throw new UnsupportedOperationException('You cannot deselect radio buttons');
}

public function deselectByValue($value)
{
throw new UnsupportedOperationException('You cannot deselect radio buttons');
}

public function deselectByVisibleText($text)
{
throw new UnsupportedOperationException('You cannot deselect radio buttons');
}

public function deselectByVisiblePartialText($text)
{
throw new UnsupportedOperationException('You cannot deselect radio buttons');
}
}
<?php

namespace Facebook\WebDriver;




interface WebDriverTargetLocator
{

public const WINDOW_TYPE_WINDOW = 'window';

public const WINDOW_TYPE_TAB = 'tab';







public function defaultContent();











public function frame($frame);
















public function window($handle);









public function alert();







public function activeElement();
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\NoSuchAlertException;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\NoSuchFrameException;
use Facebook\WebDriver\Exception\StaleElementReferenceException;






class WebDriverExpectedCondition
{





private $apply;

protected function __construct(callable $apply)
{
$this->apply = $apply;
}




public function getApply()
{
return $this->apply;
}







public static function titleIs($title)
{
return new static(
function (WebDriver $driver) use ($title) {
return $title === $driver->getTitle();
}
);
}







public static function titleContains($title)
{
return new static(
function (WebDriver $driver) use ($title) {
return mb_strpos($driver->getTitle(), $title) !== false;
}
);
}







public static function titleMatches($titleRegexp)
{
return new static(
function (WebDriver $driver) use ($titleRegexp) {
return (bool) preg_match($titleRegexp, $driver->getTitle());
}
);
}







public static function urlIs($url)
{
return new static(
function (WebDriver $driver) use ($url) {
return $url === $driver->getCurrentURL();
}
);
}







public static function urlContains($url)
{
return new static(
function (WebDriver $driver) use ($url) {
return mb_strpos($driver->getCurrentURL(), $url) !== false;
}
);
}







public static function urlMatches($urlRegexp)
{
return new static(
function (WebDriver $driver) use ($urlRegexp) {
return (bool) preg_match($urlRegexp, $driver->getCurrentURL());
}
);
}








public static function presenceOfElementLocated(WebDriverBy $by)
{
return new static(
function (WebDriver $driver) use ($by) {
try {
return $driver->findElement($by);
} catch (NoSuchElementException $e) {
return false;
}
}
);
}







public static function presenceOfAllElementsLocatedBy(WebDriverBy $by)
{
return new static(
function (WebDriver $driver) use ($by) {
$elements = $driver->findElements($by);

return count($elements) > 0 ? $elements : null;
}
);
}








public static function visibilityOfElementLocated(WebDriverBy $by)
{
return new static(
function (WebDriver $driver) use ($by) {
try {
$element = $driver->findElement($by);

return $element->isDisplayed() ? $element : null;
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}









public static function visibilityOfAnyElementLocated(WebDriverBy $by)
{
return new static(
function (WebDriver $driver) use ($by) {
$elements = $driver->findElements($by);
$visibleElements = [];

foreach ($elements as $element) {
try {
if ($element->isDisplayed()) {
$visibleElements[] = $element;
}
} catch (StaleElementReferenceException $e) {
}
}

return count($visibleElements) > 0 ? $visibleElements : null;
}
);
}








public static function visibilityOf(WebDriverElement $element)
{
return new static(
function () use ($element) {
return $element->isDisplayed() ? $element : null;
}
);
}











public static function textToBePresentInElement(WebDriverBy $by, $text)
{
return self::elementTextContains($by, $text);
}









public static function elementTextContains(WebDriverBy $by, $text)
{
return new static(
function (WebDriver $driver) use ($by, $text) {
try {
$element_text = $driver->findElement($by)->getText();

return mb_strpos($element_text, $text) !== false;
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}









public static function elementTextIs(WebDriverBy $by, $text)
{
return new static(
function (WebDriver $driver) use ($by, $text) {
try {
return $driver->findElement($by)->getText() == $text;
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}








public static function elementTextMatches(WebDriverBy $by, $regexp)
{
return new static(
function (WebDriver $driver) use ($by, $regexp) {
try {
return (bool) preg_match($regexp, $driver->findElement($by)->getText());
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}










public static function textToBePresentInElementValue(WebDriverBy $by, $text)
{
return self::elementValueContains($by, $text);
}








public static function elementValueContains(WebDriverBy $by, $text)
{
return new static(
function (WebDriver $driver) use ($by, $text) {
try {
$element_text = $driver->findElement($by)->getAttribute('value');

return mb_strpos($element_text, $text) !== false;
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}








public static function frameToBeAvailableAndSwitchToIt($frame_locator)
{
return new static(
function (WebDriver $driver) use ($frame_locator) {
try {
return $driver->switchTo()->frame($frame_locator);
} catch (NoSuchFrameException $e) {
return false;
}
}
);
}







public static function invisibilityOfElementLocated(WebDriverBy $by)
{
return new static(
function (WebDriver $driver) use ($by) {
try {
return !$driver->findElement($by)->isDisplayed();
} catch (NoSuchElementException|StaleElementReferenceException $e) {
return true;
}
}
);
}








public static function invisibilityOfElementWithText(WebDriverBy $by, $text)
{
return new static(
function (WebDriver $driver) use ($by, $text) {
try {
return !($driver->findElement($by)->getText() === $text);
} catch (NoSuchElementException|StaleElementReferenceException $e) {
return true;
}
}
);
}







public static function elementToBeClickable(WebDriverBy $by)
{
$visibility_of_element_located = self::visibilityOfElementLocated($by);

return new static(
function (WebDriver $driver) use ($visibility_of_element_located) {
$element = call_user_func(
$visibility_of_element_located->getApply(),
$driver
);

try {
if ($element !== null && $element->isEnabled()) {
return $element;
}

return null;
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}







public static function stalenessOf(WebDriverElement $element)
{
return new static(
function () use ($element) {
try {
$element->isEnabled();

return false;
} catch (StaleElementReferenceException $e) {
return true;
}
}
);
}












public static function refreshed(self $condition)
{
return new static(
function (WebDriver $driver) use ($condition) {
try {
return call_user_func($condition->getApply(), $driver);
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}







public static function elementToBeSelected($element_or_by)
{
return self::elementSelectionStateToBe(
$element_or_by,
true
);
}








public static function elementSelectionStateToBe($element_or_by, $selected)
{
if ($element_or_by instanceof WebDriverElement) {
return new static(
function () use ($element_or_by, $selected) {
return $element_or_by->isSelected() === $selected;
}
);
}

if ($element_or_by instanceof WebDriverBy) {
return new static(
function (WebDriver $driver) use ($element_or_by, $selected) {
try {
$element = $driver->findElement($element_or_by);

return $element->isSelected() === $selected;
} catch (StaleElementReferenceException $e) {
return null;
}
}
);
}

throw LogicException::forError('Instance of either WebDriverElement or WebDriverBy must be given');
}






public static function alertIsPresent()
{
return new static(
function (WebDriver $driver) {
try {



$alert = $driver->switchTo()->alert();
$alert->getText();

return $alert;
} catch (NoSuchAlertException $e) {
return null;
}
}
);
}







public static function numberOfWindowsToBe($expectedNumberOfWindows)
{
return new static(
function (WebDriver $driver) use ($expectedNumberOfWindows) {
return count($driver->getWindowHandles()) == $expectedNumberOfWindows;
}
);
}







public static function not(self $condition)
{
return new static(
function (WebDriver $driver) use ($condition) {
$result = call_user_func($condition->getApply(), $driver);

return !$result;
}
);
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
use Facebook\WebDriver\WebDriverMouse;




class RemoteMouse implements WebDriverMouse
{

public const BUTTON_LEFT = 0;

public const BUTTON_MIDDLE = 1;

public const BUTTON_RIGHT = 2;




private $executor;



private $isW3cCompliant;




public function __construct(RemoteExecuteMethod $executor, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->isW3cCompliant = $isW3cCompliant;
}




public function click(WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$moveAction = $where ? [$this->createMoveAction($where)] : [];
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'pointer',
'id' => 'mouse',
'parameters' => ['pointerType' => 'mouse'],
'actions' => array_merge($moveAction, $this->createClickActions()),
],
],
]);

return $this;
}

$this->moveIfNeeded($where);
$this->executor->execute(DriverCommand::CLICK, [
'button' => self::BUTTON_LEFT,
]);

return $this;
}




public function contextClick(WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$moveAction = $where ? [$this->createMoveAction($where)] : [];
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'pointer',
'id' => 'mouse',
'parameters' => ['pointerType' => 'mouse'],
'actions' => array_merge($moveAction, [
[
'type' => 'pointerDown',
'button' => self::BUTTON_RIGHT,
],
[
'type' => 'pointerUp',
'button' => self::BUTTON_RIGHT,
],
]),
],
],
]);

return $this;
}

$this->moveIfNeeded($where);
$this->executor->execute(DriverCommand::CLICK, [
'button' => self::BUTTON_RIGHT,
]);

return $this;
}




public function doubleClick(WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$clickActions = $this->createClickActions();
$moveAction = $where === null ? [] : [$this->createMoveAction($where)];
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'pointer',
'id' => 'mouse',
'parameters' => ['pointerType' => 'mouse'],
'actions' => array_merge($moveAction, $clickActions, $clickActions),
],
],
]);

return $this;
}

$this->moveIfNeeded($where);
$this->executor->execute(DriverCommand::DOUBLE_CLICK);

return $this;
}




public function mouseDown(WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'pointer',
'id' => 'mouse',
'parameters' => ['pointerType' => 'mouse'],
'actions' => [
$this->createMoveAction($where),
[
'type' => 'pointerDown',
'button' => self::BUTTON_LEFT,
],
],
],
],
]);

return $this;
}

$this->moveIfNeeded($where);
$this->executor->execute(DriverCommand::MOUSE_DOWN);

return $this;
}







public function mouseMove(
WebDriverCoordinates $where = null,
$x_offset = null,
$y_offset = null
) {
if ($this->isW3cCompliant) {
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'pointer',
'id' => 'mouse',
'parameters' => ['pointerType' => 'mouse'],
'actions' => [$this->createMoveAction($where, $x_offset, $y_offset)],
],
],
]);

return $this;
}

$params = [];
if ($where !== null) {
$params['element'] = $where->getAuxiliary();
}
if ($x_offset !== null) {
$params['xoffset'] = $x_offset;
}
if ($y_offset !== null) {
$params['yoffset'] = $y_offset;
}

$this->executor->execute(DriverCommand::MOVE_TO, $params);

return $this;
}




public function mouseUp(WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$moveAction = $where ? [$this->createMoveAction($where)] : [];

$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'pointer',
'id' => 'mouse',
'parameters' => ['pointerType' => 'mouse'],
'actions' => array_merge($moveAction, [
[
'type' => 'pointerUp',
'button' => self::BUTTON_LEFT,
],
]),
],
],
]);

return $this;
}

$this->moveIfNeeded($where);
$this->executor->execute(DriverCommand::MOUSE_UP);

return $this;
}

protected function moveIfNeeded(WebDriverCoordinates $where = null)
{
if ($where) {
$this->mouseMove($where);
}
}







private function createMoveAction(
WebDriverCoordinates $where = null,
$x_offset = null,
$y_offset = null
) {
$move_action = [
'type' => 'pointerMove',
'duration' => 100, 
'x' => $x_offset ?? 0,
'y' => $y_offset ?? 0,
];

if ($where !== null) {
$move_action['origin'] = [JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $where->getAuxiliary()];
} else {
$move_action['origin'] = 'pointer';
}

return $move_action;
}




private function createClickActions()
{
return [
[
'type' => 'pointerDown',
'button' => self::BUTTON_LEFT,
],
[
'type' => 'pointerUp',
'button' => self::BUTTON_LEFT,
],
];
}
}
<?php

namespace Facebook\WebDriver\Remote;

class WebDriverResponse
{



private $status;



private $value;



private $sessionID;




public function __construct($session_id = null)
{
$this->sessionID = $session_id;
}




public function getStatus()
{
return $this->status;
}





public function setStatus($status)
{
$this->status = $status;

return $this;
}




public function getValue()
{
return $this->value;
}





public function setValue($value)
{
$this->value = $value;

return $this;
}




public function getSessionID()
{
return $this->sessionID;
}





public function setSessionID($session_id)
{
$this->sessionID = $session_id;

return $this;
}
}
<?php

namespace Facebook\WebDriver\Remote\Service;

use Facebook\WebDriver\Exception\Internal\IOException;
use Facebook\WebDriver\Exception\Internal\RuntimeException;
use Facebook\WebDriver\Net\URLChecker;
use Symfony\Component\Process\Process;





class DriverService
{



private $executable;




private $url;




private $args;




private $environment;




private $process;







public function __construct($executable, $port, $args = [], $environment = null)
{
$this->setExecutable($executable);
$this->url = sprintf('http://localhost:%d', $port);
$this->args = $args;
$this->environment = $environment ?: $_ENV;
}




public function getURL()
{
return $this->url;
}




public function start()
{
if ($this->process !== null) {
return $this;
}

$this->process = $this->createProcess();
$this->process->start();

$this->checkWasStarted($this->process);

$checker = new URLChecker();
$checker->waitUntilAvailable(20 * 1000, $this->url . '/status');

return $this;
}




public function stop()
{
if ($this->process === null) {
return $this;
}

$this->process->stop();
$this->process = null;

$checker = new URLChecker();
$checker->waitUntilUnavailable(3 * 1000, $this->url . '/shutdown');

return $this;
}




public function isRunning()
{
if ($this->process === null) {
return false;
}

return $this->process->isRunning();
}







protected static function checkExecutable($executable)
{
return $executable;
}





protected function setExecutable($executable)
{
if ($this->isExecutable($executable)) {
$this->executable = $executable;

return;
}

throw IOException::forFileError(
'File is not executable. Make sure the path is correct or use environment variable to specify'
. ' location of the executable.',
$executable
);
}




protected function checkWasStarted($process)
{
usleep(10000); 

if (!$process->isRunning()) {
throw RuntimeException::forDriverError($process);
}
}

private function createProcess(): Process
{
$commandLine = array_merge([$this->executable], $this->args);

return new Process($commandLine, null, $this->environment);
}




private function isExecutable(string $filename): bool
{
if (is_executable($filename)) {
return true;
}
if ($filename !== basename($filename)) { 
return false;
}

$paths = explode(PATH_SEPARATOR, getenv('PATH'));
foreach ($paths as $path) {
if (is_executable($path . DIRECTORY_SEPARATOR . $filename)) {
return true;
}
}

return false;
}
}
<?php

namespace Facebook\WebDriver\Remote\Service;

use Facebook\WebDriver\Exception\Internal\DriverServerDiedException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\HttpCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;
use Facebook\WebDriver\Remote\WebDriverResponse;




class DriverCommandExecutor extends HttpCommandExecutor
{



private $service;

public function __construct(DriverService $service)
{
parent::__construct($service->getURL());
$this->service = $service;
}






public function execute(WebDriverCommand $command)
{
if ($command->getName() === DriverCommand::NEW_SESSION) {
$this->service->start();
}

try {
$value = parent::execute($command);
if ($command->getName() === DriverCommand::QUIT) {
$this->service->stop();
}

return $value;
} catch (\Exception $e) {
if (!$this->service->isRunning()) {
throw new DriverServerDiedException($e);
}
throw $e;
}
}
}
<?php

namespace Facebook\WebDriver\Remote;






class DriverCommand
{
public const GET_ALL_SESSIONS = 'getAllSessions';
public const GET_CAPABILITIES = 'getCapabilities';
public const NEW_SESSION = 'newSession';
public const STATUS = 'status';
public const CLOSE = 'close';
public const QUIT = 'quit';
public const GET = 'get';
public const GO_BACK = 'goBack';
public const GO_FORWARD = 'goForward';
public const REFRESH = 'refresh';
public const ADD_COOKIE = 'addCookie';
public const GET_ALL_COOKIES = 'getCookies';
public const DELETE_COOKIE = 'deleteCookie';
public const DELETE_ALL_COOKIES = 'deleteAllCookies';
public const FIND_ELEMENT = 'findElement';
public const FIND_ELEMENTS = 'findElements';
public const FIND_CHILD_ELEMENT = 'findChildElement';
public const FIND_CHILD_ELEMENTS = 'findChildElements';
public const CLEAR_ELEMENT = 'clearElement';
public const CLICK_ELEMENT = 'clickElement';
public const SEND_KEYS_TO_ELEMENT = 'sendKeysToElement';
public const SEND_KEYS_TO_ACTIVE_ELEMENT = 'sendKeysToActiveElement';
public const SUBMIT_ELEMENT = 'submitElement';
public const UPLOAD_FILE = 'uploadFile';
public const GET_CURRENT_WINDOW_HANDLE = 'getCurrentWindowHandle';
public const GET_WINDOW_HANDLES = 'getWindowHandles';
public const GET_CURRENT_CONTEXT_HANDLE = 'getCurrentContextHandle';
public const GET_CONTEXT_HANDLES = 'getContextHandles';

public const SWITCH_TO_WINDOW = 'switchToWindow';
public const SWITCH_TO_CONTEXT = 'switchToContext';
public const SWITCH_TO_FRAME = 'switchToFrame';
public const SWITCH_TO_PARENT_FRAME = 'switchToParentFrame';
public const GET_ACTIVE_ELEMENT = 'getActiveElement';

public const GET_CURRENT_URL = 'getCurrentUrl';
public const GET_PAGE_SOURCE = 'getPageSource';
public const GET_TITLE = 'getTitle';

public const EXECUTE_SCRIPT = 'executeScript';
public const EXECUTE_ASYNC_SCRIPT = 'executeAsyncScript';

public const GET_ELEMENT_TEXT = 'getElementText';
public const GET_ELEMENT_TAG_NAME = 'getElementTagName';
public const IS_ELEMENT_SELECTED = 'isElementSelected';
public const IS_ELEMENT_ENABLED = 'isElementEnabled';
public const IS_ELEMENT_DISPLAYED = 'isElementDisplayed';
public const GET_ELEMENT_LOCATION = 'getElementLocation';
public const GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW = 'getElementLocationOnceScrolledIntoView';
public const GET_ELEMENT_SIZE = 'getElementSize';
public const GET_ELEMENT_ATTRIBUTE = 'getElementAttribute';
public const GET_ELEMENT_VALUE_OF_CSS_PROPERTY = 'getElementValueOfCssProperty';
public const ELEMENT_EQUALS = 'elementEquals';
public const SCREENSHOT = 'screenshot';

public const ACCEPT_ALERT = 'acceptAlert';
public const DISMISS_ALERT = 'dismissAlert';
public const GET_ALERT_TEXT = 'getAlertText';
public const SET_ALERT_VALUE = 'setAlertValue';

public const SET_TIMEOUT = 'setTimeout';
public const IMPLICITLY_WAIT = 'implicitlyWait';
public const SET_SCRIPT_TIMEOUT = 'setScriptTimeout';

public const EXECUTE_SQL = 'executeSQL';
public const GET_LOCATION = 'getLocation';
public const SET_LOCATION = 'setLocation';
public const GET_APP_CACHE = 'getAppCache';
public const GET_APP_CACHE_STATUS = 'getStatus';
public const CLEAR_APP_CACHE = 'clearAppCache';
public const IS_BROWSER_ONLINE = 'isBrowserOnline';
public const SET_BROWSER_ONLINE = 'setBrowserOnline';

public const GET_LOCAL_STORAGE_ITEM = 'getLocalStorageItem';
public const GET_LOCAL_STORAGE_KEYS = 'getLocalStorageKeys';
public const SET_LOCAL_STORAGE_ITEM = 'setLocalStorageItem';
public const REMOVE_LOCAL_STORAGE_ITEM = 'removeLocalStorageItem';
public const CLEAR_LOCAL_STORAGE = 'clearLocalStorage';
public const GET_LOCAL_STORAGE_SIZE = 'getLocalStorageSize';

public const GET_SESSION_STORAGE_ITEM = 'getSessionStorageItem';
public const GET_SESSION_STORAGE_KEYS = 'getSessionStorageKey';
public const SET_SESSION_STORAGE_ITEM = 'setSessionStorageItem';
public const REMOVE_SESSION_STORAGE_ITEM = 'removeSessionStorageItem';
public const CLEAR_SESSION_STORAGE = 'clearSessionStorage';
public const GET_SESSION_STORAGE_SIZE = 'getSessionStorageSize';

public const SET_SCREEN_ORIENTATION = 'setScreenOrientation';
public const GET_SCREEN_ORIENTATION = 'getScreenOrientation';

public const CLICK = 'mouseClick';
public const DOUBLE_CLICK = 'mouseDoubleClick';
public const MOUSE_DOWN = 'mouseButtonDown';
public const MOUSE_UP = 'mouseButtonUp';
public const MOVE_TO = 'mouseMoveTo';

public const IME_GET_AVAILABLE_ENGINES = 'imeGetAvailableEngines';
public const IME_GET_ACTIVE_ENGINE = 'imeGetActiveEngine';
public const IME_IS_ACTIVATED = 'imeIsActivated';
public const IME_DEACTIVATE = 'imeDeactivate';
public const IME_ACTIVATE_ENGINE = 'imeActivateEngine';

public const TOUCH_SINGLE_TAP = 'touchSingleTap';
public const TOUCH_DOWN = 'touchDown';
public const TOUCH_UP = 'touchUp';
public const TOUCH_MOVE = 'touchMove';
public const TOUCH_SCROLL = 'touchScroll';
public const TOUCH_DOUBLE_TAP = 'touchDoubleTap';
public const TOUCH_LONG_PRESS = 'touchLongPress';
public const TOUCH_FLICK = 'touchFlick';

public const SET_WINDOW_SIZE = 'setWindowSize';
public const SET_WINDOW_POSITION = 'setWindowPosition';
public const GET_WINDOW_SIZE = 'getWindowSize';
public const GET_WINDOW_POSITION = 'getWindowPosition';
public const MAXIMIZE_WINDOW = 'maximizeWindow';
public const FULLSCREEN_WINDOW = 'fullscreenWindow';

public const GET_AVAILABLE_LOG_TYPES = 'getAvailableLogTypes';
public const GET_LOG = 'getLog';
public const GET_SESSION_LOGS = 'getSessionLogs';

public const GET_NETWORK_CONNECTION = 'getNetworkConnection';
public const SET_NETWORK_CONNECTION = 'setNetworkConnection';

public const CUSTOM_COMMAND = 'customCommand';


public const ACTIONS = 'actions';
public const GET_ELEMENT_PROPERTY = 'getElementProperty';
public const GET_NAMED_COOKIE = 'getNamedCookie';
public const NEW_WINDOW = 'newWindow';
public const TAKE_ELEMENT_SCREENSHOT = 'takeElementScreenshot';
public const MINIMIZE_WINDOW = 'minimizeWindow';
public const GET_ELEMENT_SHADOW_ROOT = 'getElementShadowRoot';
public const FIND_ELEMENT_FROM_SHADOW_ROOT = 'findElementFromShadowRoot';
public const FIND_ELEMENTS_FROM_SHADOW_ROOT = 'findElementsFromShadowRoot';

private function __construct()
{
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\WebDriverBy;






abstract class JsonWireCompat
{





public const WEB_DRIVER_ELEMENT_IDENTIFIER = 'element-6066-11e4-a52e-4f735466cecf';





public static function getElement($rawElement)
{

if (!is_array($rawElement)) {
throw UnexpectedResponseException::forElementNotArray($rawElement);
}

if (array_key_exists(self::WEB_DRIVER_ELEMENT_IDENTIFIER, $rawElement)) {

return $rawElement[self::WEB_DRIVER_ELEMENT_IDENTIFIER];
}


return $rawElement['ELEMENT'];
}






public static function getUsing(WebDriverBy $by, $isW3cCompliant)
{
$mechanism = $by->getMechanism();
$value = $by->getValue();

if ($isW3cCompliant) {
switch ($mechanism) {

case 'class name':
$mechanism = 'css selector';
$value = sprintf('.%s', self::escapeSelector($value));
break;
case 'id':
$mechanism = 'css selector';
$value = sprintf('#%s', self::escapeSelector($value));
break;
case 'name':
$mechanism = 'css selector';
$value = sprintf('[name=\'%s\']', self::escapeSelector($value));
break;
}
}

return ['using' => $mechanism, 'value' => $value];
}












private static function escapeSelector($selector)
{
return preg_replace_callback('/[^a-z0-9]/iSu', function ($matches) {
$chr = $matches[0];
if (mb_strlen($chr) === 1) {
$ord = ord($chr);
} else {
$chr = mb_convert_encoding($chr, 'UTF-32BE', 'UTF-8');
$ord = hexdec(bin2hex($chr));
}

return sprintf('\\%X ', $ord);
}, $selector);
}
}
<?php

namespace Facebook\WebDriver\Remote;

class RemoteExecuteMethod implements ExecuteMethod
{



private $driver;

public function __construct(RemoteWebDriver $driver)
{
$this->driver = $driver;
}





public function execute($command_name, array $parameters = [])
{
return $this->driver->execute($command_name, $parameters);
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\ElementNotInteractableException;
use Facebook\WebDriver\Exception\Internal\IOException;
use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\Support\ScreenshotHelper;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverDimension;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverKeys;
use Facebook\WebDriver\WebDriverPoint;
use ZipArchive;




class RemoteWebElement implements WebDriverElement, WebDriverLocatable
{



protected $executor;



protected $id;



protected $fileDetector;



protected $isW3cCompliant;





public function __construct(RemoteExecuteMethod $executor, $id, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->id = $id;
$this->fileDetector = new UselessFileDetector();
$this->isW3cCompliant = $isW3cCompliant;
}






public function clear()
{
$this->executor->execute(
DriverCommand::CLEAR_ELEMENT,
[':id' => $this->id]
);

return $this;
}






public function click()
{
try {
$this->executor->execute(
DriverCommand::CLICK_ELEMENT,
[':id' => $this->id]
);
} catch (ElementNotInteractableException $e) {



$this->clickChildElement($e);
}

return $this;
}











public function findElement(WebDriverBy $by)
{
$params = JsonWireCompat::getUsing($by, $this->isW3cCompliant);
$params[':id'] = $this->id;

$raw_element = $this->executor->execute(
DriverCommand::FIND_CHILD_ELEMENT,
$params
);

return $this->newElement(JsonWireCompat::getElement($raw_element));
}












public function findElements(WebDriverBy $by)
{
$params = JsonWireCompat::getUsing($by, $this->isW3cCompliant);
$params[':id'] = $this->id;
$raw_elements = $this->executor->execute(
DriverCommand::FIND_CHILD_ELEMENTS,
$params
);

if (!is_array($raw_elements)) {
throw UnexpectedResponseException::forError('Server response to findChildElements command is not an array');
}

$elements = [];
foreach ($raw_elements as $raw_element) {
$elements[] = $this->newElement(JsonWireCompat::getElement($raw_element));
}

return $elements;
}










public function getAttribute($attribute_name)
{
$params = [
':name' => $attribute_name,
':id' => $this->id,
];

if ($this->isW3cCompliant && ($attribute_name === 'value' || $attribute_name === 'index')) {
$value = $this->executor->execute(DriverCommand::GET_ELEMENT_PROPERTY, $params);

if ($value === true) {
return 'true';
}

if ($value === false) {
return 'false';
}

if ($value !== null) {
return (string) $value;
}
}

return $this->executor->execute(DriverCommand::GET_ELEMENT_ATTRIBUTE, $params);
}









public function getDomProperty($propertyName)
{
if (!$this->isW3cCompliant) {
throw new UnsupportedOperationException('This method is only supported in W3C mode');
}

$params = [
':name' => $propertyName,
':id' => $this->id,
];

return $this->executor->execute(DriverCommand::GET_ELEMENT_PROPERTY, $params);
}







public function getCSSValue($css_property_name)
{
$params = [
':propertyName' => $css_property_name,
':id' => $this->id,
];

return $this->executor->execute(
DriverCommand::GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
$params
);
}






public function getLocation()
{
$location = $this->executor->execute(
DriverCommand::GET_ELEMENT_LOCATION,
[':id' => $this->id]
);

return new WebDriverPoint($location['x'], $location['y']);
}







public function getLocationOnScreenOnceScrolledIntoView()
{
if ($this->isW3cCompliant) {
$script = <<<JS
                var e = arguments[0];
                e.scrollIntoView({ behavior: 'instant', block: 'end', inline: 'nearest' });
                var rect = e.getBoundingClientRect();
                return {'x': rect.left, 'y': rect.top};
            JS;

$result = $this->executor->execute(DriverCommand::EXECUTE_SCRIPT, [
'script' => $script,
'args' => [[JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $this->id]],
]);
$location = ['x' => $result['x'], 'y' => $result['y']];
} else {
$location = $this->executor->execute(
DriverCommand::GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW,
[':id' => $this->id]
);
}

return new WebDriverPoint($location['x'], $location['y']);
}




public function getCoordinates()
{
$element = $this;

$on_screen = null; 
$in_view_port = static function () use ($element) {
return $element->getLocationOnScreenOnceScrolledIntoView();
};
$on_page = static function () use ($element) {
return $element->getLocation();
};
$auxiliary = $this->getID();

return new WebDriverCoordinates(
$on_screen,
$in_view_port,
$on_page,
$auxiliary
);
}






public function getSize()
{
$size = $this->executor->execute(
DriverCommand::GET_ELEMENT_SIZE,
[':id' => $this->id]
);

return new WebDriverDimension($size['width'], $size['height']);
}






public function getTagName()
{




return mb_strtolower($this->executor->execute(
DriverCommand::GET_ELEMENT_TAG_NAME,
[':id' => $this->id]
));
}







public function getText()
{
return $this->executor->execute(
DriverCommand::GET_ELEMENT_TEXT,
[':id' => $this->id]
);
}







public function isDisplayed()
{
return $this->executor->execute(
DriverCommand::IS_ELEMENT_DISPLAYED,
[':id' => $this->id]
);
}







public function isEnabled()
{
return $this->executor->execute(
DriverCommand::IS_ELEMENT_ENABLED,
[':id' => $this->id]
);
}






public function isSelected()
{
return $this->executor->execute(
DriverCommand::IS_ELEMENT_SELECTED,
[':id' => $this->id]
);
}







public function sendKeys($value)
{
$local_file = $this->fileDetector->getLocalFile($value);

$params = [];
if ($local_file === null) {
if ($this->isW3cCompliant) {


$encodedValues = explode(WebDriverKeys::NULL, WebDriverKeys::encode($value, true));
foreach ($encodedValues as $encodedValue) {
$params[] = [
'text' => $encodedValue,
':id' => $this->id,
];
}
} else {
$params[] = [
'value' => WebDriverKeys::encode($value),
':id' => $this->id,
];
}
} else {
if ($this->isW3cCompliant) {
try {



$fileName = $this->upload($local_file);
} catch (PhpWebDriverExceptionInterface $e) {
$fileName = $local_file;
}

$params[] = [
'text' => $fileName,
':id' => $this->id,
];
} else {
$params[] = [
'value' => WebDriverKeys::encode($this->upload($local_file)),
':id' => $this->id,
];
}
}

foreach ($params as $param) {
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ELEMENT, $param);
}

return $this;
}














public function setFileDetector(FileDetector $detector)
{
$this->fileDetector = $detector;

return $this;
}






public function submit()
{
if ($this->isW3cCompliant) {


$submitPolyfill = <<<HTXT
                var form = arguments[0];
                while (form.nodeName !== "FORM" && form.parentNode) { // find the parent form of this element
                    form = form.parentNode;
                }
                if (!form) {
                    throw Error('Unable to find containing form element');
                }
                var event = new Event('submit', {bubbles: true, cancelable: true});
                if (form.dispatchEvent(event)) {
                    HTMLFormElement.prototype.submit.call(form);
                }
            HTXT;
$this->executor->execute(DriverCommand::EXECUTE_SCRIPT, [
'script' => $submitPolyfill,
'args' => [[JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $this->id]],
]);

return $this;
}

$this->executor->execute(
DriverCommand::SUBMIT_ELEMENT,
[':id' => $this->id]
);

return $this;
}






public function getID()
{
return $this->id;
}







public function takeElementScreenshot($save_as = null)
{
return (new ScreenshotHelper($this->executor))->takeElementScreenshot($this->id, $save_as);
}






public function equals(WebDriverElement $other)
{
if ($this->isW3cCompliant) {
return $this->getID() === $other->getID();
}

return $this->executor->execute(DriverCommand::ELEMENT_EQUALS, [
':id' => $this->id,
':other' => $other->getID(),
]);
}






public function getShadowRoot()
{
if (!$this->isW3cCompliant) {
throw new UnsupportedOperationException('This method is only supported in W3C mode');
}

$response = $this->executor->execute(
DriverCommand::GET_ELEMENT_SHADOW_ROOT,
[
':id' => $this->id,
]
);

return ShadowRoot::createFromResponse($this->executor, $response);
}














protected function clickChildElement(ElementNotInteractableException $originalException)
{
$children = $this->findElements(WebDriverBy::xpath('./*'));
foreach ($children as $child) {
try {


$this->executor->execute(
DriverCommand::CLICK_ELEMENT,
[':id' => $child->id]
);

return;
} catch (ElementNotInteractableException $e) {

}
}

throw $originalException;
}








protected function newElement($id)
{
return new static($this->executor, $id, $this->isW3cCompliant);
}









protected function upload($local_file)
{
if (!is_file($local_file)) {
throw LogicException::forError('You may only upload files: ' . $local_file);
}

$temp_zip_path = $this->createTemporaryZipArchive($local_file);

$remote_path = $this->executor->execute(
DriverCommand::UPLOAD_FILE,
['file' => base64_encode(file_get_contents($temp_zip_path))]
);

unlink($temp_zip_path);

return $remote_path;
}





protected function createTemporaryZipArchive($fileToZip)
{


$tempZipPath = sys_get_temp_dir() . '/' . uniqid('WebDriverZip', false);

$zip = new ZipArchive();
if (($errorCode = $zip->open($tempZipPath, ZipArchive::CREATE)) !== true) {
throw IOException::forFileError(sprintf('Error creating zip archive: %s', $errorCode), $tempZipPath);
}

$info = pathinfo($fileToZip);
$file_name = $info['basename'];
$zip->addFile($fileToZip, $file_name);
$zip->close();

return $tempZipPath;
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\WebDriver;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverKeys;




class RemoteKeyboard implements WebDriverKeyboard
{

private $executor;

private $driver;

private $isW3cCompliant;




public function __construct(RemoteExecuteMethod $executor, WebDriver $driver, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->driver = $driver;
$this->isW3cCompliant = $isW3cCompliant;
}






public function sendKeys($keys)
{
if ($this->isW3cCompliant) {
$activeElement = $this->driver->switchTo()->activeElement();
$activeElement->sendKeys($keys);
} else {
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
'value' => WebDriverKeys::encode($keys),
]);
}

return $this;
}








public function pressKey($key)
{
if ($this->isW3cCompliant) {
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'key',
'id' => 'keyboard',
'actions' => [['type' => 'keyDown', 'value' => $key]],
],
],
]);
} else {
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
'value' => [(string) $key],
]);
}

return $this;
}








public function releaseKey($key)
{
if ($this->isW3cCompliant) {
$this->executor->execute(DriverCommand::ACTIONS, [
'actions' => [
[
'type' => 'key',
'id' => 'keyboard',
'actions' => [['type' => 'keyUp', 'value' => $key]],
],
],
]);
} else {
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
'value' => [(string) $key],
]);
}

return $this;
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\Internal\WebDriverCurlException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\WebDriverCommandExecutor;




class HttpCommandExecutor implements WebDriverCommandExecutor
{
public const DEFAULT_HTTP_HEADERS = [
'Content-Type: application/json;charset=UTF-8',
'Accept: application/json',
];




protected static $commands = [
DriverCommand::ACCEPT_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/accept_alert'],
DriverCommand::ADD_COOKIE => ['method' => 'POST', 'url' => '/session/:sessionId/cookie'],
DriverCommand::CLEAR_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/clear'],
DriverCommand::CLICK_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/click'],
DriverCommand::CLOSE => ['method' => 'DELETE', 'url' => '/session/:sessionId/window'],
DriverCommand::DELETE_ALL_COOKIES => ['method' => 'DELETE', 'url' => '/session/:sessionId/cookie'],
DriverCommand::DELETE_COOKIE => ['method' => 'DELETE', 'url' => '/session/:sessionId/cookie/:name'],
DriverCommand::DISMISS_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/dismiss_alert'],
DriverCommand::ELEMENT_EQUALS => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/equals/:other'],
DriverCommand::FIND_CHILD_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/element'],
DriverCommand::FIND_CHILD_ELEMENTS => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/elements'],
DriverCommand::EXECUTE_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute'],
DriverCommand::EXECUTE_ASYNC_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute_async'],
DriverCommand::FIND_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element'],
DriverCommand::FIND_ELEMENTS => ['method' => 'POST', 'url' => '/session/:sessionId/elements'],
DriverCommand::SWITCH_TO_FRAME => ['method' => 'POST', 'url' => '/session/:sessionId/frame'],
DriverCommand::SWITCH_TO_PARENT_FRAME => ['method' => 'POST', 'url' => '/session/:sessionId/frame/parent'],
DriverCommand::SWITCH_TO_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window'],
DriverCommand::GET => ['method' => 'POST', 'url' => '/session/:sessionId/url'],
DriverCommand::GET_ACTIVE_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/active'],
DriverCommand::GET_ALERT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/alert_text'],
DriverCommand::GET_ALL_COOKIES => ['method' => 'GET', 'url' => '/session/:sessionId/cookie'],
DriverCommand::GET_NAMED_COOKIE => ['method' => 'GET', 'url' => '/session/:sessionId/cookie/:name'],
DriverCommand::GET_ALL_SESSIONS => ['method' => 'GET', 'url' => '/sessions'],
DriverCommand::GET_AVAILABLE_LOG_TYPES => ['method' => 'GET', 'url' => '/session/:sessionId/log/types'],
DriverCommand::GET_CURRENT_URL => ['method' => 'GET', 'url' => '/session/:sessionId/url'],
DriverCommand::GET_CURRENT_WINDOW_HANDLE => ['method' => 'GET', 'url' => '/session/:sessionId/window_handle'],
DriverCommand::GET_ELEMENT_ATTRIBUTE => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/attribute/:name',
],
DriverCommand::GET_ELEMENT_VALUE_OF_CSS_PROPERTY => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/css/:propertyName',
],
DriverCommand::GET_ELEMENT_LOCATION => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/location',
],
DriverCommand::GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/location_in_view',
],
DriverCommand::GET_ELEMENT_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/size'],
DriverCommand::GET_ELEMENT_TAG_NAME => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/name'],
DriverCommand::GET_ELEMENT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/text'],
DriverCommand::GET_LOG => ['method' => 'POST', 'url' => '/session/:sessionId/log'],
DriverCommand::GET_PAGE_SOURCE => ['method' => 'GET', 'url' => '/session/:sessionId/source'],
DriverCommand::GET_SCREEN_ORIENTATION => ['method' => 'GET', 'url' => '/session/:sessionId/orientation'],
DriverCommand::GET_CAPABILITIES => ['method' => 'GET', 'url' => '/session/:sessionId'],
DriverCommand::GET_TITLE => ['method' => 'GET', 'url' => '/session/:sessionId/title'],
DriverCommand::GET_WINDOW_HANDLES => ['method' => 'GET', 'url' => '/session/:sessionId/window_handles'],
DriverCommand::GET_WINDOW_POSITION => [
'method' => 'GET',
'url' => '/session/:sessionId/window/:windowHandle/position',
],
DriverCommand::GET_WINDOW_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/window/:windowHandle/size'],
DriverCommand::GO_BACK => ['method' => 'POST', 'url' => '/session/:sessionId/back'],
DriverCommand::GO_FORWARD => ['method' => 'POST', 'url' => '/session/:sessionId/forward'],
DriverCommand::IS_ELEMENT_DISPLAYED => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/displayed',
],
DriverCommand::IS_ELEMENT_ENABLED => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/enabled'],
DriverCommand::IS_ELEMENT_SELECTED => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/selected'],
DriverCommand::MAXIMIZE_WINDOW => [
'method' => 'POST',
'url' => '/session/:sessionId/window/:windowHandle/maximize',
],
DriverCommand::MOUSE_DOWN => ['method' => 'POST', 'url' => '/session/:sessionId/buttondown'],
DriverCommand::MOUSE_UP => ['method' => 'POST', 'url' => '/session/:sessionId/buttonup'],
DriverCommand::CLICK => ['method' => 'POST', 'url' => '/session/:sessionId/click'],
DriverCommand::DOUBLE_CLICK => ['method' => 'POST', 'url' => '/session/:sessionId/doubleclick'],
DriverCommand::MOVE_TO => ['method' => 'POST', 'url' => '/session/:sessionId/moveto'],
DriverCommand::NEW_SESSION => ['method' => 'POST', 'url' => '/session'],
DriverCommand::QUIT => ['method' => 'DELETE', 'url' => '/session/:sessionId'],
DriverCommand::REFRESH => ['method' => 'POST', 'url' => '/session/:sessionId/refresh'],
DriverCommand::UPLOAD_FILE => ['method' => 'POST', 'url' => '/session/:sessionId/file'], 
DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/keys'],
DriverCommand::SET_ALERT_VALUE => ['method' => 'POST', 'url' => '/session/:sessionId/alert_text'],
DriverCommand::SEND_KEYS_TO_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/value'],
DriverCommand::IMPLICITLY_WAIT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts/implicit_wait'],
DriverCommand::SET_SCREEN_ORIENTATION => ['method' => 'POST', 'url' => '/session/:sessionId/orientation'],
DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
DriverCommand::SET_SCRIPT_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts/async_script'],
DriverCommand::SET_WINDOW_POSITION => [
'method' => 'POST',
'url' => '/session/:sessionId/window/:windowHandle/position',
],
DriverCommand::SET_WINDOW_SIZE => [
'method' => 'POST',
'url' => '/session/:sessionId/window/:windowHandle/size',
],
DriverCommand::STATUS => ['method' => 'GET', 'url' => '/status'],
DriverCommand::SUBMIT_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/submit'],
DriverCommand::SCREENSHOT => ['method' => 'GET', 'url' => '/session/:sessionId/screenshot'],
DriverCommand::TAKE_ELEMENT_SCREENSHOT => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/screenshot',
],
DriverCommand::TOUCH_SINGLE_TAP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/click'],
DriverCommand::TOUCH_DOWN => ['method' => 'POST', 'url' => '/session/:sessionId/touch/down'],
DriverCommand::TOUCH_DOUBLE_TAP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/doubleclick'],
DriverCommand::TOUCH_FLICK => ['method' => 'POST', 'url' => '/session/:sessionId/touch/flick'],
DriverCommand::TOUCH_LONG_PRESS => ['method' => 'POST', 'url' => '/session/:sessionId/touch/longclick'],
DriverCommand::TOUCH_MOVE => ['method' => 'POST', 'url' => '/session/:sessionId/touch/move'],
DriverCommand::TOUCH_SCROLL => ['method' => 'POST', 'url' => '/session/:sessionId/touch/scroll'],
DriverCommand::TOUCH_UP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/up'],
DriverCommand::CUSTOM_COMMAND => [],
];



protected static $w3cCompliantCommands = [
DriverCommand::ACCEPT_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/alert/accept'],
DriverCommand::ACTIONS => ['method' => 'POST', 'url' => '/session/:sessionId/actions'],
DriverCommand::DISMISS_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/alert/dismiss'],
DriverCommand::EXECUTE_ASYNC_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute/async'],
DriverCommand::EXECUTE_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute/sync'],
DriverCommand::FIND_ELEMENT_FROM_SHADOW_ROOT => [
'method' => 'POST',
'url' => '/session/:sessionId/shadow/:id/element',
],
DriverCommand::FIND_ELEMENTS_FROM_SHADOW_ROOT => [
'method' => 'POST',
'url' => '/session/:sessionId/shadow/:id/elements',
],
DriverCommand::FULLSCREEN_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/fullscreen'],
DriverCommand::GET_ACTIVE_ELEMENT => ['method' => 'GET', 'url' => '/session/:sessionId/element/active'],
DriverCommand::GET_ALERT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/alert/text'],
DriverCommand::GET_CURRENT_WINDOW_HANDLE => ['method' => 'GET', 'url' => '/session/:sessionId/window'],
DriverCommand::GET_ELEMENT_LOCATION => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/rect'],
DriverCommand::GET_ELEMENT_PROPERTY => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/property/:name',
],
DriverCommand::GET_ELEMENT_SHADOW_ROOT => [
'method' => 'GET',
'url' => '/session/:sessionId/element/:id/shadow',
],
DriverCommand::GET_ELEMENT_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/rect'],
DriverCommand::GET_WINDOW_HANDLES => ['method' => 'GET', 'url' => '/session/:sessionId/window/handles'],
DriverCommand::GET_WINDOW_POSITION => ['method' => 'GET', 'url' => '/session/:sessionId/window/rect'],
DriverCommand::GET_WINDOW_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/window/rect'],
DriverCommand::IMPLICITLY_WAIT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
DriverCommand::MAXIMIZE_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/maximize'],
DriverCommand::MINIMIZE_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/minimize'],
DriverCommand::NEW_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/new'],
DriverCommand::SET_ALERT_VALUE => ['method' => 'POST', 'url' => '/session/:sessionId/alert/text'],
DriverCommand::SET_SCRIPT_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
DriverCommand::SET_WINDOW_SIZE => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'],
DriverCommand::SET_WINDOW_POSITION => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'],
];



protected $url;



protected $curl;



protected $isW3cCompliant = true;






public function __construct($url, $http_proxy = null, $http_proxy_port = null)
{
self::$w3cCompliantCommands = array_merge(self::$commands, self::$w3cCompliantCommands);

$this->url = $url;
$this->curl = curl_init();

if (!empty($http_proxy)) {
curl_setopt($this->curl, CURLOPT_PROXY, $http_proxy);
if ($http_proxy_port !== null) {
curl_setopt($this->curl, CURLOPT_PROXYPORT, $http_proxy_port);
}
}


$matches = null;
if (preg_match("/^(https?:\/\/)(.*):(.*)@(.*?)/U", $url, $matches)) {
$this->url = $matches[1] . $matches[4];
$auth_creds = $matches[2] . ':' . $matches[3];
curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($this->curl, CURLOPT_USERPWD, $auth_creds);
}

curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, static::DEFAULT_HTTP_HEADERS);

$this->setConnectionTimeout(30 * 1000); 
$this->setRequestTimeout(180 * 1000); 
}

public function disableW3cCompliance()
{
$this->isW3cCompliant = false;
}







public function setConnectionTimeout($timeout_in_ms)
{

curl_setopt(
$this->curl,

156,
$timeout_in_ms
);

return $this;
}







public function setRequestTimeout($timeout_in_ms)
{


curl_setopt(
$this->curl,

155,
$timeout_in_ms
);

return $this;
}




public function execute(WebDriverCommand $command)
{
$http_options = $this->getCommandHttpOptions($command);
$http_method = $http_options['method'];
$url = $http_options['url'];

$sessionID = $command->getSessionID();
$url = str_replace(':sessionId', $sessionID ?? '', $url);
$params = $command->getParameters();
foreach ($params as $name => $value) {
if ($name[0] === ':') {
$url = str_replace($name, $value, $url);
unset($params[$name]);
}
}

if (is_array($params) && !empty($params) && $http_method !== 'POST') {
throw LogicException::forInvalidHttpMethod($url, $http_method, $params);
}

curl_setopt($this->curl, CURLOPT_URL, $this->url . $url);


if ($command->getName() === DriverCommand::NEW_SESSION) {
curl_setopt($this->curl, CURLOPT_POST, 1);
} else {
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $http_method);
}

if (in_array($http_method, ['POST', 'PUT'], true)) {


curl_setopt($this->curl, CURLOPT_HTTPHEADER, array_merge(static::DEFAULT_HTTP_HEADERS, ['Expect:']));
} else {
curl_setopt($this->curl, CURLOPT_HTTPHEADER, static::DEFAULT_HTTP_HEADERS);
}

$encoded_params = null;

if ($http_method === 'POST') {
if (is_array($params) && !empty($params)) {
$encoded_params = json_encode($params);
} elseif ($this->isW3cCompliant) {

$encoded_params = '{}';
}
}

curl_setopt($this->curl, CURLOPT_POSTFIELDS, $encoded_params);

$raw_results = trim(curl_exec($this->curl));

if ($error = curl_error($this->curl)) {
throw WebDriverCurlException::forCurlError($http_method, $url, $error, is_array($params) ? $params : null);
}

$results = json_decode($raw_results, true);

if ($results === null && json_last_error() !== JSON_ERROR_NONE) {
throw UnexpectedResponseException::forJsonDecodingError(json_last_error(), $raw_results);
}

$value = null;
if (is_array($results) && array_key_exists('value', $results)) {
$value = $results['value'];
}

$message = null;
if (is_array($value) && array_key_exists('message', $value)) {
$message = $value['message'];
}

$sessionId = null;
if (is_array($value) && array_key_exists('sessionId', $value)) {

$sessionId = $value['sessionId'];
} elseif (is_array($results) && array_key_exists('sessionId', $results)) {

$sessionId = $results['sessionId'];
}


if (isset($value['error'])) {

WebDriverException::throwException($value['error'], $message, $results);
}

$status = $results['status'] ?? 0;
if ($status !== 0) {

WebDriverException::throwException($status, $message, $results);
}

$response = new WebDriverResponse($sessionId);

return $response
->setStatus($status)
->setValue($value);
}




public function getAddressOfRemoteServer()
{
return $this->url;
}




protected function getCommandHttpOptions(WebDriverCommand $command)
{
$commandName = $command->getName();
if (!isset(self::$commands[$commandName])) {
if ($this->isW3cCompliant && !isset(self::$w3cCompliantCommands[$commandName])) {
throw LogicException::forError($command->getName() . ' is not a valid command.');
}
}

if ($this->isW3cCompliant) {
$raw = self::$w3cCompliantCommands[$command->getName()];
} else {
$raw = self::$commands[$command->getName()];
}

if ($command instanceof CustomWebDriverCommand) {
$url = $command->getCustomUrl();
$method = $command->getCustomMethod();
} else {
$url = $raw['url'];
$method = $raw['method'];
}

return [
'url' => $url,
'method' => $method,
];
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Interactions\WebDriverActions;
use Facebook\WebDriver\JavaScriptExecutor;
use Facebook\WebDriver\Support\IsElementDisplayedAtom;
use Facebook\WebDriver\Support\ScreenshotHelper;
use Facebook\WebDriver\WebDriver;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverCapabilities;
use Facebook\WebDriver\WebDriverCommandExecutor;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverHasInputDevices;
use Facebook\WebDriver\WebDriverNavigation;
use Facebook\WebDriver\WebDriverOptions;
use Facebook\WebDriver\WebDriverWait;

class RemoteWebDriver implements WebDriver, JavaScriptExecutor, WebDriverHasInputDevices
{



protected $executor;



protected $capabilities;




protected $sessionID;



protected $mouse;



protected $keyboard;



protected $touch;



protected $executeMethod;



protected $isW3cCompliant;





protected function __construct(
HttpCommandExecutor $commandExecutor,
$sessionId,
WebDriverCapabilities $capabilities,
$isW3cCompliant = false
) {
$this->executor = $commandExecutor;
$this->sessionID = $sessionId;
$this->isW3cCompliant = $isW3cCompliant;
$this->capabilities = $capabilities;
}














public static function create(
$selenium_server_url = 'http://localhost:4444/wd/hub',
$desired_capabilities = null,
$connection_timeout_in_ms = null,
$request_timeout_in_ms = null,
$http_proxy = null,
$http_proxy_port = null,
DesiredCapabilities $required_capabilities = null
) {
$selenium_server_url = preg_replace('#/+$#', '', $selenium_server_url);

$desired_capabilities = self::castToDesiredCapabilitiesObject($desired_capabilities);

$executor = new HttpCommandExecutor($selenium_server_url, $http_proxy, $http_proxy_port);
if ($connection_timeout_in_ms !== null) {
$executor->setConnectionTimeout($connection_timeout_in_ms);
}
if ($request_timeout_in_ms !== null) {
$executor->setRequestTimeout($request_timeout_in_ms);
}


$parameters = [
'capabilities' => [
'firstMatch' => [(object) $desired_capabilities->toW3cCompatibleArray()],
],
];

if ($required_capabilities !== null && !empty($required_capabilities->toArray())) {
$parameters['capabilities']['alwaysMatch'] = (object) $required_capabilities->toW3cCompatibleArray();
}


if ($required_capabilities !== null) {



$desired_capabilities->setCapability('requiredCapabilities', (object) $required_capabilities->toArray());
}

$parameters['desiredCapabilities'] = (object) $desired_capabilities->toArray();

$command = WebDriverCommand::newSession($parameters);

$response = $executor->execute($command);

return static::createFromResponse($response, $executor);
}






















public static function createBySessionID(
$session_id,
$selenium_server_url = 'http://localhost:4444/wd/hub',
$connection_timeout_in_ms = null,
$request_timeout_in_ms = null
) {

$isW3cCompliant = func_num_args() > 4 ? func_get_arg(4) : true;
$existingCapabilities = func_num_args() > 5 ? func_get_arg(5) : null;

$executor = new HttpCommandExecutor($selenium_server_url, null, null);
if ($connection_timeout_in_ms !== null) {
$executor->setConnectionTimeout($connection_timeout_in_ms);
}
if ($request_timeout_in_ms !== null) {
$executor->setRequestTimeout($request_timeout_in_ms);
}

if (!$isW3cCompliant) {
$executor->disableW3cCompliance();
}


if ($existingCapabilities === null) {
$existingCapabilities = self::readExistingCapabilitiesFromSeleniumGrid($session_id, $executor);
}

return new static($executor, $session_id, $existingCapabilities, $isW3cCompliant);
}






public function close()
{
$this->execute(DriverCommand::CLOSE, []);

return $this;
}








public function newWindow()
{
return $this->switchTo()->newWindow();
}







public function findElement(WebDriverBy $by)
{
$raw_element = $this->execute(
DriverCommand::FIND_ELEMENT,
JsonWireCompat::getUsing($by, $this->isW3cCompliant)
);

return $this->newElement(JsonWireCompat::getElement($raw_element));
}







public function findElements(WebDriverBy $by)
{
$raw_elements = $this->execute(
DriverCommand::FIND_ELEMENTS,
JsonWireCompat::getUsing($by, $this->isW3cCompliant)
);

if (!is_array($raw_elements)) {
throw UnexpectedResponseException::forError('Server response to findElements command is not an array');
}

$elements = [];
foreach ($raw_elements as $raw_element) {
$elements[] = $this->newElement(JsonWireCompat::getElement($raw_element));
}

return $elements;
}








public function get($url)
{
$params = ['url' => (string) $url];
$this->execute(DriverCommand::GET, $params);

return $this;
}






public function getCurrentURL()
{
return $this->execute(DriverCommand::GET_CURRENT_URL);
}






public function getPageSource()
{
return $this->execute(DriverCommand::GET_PAGE_SOURCE);
}






public function getTitle()
{
return $this->execute(DriverCommand::GET_TITLE);
}






public function getWindowHandle()
{
return $this->execute(
DriverCommand::GET_CURRENT_WINDOW_HANDLE,
[]
);
}









public function getWindowHandles()
{
return $this->execute(DriverCommand::GET_WINDOW_HANDLES, []);
}




public function quit()
{
$this->execute(DriverCommand::QUIT);
$this->executor = null;
}









public function executeScript($script, array $arguments = [])
{
$params = [
'script' => $script,
'args' => $this->prepareScriptArguments($arguments),
];

return $this->execute(DriverCommand::EXECUTE_SCRIPT, $params);
}













public function executeAsyncScript($script, array $arguments = [])
{
$params = [
'script' => $script,
'args' => $this->prepareScriptArguments($arguments),
];

return $this->execute(
DriverCommand::EXECUTE_ASYNC_SCRIPT,
$params
);
}







public function takeScreenshot($save_as = null)
{
return (new ScreenshotHelper($this->getExecuteMethod()))->takePageScreenshot($save_as);
}




public function getStatus()
{
$response = $this->execute(DriverCommand::STATUS);

return RemoteStatus::createFromResponse($response);
}















public function wait($timeout_in_second = 30, $interval_in_millisecond = 250)
{
return new WebDriverWait(
$this,
$timeout_in_second,
$interval_in_millisecond
);
}






public function manage()
{
return new WebDriverOptions($this->getExecuteMethod(), $this->isW3cCompliant);
}







public function navigate()
{
return new WebDriverNavigation($this->getExecuteMethod());
}







public function switchTo()
{
return new RemoteTargetLocator($this->getExecuteMethod(), $this, $this->isW3cCompliant);
}




public function getMouse()
{
if (!$this->mouse) {
$this->mouse = new RemoteMouse($this->getExecuteMethod(), $this->isW3cCompliant);
}

return $this->mouse;
}




public function getKeyboard()
{
if (!$this->keyboard) {
$this->keyboard = new RemoteKeyboard($this->getExecuteMethod(), $this, $this->isW3cCompliant);
}

return $this->keyboard;
}




public function getTouch()
{
if (!$this->touch) {
$this->touch = new RemoteTouchScreen($this->getExecuteMethod());
}

return $this->touch;
}






public function action()
{
return new WebDriverActions($this);
}










public function setCommandExecutor(WebDriverCommandExecutor $executor)
{
$this->executor = $executor;

return $this;
}






public function getCommandExecutor()
{
return $this->executor;
}










public function setSessionID($session_id)
{
$this->sessionID = $session_id;

return $this;
}






public function getSessionID()
{
return $this->sessionID;
}






public function getCapabilities()
{
return $this->capabilities;
}









public static function getAllSessions($selenium_server_url = 'http://localhost:4444/wd/hub', $timeout_in_ms = 30000)
{
$executor = new HttpCommandExecutor($selenium_server_url, null, null);
$executor->setConnectionTimeout($timeout_in_ms);

$command = new WebDriverCommand(
null,
DriverCommand::GET_ALL_SESSIONS,
[]
);

return $executor->execute($command)->getValue();
}

public function execute($command_name, $params = [])
{


if ($command_name === DriverCommand::IS_ELEMENT_DISPLAYED
&& (

$this->getCapabilities() === null

|| IsElementDisplayedAtom::match($this->getCapabilities()->getBrowserName())
)
) {
return (new IsElementDisplayedAtom($this))->execute($params);
}

$command = new WebDriverCommand(
$this->sessionID,
$command_name,
$params
);

if ($this->executor) {
$response = $this->executor->execute($command);

return $response->getValue();
}

return null;
}











public function executeCustomCommand($endpointUrl, $method = 'GET', $params = [])
{
$command = new CustomWebDriverCommand(
$this->sessionID,
$endpointUrl,
$method,
$params
);

if ($this->executor) {
$response = $this->executor->execute($command);

return $response->getValue();
}

return null;
}





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








protected static function createFromResponse(WebDriverResponse $response, HttpCommandExecutor $commandExecutor)
{
$responseValue = $response->getValue();

if (!$isW3cCompliant = isset($responseValue['capabilities'])) {
$commandExecutor->disableW3cCompliance();
}

if ($isW3cCompliant) {
$returnedCapabilities = DesiredCapabilities::createFromW3cCapabilities($responseValue['capabilities']);
} else {
$returnedCapabilities = new DesiredCapabilities($responseValue);
}

return new static($commandExecutor, $response->getSessionID(), $returnedCapabilities, $isW3cCompliant);
}






protected function prepareScriptArguments(array $arguments)
{
$args = [];
foreach ($arguments as $key => $value) {
if ($value instanceof WebDriverElement) {
$args[$key] = [
$this->isW3cCompliant ?
JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER
: 'ELEMENT' => $value->getID(),
];
} else {
if (is_array($value)) {
$value = $this->prepareScriptArguments($value);
}
$args[$key] = $value;
}
}

return $args;
}




protected function getExecuteMethod()
{
if (!$this->executeMethod) {
$this->executeMethod = new RemoteExecuteMethod($this);
}

return $this->executeMethod;
}







protected function newElement($id)
{
return new RemoteWebElement($this->getExecuteMethod(), $id, $this->isW3cCompliant);
}








protected static function castToDesiredCapabilitiesObject($desired_capabilities = null)
{
if ($desired_capabilities === null) {
return new DesiredCapabilities();
}

if (is_array($desired_capabilities)) {
return new DesiredCapabilities($desired_capabilities);
}

return $desired_capabilities;
}

protected static function readExistingCapabilitiesFromSeleniumGrid(
string $session_id,
HttpCommandExecutor $executor
): DesiredCapabilities {
$getCapabilitiesCommand = new CustomWebDriverCommand($session_id, '/se/grid/session/:sessionId', 'GET', []);

try {
$capabilitiesResponse = $executor->execute($getCapabilitiesCommand);

$existingCapabilities = DesiredCapabilities::createFromW3cCapabilities(
$capabilitiesResponse->getValue()['capabilities']
);
if ($existingCapabilities === null) {
throw UnexpectedResponseException::forError('Empty capabilities received');
}
} catch (\Exception $e) {
throw UnexpectedResponseException::forCapabilitiesRetrievalError($e);
}

return $existingCapabilities;
}
}
<?php

namespace Facebook\WebDriver\Remote;

class UselessFileDetector implements FileDetector
{
public function getLocalFile($file)
{
return null;
}
}
<?php

namespace Facebook\WebDriver\Remote;

interface FileDetector
{








public function getLocalFile($file);
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Firefox\FirefoxDriver;
use Facebook\WebDriver\Firefox\FirefoxOptions;
use Facebook\WebDriver\Firefox\FirefoxProfile;
use Facebook\WebDriver\WebDriverCapabilities;
use Facebook\WebDriver\WebDriverPlatform;

class DesiredCapabilities implements WebDriverCapabilities
{

private $capabilities;


private static $ossToW3c = [
WebDriverCapabilityType::PLATFORM => 'platformName',
WebDriverCapabilityType::VERSION => 'browserVersion',
WebDriverCapabilityType::ACCEPT_SSL_CERTS => 'acceptInsecureCerts',
];

public function __construct(array $capabilities = [])
{
$this->capabilities = $capabilities;
}

public static function createFromW3cCapabilities(array $capabilities = [])
{
$w3cToOss = array_flip(self::$ossToW3c);

foreach ($w3cToOss as $w3cCapability => $ossCapability) {

if (array_key_exists($w3cCapability, $capabilities)) {
$capabilities[$ossCapability] = $capabilities[$w3cCapability];
}
}

return new self($capabilities);
}




public function getBrowserName()
{
return $this->get(WebDriverCapabilityType::BROWSER_NAME, '');
}





public function setBrowserName($browser_name)
{
$this->set(WebDriverCapabilityType::BROWSER_NAME, $browser_name);

return $this;
}




public function getVersion()
{
return $this->get(WebDriverCapabilityType::VERSION, '');
}





public function setVersion($version)
{
$this->set(WebDriverCapabilityType::VERSION, $version);

return $this;
}





public function getCapability($name)
{
return $this->get($name);
}






public function setCapability($name, $value)
{




if ($name === FirefoxOptions::CAPABILITY && is_array($value)) {
$defaultOptions = (new FirefoxOptions())->toArray();
$value = array_merge($defaultOptions, $value);
}

$this->set($name, $value);

return $this;
}




public function getPlatform()
{
return $this->get(WebDriverCapabilityType::PLATFORM, '');
}





public function setPlatform($platform)
{
$this->set(WebDriverCapabilityType::PLATFORM, $platform);

return $this;
}





public function is($capability_name)
{
return (bool) $this->get($capability_name);
}






public function isJavascriptEnabled()
{
return $this->get(WebDriverCapabilityType::JAVASCRIPT_ENABLED, false);
}









public function setJavascriptEnabled($enabled)
{
$browser = $this->getBrowserName();
if ($browser && $browser !== WebDriverBrowserType::HTMLUNIT) {
throw new UnsupportedOperationException(
'isJavascriptEnabled() is a htmlunit-only option. ' .
'See https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#read-write-capabilities.'
);
}

$this->set(WebDriverCapabilityType::JAVASCRIPT_ENABLED, $enabled);

return $this;
}





public function toArray()
{
if (isset($this->capabilities[ChromeOptions::CAPABILITY]) &&
$this->capabilities[ChromeOptions::CAPABILITY] instanceof ChromeOptions
) {
$this->capabilities[ChromeOptions::CAPABILITY] =
$this->capabilities[ChromeOptions::CAPABILITY]->toArray();
}

if (isset($this->capabilities[FirefoxOptions::CAPABILITY]) &&
$this->capabilities[FirefoxOptions::CAPABILITY] instanceof FirefoxOptions
) {
$this->capabilities[FirefoxOptions::CAPABILITY] =
$this->capabilities[FirefoxOptions::CAPABILITY]->toArray();
}

if (isset($this->capabilities[FirefoxDriver::PROFILE]) &&
$this->capabilities[FirefoxDriver::PROFILE] instanceof FirefoxProfile
) {
$this->capabilities[FirefoxDriver::PROFILE] =
$this->capabilities[FirefoxDriver::PROFILE]->encode();
}

return $this->capabilities;
}




public function toW3cCompatibleArray()
{
$allowedW3cCapabilities = [
'browserName',
'browserVersion',
'platformName',
'acceptInsecureCerts',
'pageLoadStrategy',
'proxy',
'setWindowRect',
'timeouts',
'strictFileInteractability',
'unhandledPromptBehavior',
];

$ossCapabilities = $this->toArray();
$w3cCapabilities = [];

foreach ($ossCapabilities as $capabilityKey => $capabilityValue) {

if (in_array($capabilityKey, $allowedW3cCapabilities, true)) {
$w3cCapabilities[$capabilityKey] = $capabilityValue;
}


if (array_key_exists($capabilityKey, self::$ossToW3c)) {
if ($capabilityKey === WebDriverCapabilityType::PLATFORM) {
$w3cCapabilities[self::$ossToW3c[$capabilityKey]] = mb_strtolower($capabilityValue);


if ($w3cCapabilities[self::$ossToW3c[$capabilityKey]] === 'any') {
unset($w3cCapabilities[self::$ossToW3c[$capabilityKey]]);
}
} else {
$w3cCapabilities[self::$ossToW3c[$capabilityKey]] = $capabilityValue;
}
}


if (mb_strpos($capabilityKey, ':') !== false) {
$w3cCapabilities[$capabilityKey] = $capabilityValue;
}
}


if (array_key_exists(ChromeOptions::CAPABILITY, $ossCapabilities)) {
$w3cCapabilities[ChromeOptions::CAPABILITY] = $ossCapabilities[ChromeOptions::CAPABILITY];
}


if (array_key_exists(FirefoxDriver::PROFILE, $ossCapabilities)) {

if (!array_key_exists(FirefoxOptions::CAPABILITY, $ossCapabilities)
|| !array_key_exists('profile', $ossCapabilities[FirefoxOptions::CAPABILITY])) {
$w3cCapabilities[FirefoxOptions::CAPABILITY]['profile'] = $ossCapabilities[FirefoxDriver::PROFILE];
}
}

return $w3cCapabilities;
}




public static function android()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::ANDROID,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANDROID,
]);
}




public static function chrome()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::CHROME,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);
}




public static function firefox()
{
$caps = new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::FIREFOX,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);

$caps->setCapability(FirefoxOptions::CAPABILITY, new FirefoxOptions()); 

return $caps;
}




public static function htmlUnit()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::HTMLUNIT,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);
}




public static function htmlUnitWithJS()
{
$caps = new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::HTMLUNIT,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);

return $caps->setJavascriptEnabled(true);
}




public static function internetExplorer()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IE,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::WINDOWS,
]);
}




public static function microsoftEdge()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::MICROSOFT_EDGE,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::WINDOWS,
]);
}




public static function iphone()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IPHONE,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::MAC,
]);
}




public static function ipad()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IPAD,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::MAC,
]);
}




public static function opera()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::OPERA,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);
}




public static function safari()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::SAFARI,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);
}






public static function phantomjs()
{
return new static([
WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::PHANTOMJS,
WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
]);
}






private function set($key, $value)
{
$this->capabilities[$key] = $value;

return $this;
}






private function get($key, $default = null)
{
return $this->capabilities[$key] ?? $default;
}
}
<?php

namespace Facebook\WebDriver\Remote;






class WebDriverBrowserType
{
public const FIREFOX = 'firefox';
public const FIREFOX_PROXY = 'firefoxproxy';
public const FIREFOX_CHROME = 'firefoxchrome';
public const GOOGLECHROME = 'googlechrome';
public const SAFARI = 'safari';
public const SAFARI_PROXY = 'safariproxy';
public const OPERA = 'opera';
public const MICROSOFT_EDGE = 'MicrosoftEdge';
public const IEXPLORE = 'iexplore';
public const IEXPLORE_PROXY = 'iexploreproxy';
public const CHROME = 'chrome';
public const KONQUEROR = 'konqueror';
public const MOCK = 'mock';
public const IE_HTA = 'iehta';
public const ANDROID = 'android';
public const HTMLUNIT = 'htmlunit';
public const IE = 'internet explorer';
public const IPHONE = 'iphone';
public const IPAD = 'iPad';




public const PHANTOMJS = 'phantomjs';

private function __construct()
{
}
}
<?php

namespace Facebook\WebDriver\Remote;






class WebDriverCapabilityType
{
public const BROWSER_NAME = 'browserName';
public const VERSION = 'version';
public const PLATFORM = 'platform';
public const JAVASCRIPT_ENABLED = 'javascriptEnabled';
public const TAKES_SCREENSHOT = 'takesScreenshot';
public const HANDLES_ALERTS = 'handlesAlerts';
public const DATABASE_ENABLED = 'databaseEnabled';
public const LOCATION_CONTEXT_ENABLED = 'locationContextEnabled';
public const APPLICATION_CACHE_ENABLED = 'applicationCacheEnabled';
public const BROWSER_CONNECTION_ENABLED = 'browserConnectionEnabled';
public const CSS_SELECTORS_ENABLED = 'cssSelectorsEnabled';
public const WEB_STORAGE_ENABLED = 'webStorageEnabled';
public const ROTATABLE = 'rotatable';
public const ACCEPT_SSL_CERTS = 'acceptSslCerts';
public const NATIVE_EVENTS = 'nativeEvents';
public const PROXY = 'proxy';

private function __construct()
{
}
}
<?php

namespace Facebook\WebDriver\Remote;






class RemoteStatus
{

protected $isReady;

protected $message;

protected $meta = [];





protected function __construct($isReady, $message, array $meta = [])
{
$this->isReady = (bool) $isReady;
$this->message = (string) $message;

$this->setMeta($meta);
}




public static function createFromResponse(array $responseBody)
{
$object = new static($responseBody['ready'], $responseBody['message'], $responseBody);

return $object;
}








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






public function getMessage()
{
return $this->message;
}






public function getMeta()
{
return $this->meta;
}

protected function setMeta(array $meta)
{
unset($meta['ready'], $meta['message']);

$this->meta = $meta;
}
}
<?php

namespace Facebook\WebDriver\Remote;

class WebDriverCommand
{

protected $sessionID;

protected $name;

protected $parameters;








public function __construct($session_id, $name, $parameters)
{
$this->sessionID = $session_id;
$this->name = $name;
$this->parameters = $parameters;
}




public static function newSession(array $parameters)
{

return new self(null, DriverCommand::NEW_SESSION, $parameters);
}




public function getName()
{
return $this->name;
}




public function getSessionID()
{
return $this->sessionID;
}




public function getParameters()
{
return $this->parameters;
}
}
<?php

namespace Facebook\WebDriver\Remote;

interface ExecuteMethod
{




public function execute($command_name, array $parameters = []);
}
<?php

namespace Facebook\WebDriver\Remote;

class LocalFileDetector implements FileDetector
{





public function getLocalFile($file)
{
if (is_file($file)) {
return realpath($file);
}

return null;
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\WebDriverAlert;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverTargetLocator;




class RemoteTargetLocator implements WebDriverTargetLocator
{

protected $executor;

protected $driver;

protected $isW3cCompliant;

public function __construct(RemoteExecuteMethod $executor, RemoteWebDriver $driver, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->driver = $driver;
$this->isW3cCompliant = $isW3cCompliant;
}




public function defaultContent()
{
$params = ['id' => null];
$this->executor->execute(DriverCommand::SWITCH_TO_FRAME, $params);

return $this->driver;
}







public function frame($frame)
{
if ($this->isW3cCompliant) {
if ($frame instanceof WebDriverElement) {
$id = [JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $frame->getID()];
} elseif ($frame === null) {
$id = null;
} elseif (is_int($frame)) {
$id = $frame;
} else {
throw LogicException::forError(
'In W3C compliance mode frame must be either instance of WebDriverElement, integer or null'
);
}
} else {
if ($frame instanceof WebDriverElement) {
$id = ['ELEMENT' => $frame->getID()];
} elseif ($frame === null) {
$id = null;
} elseif (is_int($frame)) {
$id = $frame;
} else {
$id = (string) $frame;
}
}

$params = ['id' => $id];
$this->executor->execute(DriverCommand::SWITCH_TO_FRAME, $params);

return $this->driver;
}






public function parent()
{
$this->executor->execute(DriverCommand::SWITCH_TO_PARENT_FRAME, []);

return $this->driver;
}





public function window($handle)
{
if ($this->isW3cCompliant) {
$params = ['handle' => (string) $handle];
} else {
$params = ['name' => (string) $handle];
}

$this->executor->execute(DriverCommand::SWITCH_TO_WINDOW, $params);

return $this->driver;
}











public function newWindow($windowType = self::WINDOW_TYPE_TAB)
{
if ($windowType !== self::WINDOW_TYPE_TAB && $windowType !== self::WINDOW_TYPE_WINDOW) {
throw LogicException::forError('Window type must by either "tab" or "window"');
}

if (!$this->isW3cCompliant) {
throw LogicException::forError('New window is only supported in W3C mode');
}

$response = $this->executor->execute(DriverCommand::NEW_WINDOW, ['type' => $windowType]);

$this->window($response['handle']);

return $this->driver;
}

public function alert()
{
return new WebDriverAlert($this->executor);
}




public function activeElement()
{
$response = $this->driver->execute(DriverCommand::GET_ACTIVE_ELEMENT, []);
$method = new RemoteExecuteMethod($this->driver);

return new RemoteWebElement($method, JsonWireCompat::getElement($response), $this->isW3cCompliant);
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;
use Facebook\WebDriver\WebDriverElement;




class RemoteTouchScreen implements WebDriverTouchScreen
{



private $executor;

public function __construct(RemoteExecuteMethod $executor)
{
$this->executor = $executor;
}




public function tap(WebDriverElement $element)
{
$this->executor->execute(
DriverCommand::TOUCH_SINGLE_TAP,
['element' => $element->getID()]
);

return $this;
}




public function doubleTap(WebDriverElement $element)
{
$this->executor->execute(
DriverCommand::TOUCH_DOUBLE_TAP,
['element' => $element->getID()]
);

return $this;
}







public function down($x, $y)
{
$this->executor->execute(DriverCommand::TOUCH_DOWN, [
'x' => $x,
'y' => $y,
]);

return $this;
}







public function flick($xspeed, $yspeed)
{
$this->executor->execute(DriverCommand::TOUCH_FLICK, [
'xspeed' => $xspeed,
'yspeed' => $yspeed,
]);

return $this;
}








public function flickFromElement(WebDriverElement $element, $xoffset, $yoffset, $speed)
{
$this->executor->execute(DriverCommand::TOUCH_FLICK, [
'xoffset' => $xoffset,
'yoffset' => $yoffset,
'element' => $element->getID(),
'speed' => $speed,
]);

return $this;
}




public function longPress(WebDriverElement $element)
{
$this->executor->execute(
DriverCommand::TOUCH_LONG_PRESS,
['element' => $element->getID()]
);

return $this;
}







public function move($x, $y)
{
$this->executor->execute(DriverCommand::TOUCH_MOVE, [
'x' => $x,
'y' => $y,
]);

return $this;
}







public function scroll($xoffset, $yoffset)
{
$this->executor->execute(DriverCommand::TOUCH_SCROLL, [
'xoffset' => $xoffset,
'yoffset' => $yoffset,
]);

return $this;
}







public function scrollFromElement(WebDriverElement $element, $xoffset, $yoffset)
{
$this->executor->execute(DriverCommand::TOUCH_SCROLL, [
'element' => $element->getID(),
'xoffset' => $xoffset,
'yoffset' => $yoffset,
]);

return $this;
}







public function up($x, $y)
{
$this->executor->execute(DriverCommand::TOUCH_UP, [
'x' => $x,
'y' => $y,
]);

return $this;
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\WebDriverException;

class CustomWebDriverCommand extends WebDriverCommand
{
public const METHOD_GET = 'GET';
public const METHOD_POST = 'POST';


private $customUrl;

private $customMethod;






public function __construct($session_id, $url, $method, array $parameters)
{
$this->setCustomRequestParameters($url, $method);

parent::__construct($session_id, DriverCommand::CUSTOM_COMMAND, $parameters);
}





public function getCustomUrl()
{
if ($this->customUrl === null) {
throw LogicException::forError('URL of custom command is not set');
}

return $this->customUrl;
}





public function getCustomMethod()
{
if ($this->customMethod === null) {
throw LogicException::forError('Method of custom command is not set');
}

return $this->customMethod;
}






protected function setCustomRequestParameters($custom_url, $custom_method)
{
$allowedMethods = [static::METHOD_GET, static::METHOD_POST];
if (!in_array($custom_method, $allowedMethods, true)) {
throw LogicException::forError(
sprintf(
'Invalid custom method "%s", must be one of [%s]',
$custom_method,
implode(', ', $allowedMethods)
)
);
}
$this->customMethod = $custom_method;

if (mb_strpos($custom_url, '/') !== 0) {
throw LogicException::forError(
sprintf('URL of custom command has to start with / but is "%s"', $custom_url)
);
}
$this->customUrl = $custom_url;
}
}
<?php

namespace Facebook\WebDriver\Remote;

use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\UnknownErrorException;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverSearchContext;

class ShadowRoot implements WebDriverSearchContext
{





public const SHADOW_ROOT_IDENTIFIER = 'shadow-6066-11e4-a52e-4f735466cecf';




private $executor;




private $id;

public function __construct(RemoteExecuteMethod $executor, $id)
{
$this->executor = $executor;
$this->id = $id;
}




public static function createFromResponse(RemoteExecuteMethod $executor, array $response)
{
if (empty($response[self::SHADOW_ROOT_IDENTIFIER])) {
throw new UnknownErrorException('Shadow root is missing in server response');
}

return new self($executor, $response[self::SHADOW_ROOT_IDENTIFIER]);
}




public function findElement(WebDriverBy $locator)
{
$params = JsonWireCompat::getUsing($locator, true);
$params[':id'] = $this->id;

$rawElement = $this->executor->execute(
DriverCommand::FIND_ELEMENT_FROM_SHADOW_ROOT,
$params
);

return new RemoteWebElement($this->executor, JsonWireCompat::getElement($rawElement), true);
}




public function findElements(WebDriverBy $locator)
{
$params = JsonWireCompat::getUsing($locator, true);
$params[':id'] = $this->id;

$rawElements = $this->executor->execute(
DriverCommand::FIND_ELEMENTS_FROM_SHADOW_ROOT,
$params
);

if (!is_array($rawElements)) {
throw UnexpectedResponseException::forError(
'Server response to findElementsFromShadowRoot command is not an array'
);
}

$elements = [];
foreach ($rawElements as $rawElement) {
$elements[] = new RemoteWebElement($this->executor, JsonWireCompat::getElement($rawElement), true);
}

return $elements;
}




public function getID()
{
return $this->id;
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\Internal\LogicException;








class Cookie implements \ArrayAccess
{

protected $cookie = [];





public function __construct($name, $value)
{
$this->validateCookieName($name);
$this->validateCookieValue($value);

$this->cookie['name'] = $name;
$this->cookie['value'] = $value;
}





public static function createFromArray(array $cookieArray)
{
if (!isset($cookieArray['name'])) {
throw LogicException::forError('Cookie name should be set');
}
if (!isset($cookieArray['value'])) {
throw LogicException::forError('Cookie value should be set');
}
$cookie = new self($cookieArray['name'], $cookieArray['value']);

if (isset($cookieArray['path'])) {
$cookie->setPath($cookieArray['path']);
}
if (isset($cookieArray['domain'])) {
$cookie->setDomain($cookieArray['domain']);
}
if (isset($cookieArray['expiry'])) {
$cookie->setExpiry($cookieArray['expiry']);
}
if (isset($cookieArray['secure'])) {
$cookie->setSecure($cookieArray['secure']);
}
if (isset($cookieArray['httpOnly'])) {
$cookie->setHttpOnly($cookieArray['httpOnly']);
}
if (isset($cookieArray['sameSite'])) {
$cookie->setSameSite($cookieArray['sameSite']);
}

return $cookie;
}




public function getName()
{
return $this->offsetGet('name');
}




public function getValue()
{
return $this->offsetGet('value');
}






public function setPath($path)
{
$this->offsetSet('path', $path);
}




public function getPath()
{
return $this->offsetGet('path');
}






public function setDomain($domain)
{
if (mb_strpos($domain, ':') !== false) {
throw LogicException::forError(sprintf('Cookie domain "%s" should not contain a port', $domain));
}

$this->offsetSet('domain', $domain);
}




public function getDomain()
{
return $this->offsetGet('domain');
}






public function setExpiry($expiry)
{
$this->offsetSet('expiry', (int) $expiry);
}




public function getExpiry()
{
return $this->offsetGet('expiry');
}






public function setSecure($secure)
{
$this->offsetSet('secure', $secure);
}




public function isSecure()
{
return $this->offsetGet('secure');
}






public function setHttpOnly($httpOnly)
{
$this->offsetSet('httpOnly', $httpOnly);
}




public function isHttpOnly()
{
return $this->offsetGet('httpOnly');
}






public function setSameSite($sameSite)
{
$this->offsetSet('sameSite', $sameSite);
}




public function getSameSite()
{
return $this->offsetGet('sameSite');
}




public function toArray()
{
$cookie = $this->cookie;
if (!isset($cookie['secure'])) {

$cookie['secure'] = false;
}

return $cookie;
}





#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->cookie[$offset]);
}





#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->cookie[$offset] : null;
}






#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if ($value === null) {
unset($this->cookie[$offset]);
} else {
$this->cookie[$offset] = $value;
}
}





#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->cookie[$offset]);
}




protected function validateCookieName($name)
{
if ($name === null || $name === '') {
throw LogicException::forError('Cookie name should be non-empty');
}

if (mb_strpos($name, ';') !== false) {
throw LogicException::forError('Cookie name should not contain a ";"');
}
}




protected function validateCookieValue($value)
{
if ($value === null) {
throw LogicException::forError('Cookie value is required when setting a cookie');
}
}
}
<?php

namespace Facebook\WebDriver\Support\Events;

use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;
use Facebook\WebDriver\JavaScriptExecutor;
use Facebook\WebDriver\WebDriver;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverDispatcher;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverOptions;
use Facebook\WebDriver\WebDriverTargetLocator;
use Facebook\WebDriver\WebDriverWait;

class EventFiringWebDriver implements WebDriver, JavaScriptExecutor
{



protected $driver;




protected $dispatcher;

public function __construct(WebDriver $driver, WebDriverDispatcher $dispatcher = null)
{
$this->dispatcher = $dispatcher ?: new WebDriverDispatcher();
if (!$this->dispatcher->getDefaultDriver()) {
$this->dispatcher->setDefaultDriver($this);
}
$this->driver = $driver;
}




public function getDispatcher()
{
return $this->dispatcher;
}




public function getWebDriver()
{
return $this->driver;
}






public function get($url)
{
$this->dispatch('beforeNavigateTo', $url, $this);

try {
$this->driver->get($url);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
$this->dispatch('afterNavigateTo', $url, $this);

return $this;
}





public function findElements(WebDriverBy $by)
{
$this->dispatch('beforeFindBy', $by, null, $this);
$elements = [];

try {
foreach ($this->driver->findElements($by) as $element) {
$elements[] = $this->newElement($element);
}
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}

$this->dispatch('afterFindBy', $by, null, $this);

return $elements;
}





public function findElement(WebDriverBy $by)
{
$this->dispatch('beforeFindBy', $by, null, $this);

try {
$element = $this->newElement($this->driver->findElement($by));
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}

$this->dispatch('afterFindBy', $by, null, $this);

return $element;
}






public function executeScript($script, array $arguments = [])
{
if (!$this->driver instanceof JavaScriptExecutor) {
throw new UnsupportedOperationException(
'driver does not implement JavaScriptExecutor'
);
}

$this->dispatch('beforeScript', $script, $this);

try {
$result = $this->driver->executeScript($script, $arguments);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}

$this->dispatch('afterScript', $script, $this);

return $result;
}






public function executeAsyncScript($script, array $arguments = [])
{
if (!$this->driver instanceof JavaScriptExecutor) {
throw new UnsupportedOperationException(
'driver does not implement JavaScriptExecutor'
);
}

$this->dispatch('beforeScript', $script, $this);

try {
$result = $this->driver->executeAsyncScript($script, $arguments);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
$this->dispatch('afterScript', $script, $this);

return $result;
}





public function close()
{
try {
$this->driver->close();

return $this;
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getCurrentURL()
{
try {
return $this->driver->getCurrentURL();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getPageSource()
{
try {
return $this->driver->getPageSource();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getTitle()
{
try {
return $this->driver->getTitle();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getWindowHandle()
{
try {
return $this->driver->getWindowHandle();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getWindowHandles()
{
try {
return $this->driver->getWindowHandles();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}




public function quit()
{
try {
$this->driver->quit();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}






public function takeScreenshot($save_as = null)
{
try {
return $this->driver->takeScreenshot($save_as);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}







public function wait($timeout_in_second = 30, $interval_in_millisecond = 250)
{
try {
return $this->driver->wait($timeout_in_second, $interval_in_millisecond);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function manage()
{
try {
return $this->driver->manage();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function navigate()
{
try {
return new EventFiringWebDriverNavigation(
$this->driver->navigate(),
$this->getDispatcher()
);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function switchTo()
{
try {
return $this->driver->switchTo();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getTouch()
{
try {
return $this->driver->getTouch();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}

public function execute($name, $params)
{
try {
return $this->driver->execute($name, $params);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}




protected function newElement(WebDriverElement $element)
{
return new EventFiringWebElement($element, $this->getDispatcher());
}





protected function dispatch($method, ...$arguments)
{
if (!$this->dispatcher) {
return;
}

$this->dispatcher->dispatch($method, $arguments);
}

protected function dispatchOnException(WebDriverException $exception)
{
$this->dispatch('onException', $exception, $this);
}
}
<?php

namespace Facebook\WebDriver\Support\Events;

use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverDimension;
use Facebook\WebDriver\WebDriverDispatcher;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverPoint;

class EventFiringWebElement implements WebDriverElement, WebDriverLocatable
{



protected $element;



protected $dispatcher;

public function __construct(WebDriverElement $element, WebDriverDispatcher $dispatcher)
{
$this->element = $element;
$this->dispatcher = $dispatcher;
}




public function getDispatcher()
{
return $this->dispatcher;
}




public function getElement()
{
return $this->element;
}






public function sendKeys($value)
{
$this->dispatch('beforeChangeValueOf', $this);

try {
$this->element->sendKeys($value);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
$this->dispatch('afterChangeValueOf', $this);

return $this;
}





public function click()
{
$this->dispatch('beforeClickOn', $this);

try {
$this->element->click();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
$this->dispatch('afterClickOn', $this);

return $this;
}





public function findElement(WebDriverBy $by)
{
$this->dispatch(
'beforeFindBy',
$by,
$this,
$this->dispatcher->getDefaultDriver()
);

try {
$element = $this->newElement($this->element->findElement($by));
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}

$this->dispatch(
'afterFindBy',
$by,
$this,
$this->dispatcher->getDefaultDriver()
);

return $element;
}





public function findElements(WebDriverBy $by)
{
$this->dispatch(
'beforeFindBy',
$by,
$this,
$this->dispatcher->getDefaultDriver()
);

try {
$elements = [];
foreach ($this->element->findElements($by) as $element) {
$elements[] = $this->newElement($element);
}
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
$this->dispatch(
'afterFindBy',
$by,
$this,
$this->dispatcher->getDefaultDriver()
);

return $elements;
}





public function clear()
{
try {
$this->element->clear();

return $this;
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}






public function getAttribute($attribute_name)
{
try {
return $this->element->getAttribute($attribute_name);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}






public function getCSSValue($css_property_name)
{
try {
return $this->element->getCSSValue($css_property_name);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getLocation()
{
try {
return $this->element->getLocation();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getLocationOnScreenOnceScrolledIntoView()
{
try {
return $this->element->getLocationOnScreenOnceScrolledIntoView();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}




public function getCoordinates()
{
try {
return $this->element->getCoordinates();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getSize()
{
try {
return $this->element->getSize();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getTagName()
{
try {
return $this->element->getTagName();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getText()
{
try {
return $this->element->getText();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function isDisplayed()
{
try {
return $this->element->isDisplayed();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function isEnabled()
{
try {
return $this->element->isEnabled();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function isSelected()
{
try {
return $this->element->isSelected();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function submit()
{
try {
$this->element->submit();

return $this;
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}





public function getID()
{
try {
return $this->element->getID();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}






public function equals(WebDriverElement $other)
{
try {
return $this->element->equals($other);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}

public function takeElementScreenshot($save_as = null)
{
try {
return $this->element->takeElementScreenshot($save_as);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}

public function getShadowRoot()
{
try {
return $this->element->getShadowRoot();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}

protected function dispatchOnException(WebDriverException $exception)
{
$this->dispatch(
'onException',
$exception,
$this->dispatcher->getDefaultDriver()
);
}





protected function dispatch($method, ...$arguments)
{
if (!$this->dispatcher) {
return;
}

$this->dispatcher->dispatch($method, $arguments);
}




protected function newElement(WebDriverElement $element)
{
return new static($element, $this->getDispatcher());
}
}
<?php

namespace Facebook\WebDriver\Support\Events;

use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\WebDriverDispatcher;
use Facebook\WebDriver\WebDriverNavigationInterface;

class EventFiringWebDriverNavigation implements WebDriverNavigationInterface
{



protected $navigator;



protected $dispatcher;

public function __construct(WebDriverNavigationInterface $navigator, WebDriverDispatcher $dispatcher)
{
$this->navigator = $navigator;
$this->dispatcher = $dispatcher;
}




public function getDispatcher()
{
return $this->dispatcher;
}




public function getNavigator()
{
return $this->navigator;
}

public function back()
{
$this->dispatch(
'beforeNavigateBack',
$this->getDispatcher()->getDefaultDriver()
);

try {
$this->navigator->back();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
}
$this->dispatch(
'afterNavigateBack',
$this->getDispatcher()->getDefaultDriver()
);

return $this;
}

public function forward()
{
$this->dispatch(
'beforeNavigateForward',
$this->getDispatcher()->getDefaultDriver()
);

try {
$this->navigator->forward();
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
}
$this->dispatch(
'afterNavigateForward',
$this->getDispatcher()->getDefaultDriver()
);

return $this;
}

public function refresh()
{
try {
$this->navigator->refresh();

return $this;
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}
}

public function to($url)
{
$this->dispatch(
'beforeNavigateTo',
$url,
$this->getDispatcher()->getDefaultDriver()
);

try {
$this->navigator->to($url);
} catch (WebDriverException $exception) {
$this->dispatchOnException($exception);
throw $exception;
}

$this->dispatch(
'afterNavigateTo',
$url,
$this->getDispatcher()->getDefaultDriver()
);

return $this;
}





protected function dispatch($method, ...$arguments)
{
if (!$this->dispatcher) {
return;
}

$this->dispatcher->dispatch($method, $arguments);
}

protected function dispatchOnException(WebDriverException $exception)
{
$this->dispatch('onException', $exception);
}
}
<?php

namespace Facebook\WebDriver\Support;

use Facebook\WebDriver\Exception\Internal\IOException;
use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\RemoteExecuteMethod;




class ScreenshotHelper
{

private $executor;

public function __construct(RemoteExecuteMethod $executor)
{
$this->executor = $executor;
}






public function takePageScreenshot($saveAs = null)
{
$commandToExecute = [DriverCommand::SCREENSHOT];

return $this->takeScreenshot($commandToExecute, $saveAs);
}

public function takeElementScreenshot($elementId, $saveAs = null)
{
$commandToExecute = [DriverCommand::TAKE_ELEMENT_SCREENSHOT, [':id' => $elementId]];

return $this->takeScreenshot($commandToExecute, $saveAs);
}

private function takeScreenshot(array $commandToExecute, $saveAs = null)
{
$response = $this->executor->execute(...$commandToExecute);

if (!is_string($response)) {
throw UnexpectedResponseException::forError(
'Error taking screenshot, no data received from the remote end'
);
}

$screenshot = base64_decode($response, true);

if ($screenshot === false) {
throw UnexpectedResponseException::forError('Error decoding screenshot data');
}

if ($saveAs !== null) {
$this->saveScreenshotToPath($screenshot, $saveAs);
}

return $screenshot;
}

private function saveScreenshotToPath($screenshot, $path)
{
$this->createDirectoryIfNotExists(dirname($path));

file_put_contents($path, $screenshot);
}

private function createDirectoryIfNotExists($directoryPath)
{
if (!file_exists($directoryPath)) {
if (!mkdir($directoryPath, 0777, true) && !is_dir($directoryPath)) {
throw IOException::forFileError('Directory cannot be not created', $directoryPath);
}
}
}
}
<?php

namespace Facebook\WebDriver\Support;

class XPathEscaper
{







public static function escapeQuotes($xpathToEscape)
{

if (mb_strpos($xpathToEscape, "'") === false) {
return sprintf("'%s'", $xpathToEscape);
}


if (mb_strpos($xpathToEscape, '"') === false) {
return sprintf('"%s"', $xpathToEscape);
}


return sprintf(
"concat('%s')",
str_replace("'", "', \"'\" ,'", $xpathToEscape)
);
}
}
<?php

namespace Facebook\WebDriver\Support;

use Facebook\WebDriver\Remote\RemoteExecuteMethod;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\RemoteWebElement;
use Facebook\WebDriver\Remote\WebDriverBrowserType;











class IsElementDisplayedAtom
{





public const BROWSERS_WITH_ENDPOINT_SUPPORT = [
WebDriverBrowserType::CHROME,
WebDriverBrowserType::FIREFOX,
WebDriverBrowserType::MICROSOFT_EDGE,
];




private $driver;

public function __construct(RemoteWebDriver $driver)
{
$this->driver = $driver;
}

public static function match($browserName)
{
return !in_array($browserName, self::BROWSERS_WITH_ENDPOINT_SUPPORT, true);
}

public function execute($params)
{
$element = new RemoteWebElement(
new RemoteExecuteMethod($this->driver),
$params[':id'],
$this->driver->isW3cCompliant()
);

return $this->executeAtom('isElementDisplayed', $element);
}

protected function executeAtom($atomName, ...$params)
{
return $this->driver->executeScript(
sprintf('%s; return (%s).apply(null, arguments);', $this->loadAtomScript($atomName), $atomName),
$params
);
}

private function loadAtomScript($atomName)
{
return file_get_contents(__DIR__ . '/../scripts/' . $atomName . '.js');
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\NoSuchCookieException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;




class WebDriverOptions
{



protected $executor;



protected $isW3cCompliant;

public function __construct(ExecuteMethod $executor, $isW3cCompliant = false)
{
$this->executor = $executor;
$this->isW3cCompliant = $isW3cCompliant;
}








public function addCookie($cookie)
{
if (is_array($cookie)) { 
$cookie = Cookie::createFromArray($cookie);
}
if (!$cookie instanceof Cookie) {
throw LogicException::forError('Cookie must be set from instance of Cookie class or from array.');
}

$this->executor->execute(
DriverCommand::ADD_COOKIE,
['cookie' => $cookie->toArray()]
);

return $this;
}






public function deleteAllCookies()
{
$this->executor->execute(DriverCommand::DELETE_ALL_COOKIES);

return $this;
}







public function deleteCookieNamed($name)
{
$this->executor->execute(
DriverCommand::DELETE_COOKIE,
[':name' => $name]
);

return $this;
}








public function getCookieNamed($name)
{
if ($this->isW3cCompliant) {
$cookieArray = $this->executor->execute(
DriverCommand::GET_NAMED_COOKIE,
[':name' => $name]
);

if (!is_array($cookieArray)) { 
throw new NoSuchCookieException('no such cookie');
}

return Cookie::createFromArray($cookieArray);
}

$cookies = $this->getCookies();
foreach ($cookies as $cookie) {
if ($cookie['name'] === $name) {
return $cookie;
}
}

return null;
}






public function getCookies()
{
$cookieArrays = $this->executor->execute(DriverCommand::GET_ALL_COOKIES);
if (!is_array($cookieArrays)) { 
return [];
}

$cookies = [];
foreach ($cookieArrays as $cookieArray) {
$cookies[] = Cookie::createFromArray($cookieArray);
}

return $cookies;
}






public function timeouts()
{
return new WebDriverTimeouts($this->executor, $this->isW3cCompliant);
}







public function window()
{
return new WebDriverWindow($this->executor, $this->isW3cCompliant);
}








public function getLog($log_type)
{
return $this->executor->execute(
DriverCommand::GET_LOG,
['type' => $log_type]
);
}







public function getAvailableLogTypes()
{
return $this->executor->execute(DriverCommand::GET_AVAILABLE_LOG_TYPES);
}
}
<?php

namespace Facebook\WebDriver;






class WebDriverKeys
{
public const NULL = "\xEE\x80\x80";
public const CANCEL = "\xEE\x80\x81";
public const HELP = "\xEE\x80\x82";
public const BACKSPACE = "\xEE\x80\x83";
public const TAB = "\xEE\x80\x84";
public const CLEAR = "\xEE\x80\x85";
public const RETURN_KEY = "\xEE\x80\x86";
public const ENTER = "\xEE\x80\x87";
public const SHIFT = "\xEE\x80\x88";
public const CONTROL = "\xEE\x80\x89";
public const ALT = "\xEE\x80\x8A";
public const PAUSE = "\xEE\x80\x8B";
public const ESCAPE = "\xEE\x80\x8C";
public const SPACE = "\xEE\x80\x8D";
public const PAGE_UP = "\xEE\x80\x8E";
public const PAGE_DOWN = "\xEE\x80\x8F";
public const END = "\xEE\x80\x90";
public const HOME = "\xEE\x80\x91";
public const ARROW_LEFT = "\xEE\x80\x92";
public const ARROW_UP = "\xEE\x80\x93";
public const ARROW_RIGHT = "\xEE\x80\x94";
public const ARROW_DOWN = "\xEE\x80\x95";
public const INSERT = "\xEE\x80\x96";
public const DELETE = "\xEE\x80\x97";
public const SEMICOLON = "\xEE\x80\x98";
public const EQUALS = "\xEE\x80\x99";
public const NUMPAD0 = "\xEE\x80\x9A";
public const NUMPAD1 = "\xEE\x80\x9B";
public const NUMPAD2 = "\xEE\x80\x9C";
public const NUMPAD3 = "\xEE\x80\x9D";
public const NUMPAD4 = "\xEE\x80\x9E";
public const NUMPAD5 = "\xEE\x80\x9F";
public const NUMPAD6 = "\xEE\x80\xA0";
public const NUMPAD7 = "\xEE\x80\xA1";
public const NUMPAD8 = "\xEE\x80\xA2";
public const NUMPAD9 = "\xEE\x80\xA3";
public const MULTIPLY = "\xEE\x80\xA4";
public const ADD = "\xEE\x80\xA5";
public const SEPARATOR = "\xEE\x80\xA6";
public const SUBTRACT = "\xEE\x80\xA7";
public const DECIMAL = "\xEE\x80\xA8";
public const DIVIDE = "\xEE\x80\xA9";
public const F1 = "\xEE\x80\xB1";
public const F2 = "\xEE\x80\xB2";
public const F3 = "\xEE\x80\xB3";
public const F4 = "\xEE\x80\xB4";
public const F5 = "\xEE\x80\xB5";
public const F6 = "\xEE\x80\xB6";
public const F7 = "\xEE\x80\xB7";
public const F8 = "\xEE\x80\xB8";
public const F9 = "\xEE\x80\xB9";
public const F10 = "\xEE\x80\xBA";
public const F11 = "\xEE\x80\xBB";
public const F12 = "\xEE\x80\xBC";
public const META = "\xEE\x80\xBD";
public const ZENKAKU_HANKAKU = "\xEE\x80\xC0";
public const RIGHT_SHIFT = "\xEE\x81\x90";
public const RIGHT_CONTROL = "\xEE\x81\x91";
public const RIGHT_ALT = "\xEE\x81\x92";
public const RIGHT_META = "\xEE\x81\x93";
public const NUMPAD_PAGE_UP = "\xEE\x81\x94";
public const NUMPAD_PAGE_DOWN = "\xEE\x81\x95";
public const NUMPAD_END = "\xEE\x81\x96";
public const NUMPAD_HOME = "\xEE\x81\x97";
public const NUMPAD_ARROW_LEFT = "\xEE\x81\x98";
public const NUMPAD_ARROW_UP = "\xEE\x81\x99";
public const NUMPAD_ARROW_RIGHT = "\xEE\x81\x9A";
public const NUMPAD_ARROW_DOWN = "\xEE\x81\x9B";
public const NUMPAD_ARROW_INSERT = "\xEE\x81\x9C";
public const NUMPAD_ARROW_DELETE = "\xEE\x81\x9D";

public const LEFT_SHIFT = self::SHIFT;
public const LEFT_CONTROL = self::CONTROL;
public const LEFT_ALT = self::ALT;
public const LEFT = self::ARROW_LEFT;
public const UP = self::ARROW_UP;
public const RIGHT = self::ARROW_RIGHT;
public const DOWN = self::ARROW_DOWN;
public const COMMAND = self::META;








public static function encode($keys, $isW3cCompliant = false)
{
if (is_numeric($keys)) {
$keys = (string) $keys;
}

if (is_string($keys)) {
$keys = [$keys];
}

if (!is_array($keys)) {
if (!$isW3cCompliant) {
return [];
}

return '';
}

$encoded = [];
foreach ($keys as $key) {
if (is_array($key)) {

$key = implode('', $key) . self::NULL; 
}
$encoded[] = (string) $key;
}

if (!$isW3cCompliant) {
return $encoded;
}

return implode('', $encoded);
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;

class WebDriverNavigation implements WebDriverNavigationInterface
{
protected $executor;

public function __construct(ExecuteMethod $executor)
{
$this->executor = $executor;
}

public function back()
{
$this->executor->execute(DriverCommand::GO_BACK);

return $this;
}

public function forward()
{
$this->executor->execute(DriverCommand::GO_FORWARD);

return $this;
}

public function refresh()
{
$this->executor->execute(DriverCommand::REFRESH);

return $this;
}

public function to($url)
{
$params = ['url' => (string) $url];
$this->executor->execute(DriverCommand::GET, $params);

return $this;
}
}
<?php

namespace Facebook\WebDriver;





interface WebDriverNavigationInterface
{






public function back();







public function forward();







public function refresh();








public function to($url);
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnexpectedTagNameException;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Support\XPathEscaper;




class WebDriverSelect implements WebDriverSelectInterface
{

private $element;

private $isMulti;

public function __construct(WebDriverElement $element)
{
$tag_name = $element->getTagName();

if ($tag_name !== 'select') {
throw new UnexpectedTagNameException('select', $tag_name);
}
$this->element = $element;
$value = $element->getAttribute('multiple');







$this->isMulti = $value === 'true' || $value === 'multiple';
}

public function isMultiple()
{
return $this->isMulti;
}

public function getOptions()
{
return $this->element->findElements(WebDriverBy::tagName('option'));
}

public function getAllSelectedOptions()
{
$selected_options = [];
foreach ($this->getOptions() as $option) {
if ($option->isSelected()) {
$selected_options[] = $option;

if (!$this->isMultiple()) {
return $selected_options;
}
}
}

return $selected_options;
}

public function getFirstSelectedOption()
{
foreach ($this->getOptions() as $option) {
if ($option->isSelected()) {
return $option;
}
}

throw new NoSuchElementException('No options are selected');
}

public function selectByIndex($index)
{
foreach ($this->getOptions() as $option) {
if ($option->getAttribute('index') === (string) $index) {
$this->selectOption($option);

return;
}
}

throw new NoSuchElementException(sprintf('Cannot locate option with index: %d', $index));
}

public function selectByValue($value)
{
$matched = false;
$xpath = './/option[@value = ' . XPathEscaper::escapeQuotes($value) . ']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));

foreach ($options as $option) {
$this->selectOption($option);
if (!$this->isMultiple()) {
return;
}
$matched = true;
}

if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate option with value: %s', $value)
);
}
}

public function selectByVisibleText($text)
{
$matched = false;
$xpath = './/option[normalize-space(.) = ' . XPathEscaper::escapeQuotes($text) . ']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));

foreach ($options as $option) {
$this->selectOption($option);
if (!$this->isMultiple()) {
return;
}
$matched = true;
}



if (!$matched) {
foreach ($this->getOptions() as $option) {
if ($option->getText() === $text) {
$this->selectOption($option);
if (!$this->isMultiple()) {
return;
}
$matched = true;
}
}
}

if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate option with text: %s', $text)
);
}
}

public function selectByVisiblePartialText($text)
{
$matched = false;
$xpath = './/option[contains(normalize-space(.), ' . XPathEscaper::escapeQuotes($text) . ')]';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));

foreach ($options as $option) {
$this->selectOption($option);
if (!$this->isMultiple()) {
return;
}
$matched = true;
}

if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate option with text: %s', $text)
);
}
}

public function deselectAll()
{
if (!$this->isMultiple()) {
throw new UnsupportedOperationException('You may only deselect all options of a multi-select');
}

foreach ($this->getOptions() as $option) {
$this->deselectOption($option);
}
}

public function deselectByIndex($index)
{
if (!$this->isMultiple()) {
throw new UnsupportedOperationException('You may only deselect options of a multi-select');
}

foreach ($this->getOptions() as $option) {
if ($option->getAttribute('index') === (string) $index) {
$this->deselectOption($option);

return;
}
}
}

public function deselectByValue($value)
{
if (!$this->isMultiple()) {
throw new UnsupportedOperationException('You may only deselect options of a multi-select');
}

$xpath = './/option[@value = ' . XPathEscaper::escapeQuotes($value) . ']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
$this->deselectOption($option);
}
}

public function deselectByVisibleText($text)
{
if (!$this->isMultiple()) {
throw new UnsupportedOperationException('You may only deselect options of a multi-select');
}

$xpath = './/option[normalize-space(.) = ' . XPathEscaper::escapeQuotes($text) . ']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
$this->deselectOption($option);
}
}

public function deselectByVisiblePartialText($text)
{
if (!$this->isMultiple()) {
throw new UnsupportedOperationException('You may only deselect options of a multi-select');
}

$xpath = './/option[contains(normalize-space(.), ' . XPathEscaper::escapeQuotes($text) . ')]';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
$this->deselectOption($option);
}
}




protected function selectOption(WebDriverElement $option)
{
if (!$option->isSelected()) {
$option->click();
}
}




protected function deselectOption(WebDriverElement $option)
{
if ($option->isSelected()) {
$option->click();
}
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\NoSuchElementException;




interface WebDriverSearchContext
{







public function findElement(WebDriverBy $locator);







public function findElements(WebDriverBy $locator);
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnsupportedOperationException;




interface WebDriverSelectInterface
{



public function isMultiple();




public function getOptions();




public function getAllSelectedOptions();







public function getFirstSelectedOption();








public function selectByIndex($index);











public function selectByValue($value);











public function selectByVisibleText($text);











public function selectByVisiblePartialText($text);






public function deselectAll();







public function deselectByIndex($index);










public function deselectByValue($value);










public function deselectByVisibleText($text);










public function deselectByVisiblePartialText($text);
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Remote\WebDriverCommand;
use Facebook\WebDriver\Remote\WebDriverResponse;




interface WebDriverCommandExecutor
{



public function execute(WebDriverCommand $command);
}
<?php

namespace Facebook\WebDriver\Internal;

use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;




interface WebDriverLocatable
{



public function getCoordinates();
}
<?php

namespace Facebook\WebDriver;








class WebDriverBy
{



private $mechanism;



private $value;

protected function __construct($mechanism, $value)
{
$this->mechanism = $mechanism;
$this->value = $value;
}




public function getMechanism()
{
return $this->mechanism;
}




public function getValue()
{
return $this->value;
}








public static function className($class_name)
{
return new static('class name', $class_name);
}







public static function cssSelector($css_selector)
{
return new static('css selector', $css_selector);
}







public static function id($id)
{
return new static('id', $id);
}







public static function name($name)
{
return new static('name', $name);
}







public static function linkText($link_text)
{
return new static('link text', $link_text);
}








public static function partialLinkText($partial_link_text)
{
return new static('partial link text', $partial_link_text);
}







public static function tagName($tag_name)
{
return new static('tag name', $tag_name);
}







public static function xpath($xpath)
{
return new static('xpath', $xpath);
}
}
<?php

namespace Facebook\WebDriver\Chrome;

use Facebook\WebDriver\Remote\Service\DriverService;

class ChromeDriverService extends DriverService
{




public const CHROME_DRIVER_EXE_PROPERTY = 'webdriver.chrome.driver';

public const CHROME_DRIVER_EXECUTABLE = 'WEBDRIVER_CHROME_DRIVER';




public const DEFAULT_EXECUTABLE = 'chromedriver';




public static function createDefaultService()
{
$pathToExecutable = getenv(self::CHROME_DRIVER_EXECUTABLE) ?: getenv(self::CHROME_DRIVER_EXE_PROPERTY);
if ($pathToExecutable === false || $pathToExecutable === '') {
$pathToExecutable = static::DEFAULT_EXECUTABLE;
}

$port = 9515; 
$args = ['--port=' . $port];

return new static($pathToExecutable, $port, $args);
}
}
<?php

namespace Facebook\WebDriver\Chrome;

use Facebook\WebDriver\Local\LocalWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\Service\DriverCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;

class ChromeDriver extends LocalWebDriver
{

private $devTools;










public static function start(DesiredCapabilities $desired_capabilities = null, ChromeDriverService $service = null)
{
if ($service === null) { 
$service = ChromeDriverService::createDefaultService();
}

return static::startUsingDriverService($service, $desired_capabilities);
}









public static function startUsingDriverService(
ChromeDriverService $service,
DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::chrome();
}

$executor = new DriverCommandExecutor($service);
$newSessionCommand = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()],
],
'desiredCapabilities' => (object) $capabilities->toArray(),
]
);

$response = $executor->execute($newSessionCommand);





return static::createFromResponse($response, $executor);
}







public function startSession(DesiredCapabilities $desired_capabilities)
{
$command = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $desired_capabilities->toW3cCompatibleArray()],
],
'desiredCapabilities' => (object) $desired_capabilities->toArray(),
]
);
$response = $this->executor->execute($command);
$value = $response->getValue();

if (!$this->isW3cCompliant = isset($value['capabilities'])) {
$this->executor->disableW3cCompliance();
}

$this->sessionID = $response->getSessionID();
}




public function getDevTools()
{
if ($this->devTools === null) {
$this->devTools = new ChromeDevToolsDriver($this);
}

return $this->devTools;
}
}
<?php

namespace Facebook\WebDriver\Chrome;

use Facebook\WebDriver\Remote\DesiredCapabilities;
use JsonSerializable;
use ReturnTypeWillChange;






class ChromeOptions implements JsonSerializable
{



public const CAPABILITY = 'goog:chromeOptions';



public const CAPABILITY_W3C = self::CAPABILITY;



private $arguments = [];



private $binary = '';



private $extensions = [];



private $experimentalOptions = [];






#[ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
}








public function setBinary($path)
{
$this->binary = $path;

return $this;
}




public function addArguments(array $arguments)
{
$this->arguments = array_merge($this->arguments, $arguments);

return $this;
}







public function addExtensions(array $paths)
{
foreach ($paths as $path) {
$this->addExtension($path);
}

return $this;
}





public function addEncodedExtensions(array $encoded_extensions)
{
foreach ($encoded_extensions as $encoded_extension) {
$this->addEncodedExtension($encoded_extension);
}

return $this;
}











public function setExperimentalOption($name, $value)
{
$this->experimentalOptions[$name] = $value;

return $this;
}




public function toCapabilities()
{
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(self::CAPABILITY, $this);

return $capabilities;
}




public function toArray()
{




$options = new \ArrayObject($this->experimentalOptions);

if (!empty($this->binary)) {
$options['binary'] = $this->binary;
}

if (!empty($this->arguments)) {
$options['args'] = $this->arguments;
}

if (!empty($this->extensions)) {
$options['extensions'] = $this->extensions;
}

return $options;
}








private function addExtension($path)
{
$this->addEncodedExtension(base64_encode(file_get_contents($path)));

return $this;
}





private function addEncodedExtension($encoded_extension)
{
$this->extensions[] = $encoded_extension;

return $this;
}
}
<?php

namespace Facebook\WebDriver\Chrome;

use Facebook\WebDriver\Remote\RemoteWebDriver;






class ChromeDevToolsDriver
{
public const SEND_COMMAND = [
'method' => 'POST',
'url' => '/session/:sessionId/goog/cdp/execute',
];




private $driver;

public function __construct(RemoteWebDriver $driver)
{
$this->driver = $driver;
}








public function execute($command, array $parameters = [])
{
$params = ['cmd' => $command, 'params' => (object) $parameters];

return $this->driver->executeCustomCommand(
self::SEND_COMMAND['url'],
self::SEND_COMMAND['method'],
$params
);
}
}
<?php

namespace Facebook\WebDriver\Net;

use Exception;
use Facebook\WebDriver\Exception\TimeoutException;

class URLChecker
{
public const POLL_INTERVAL_MS = 500;
public const CONNECT_TIMEOUT_MS = 500;

public function waitUntilAvailable($timeout_in_ms, $url)
{
$end = microtime(true) + $timeout_in_ms / 1000;

while ($end > microtime(true)) {
if ($this->getHTTPResponseCode($url) === 200) {
return $this;
}
usleep(self::POLL_INTERVAL_MS);
}

throw new TimeoutException(sprintf(
'Timed out waiting for %s to become available after %d ms.',
$url,
$timeout_in_ms
));
}

public function waitUntilUnavailable($timeout_in_ms, $url)
{
$end = microtime(true) + $timeout_in_ms / 1000;

while ($end > microtime(true)) {
if ($this->getHTTPResponseCode($url) !== 200) {
return $this;
}
usleep(self::POLL_INTERVAL_MS);
}

throw new TimeoutException(sprintf(
'Timed out waiting for %s to become unavailable after %d ms.',
$url,
$timeout_in_ms
));
}

private function getHTTPResponseCode($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);


if (!defined('CURLOPT_CONNECTTIMEOUT_MS')) {
define('CURLOPT_CONNECTTIMEOUT_MS', 156); 
}
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, self::CONNECT_TIMEOUT_MS);

$code = null;

try {
curl_exec($ch);
$info = curl_getinfo($ch);
$code = $info['http_code'];
} catch (Exception $e) {
}
curl_close($ch);

return $code;
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\InvalidElementStateException;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnexpectedTagNameException;
use Facebook\WebDriver\Support\XPathEscaper;




abstract class AbstractWebDriverCheckboxOrRadio implements WebDriverSelectInterface
{

protected $element;


protected $type;


protected $name;

public function __construct(WebDriverElement $element)
{
$tagName = $element->getTagName();
if ($tagName !== 'input') {
throw new UnexpectedTagNameException('input', $tagName);
}

$this->name = $element->getAttribute('name');
if ($this->name === null) {
throw new InvalidElementStateException('The input does not have a "name" attribute.');
}

$this->element = $element;
}

public function getOptions()
{
return $this->getRelatedElements();
}

public function getAllSelectedOptions()
{
$selectedElement = [];
foreach ($this->getRelatedElements() as $element) {
if ($element->isSelected()) {
$selectedElement[] = $element;

if (!$this->isMultiple()) {
return $selectedElement;
}
}
}

return $selectedElement;
}

public function getFirstSelectedOption()
{
foreach ($this->getRelatedElements() as $element) {
if ($element->isSelected()) {
return $element;
}
}

throw new NoSuchElementException(
sprintf('No %s are selected', $this->type === 'radio' ? 'radio buttons' : 'checkboxes')
);
}

public function selectByIndex($index)
{
$this->byIndex($index);
}

public function selectByValue($value)
{
$this->byValue($value);
}

public function selectByVisibleText($text)
{
$this->byVisibleText($text);
}

public function selectByVisiblePartialText($text)
{
$this->byVisibleText($text, true);
}








protected function byValue($value, $select = true)
{
$matched = false;
foreach ($this->getRelatedElements($value) as $element) {
$select ? $this->selectOption($element) : $this->deselectOption($element);
if (!$this->isMultiple()) {
return;
}

$matched = true;
}

if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate %s with value: %s', $this->type, $value)
);
}
}








protected function byIndex($index, $select = true)
{
$elements = $this->getRelatedElements();
if (!isset($elements[$index])) {
throw new NoSuchElementException(sprintf('Cannot locate %s with index: %d', $this->type, $index));
}

$select ? $this->selectOption($elements[$index]) : $this->deselectOption($elements[$index]);
}








protected function byVisibleText($text, $partial = false, $select = true)
{
foreach ($this->getRelatedElements() as $element) {
$normalizeFilter = sprintf(
$partial ? 'contains(normalize-space(.), %s)' : 'normalize-space(.) = %s',
XPathEscaper::escapeQuotes($text)
);

$xpath = 'ancestor::label';
$xpathNormalize = sprintf('%s[%s]', $xpath, $normalizeFilter);

$id = $element->getAttribute('id');
if ($id !== null) {
$idFilter = sprintf('@for = %s', XPathEscaper::escapeQuotes($id));

$xpath .= sprintf(' | //label[%s]', $idFilter);
$xpathNormalize .= sprintf(' | //label[%s and %s]', $idFilter, $normalizeFilter);
}

try {
$element->findElement(WebDriverBy::xpath($xpathNormalize));
} catch (NoSuchElementException $e) {
if ($partial) {
continue;
}

try {


if ($text !== $element->findElement(WebDriverBy::xpath($xpath))->getText()) {
continue;
}
} catch (NoSuchElementException $e) {
continue;
}
}

$select ? $this->selectOption($element) : $this->deselectOption($element);
if (!$this->isMultiple()) {
return;
}
}
}







protected function getRelatedElements($value = null)
{
$valueSelector = $value ? sprintf(' and @value = %s', XPathEscaper::escapeQuotes($value)) : '';
$formId = $this->element->getAttribute('form');
if ($formId === null) {
$form = $this->element->findElement(WebDriverBy::xpath('ancestor::form'));

$formId = $form->getAttribute('id');
if ($formId === '' || $formId === null) {
return $form->findElements(WebDriverBy::xpath(
sprintf('.//input[@name = %s%s]', XPathEscaper::escapeQuotes($this->name), $valueSelector)
));
}
}


return $this->element->findElements(
WebDriverBy::xpath(sprintf(
'//form[@id = %1$s]//input[@name = %2$s%3$s'
. ' and ((boolean(@form) = true() and @form = %1$s) or boolean(@form) = false())]'
. ' | //input[@form = %1$s and @name = %2$s%3$s]',
XPathEscaper::escapeQuotes($formId),
XPathEscaper::escapeQuotes($this->name),
$valueSelector
))
);
}




protected function selectOption(WebDriverElement $element)
{
if (!$element->isSelected()) {
$element->click();
}
}




protected function deselectOption(WebDriverElement $element)
{
if ($element->isSelected()) {
$element->click();
}
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;




class WebDriverAlert
{



protected $executor;

public function __construct(ExecuteMethod $executor)
{
$this->executor = $executor;
}






public function accept()
{
$this->executor->execute(DriverCommand::ACCEPT_ALERT);

return $this;
}






public function dismiss()
{
$this->executor->execute(DriverCommand::DISMISS_ALERT);

return $this;
}






public function getText()
{
return $this->executor->execute(DriverCommand::GET_ALERT_TEXT);
}







public function sendKeys($value)
{
$this->executor->execute(
DriverCommand::SET_ALERT_VALUE,
['text' => $value]
);

return $this;
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;




interface WebDriver extends WebDriverSearchContext
{





public function close();







public function get($url);






public function getCurrentURL();






public function getPageSource();






public function getTitle();







public function getWindowHandle();






public function getWindowHandles();




public function quit();







public function takeScreenshot($save_as = null);













public function wait(
$timeout_in_second = 30,
$interval_in_millisecond = 250
);







public function manage();








public function navigate();







public function switchTo();












public function execute($name, $params);













}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Remote\ShadowRoot;




interface WebDriverElement extends WebDriverSearchContext
{





public function clear();






public function click();









public function getAttribute($attribute_name);


















public function getCSSValue($css_property_name);






public function getLocation();







public function getLocationOnScreenOnceScrolledIntoView();






public function getSize();






public function getTagName();







public function getText();







public function isDisplayed();







public function isEnabled();






public function isSelected();







public function sendKeys($value);







public function submit();






public function getID();

















}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\InvalidElementStateException;




class WebDriverCheckboxes extends AbstractWebDriverCheckboxOrRadio
{
public function __construct(WebDriverElement $element)
{
parent::__construct($element);

$this->type = $element->getAttribute('type');
if ($this->type !== 'checkbox') {
throw new InvalidElementStateException('The input must be of type "checkbox".');
}
}

public function isMultiple()
{
return true;
}

public function deselectAll()
{
foreach ($this->getRelatedElements() as $checkbox) {
$this->deselectOption($checkbox);
}
}

public function deselectByIndex($index)
{
$this->byIndex($index, false);
}

public function deselectByValue($value)
{
$this->byValue($value, false);
}

public function deselectByVisibleText($text)
{
$this->byVisibleText($text, false, false);
}

public function deselectByVisiblePartialText($text)
{
$this->byVisibleText($text, true, false);
}
}
<?php

namespace Facebook\WebDriver;




interface JavaScriptExecutor
{









public function executeScript($script, array $arguments = []);














public function executeAsyncScript($script, array $arguments = []);
}
<?php

namespace Facebook\WebDriver;




class WebDriverDimension
{



private $height;



private $width;





public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}






public function getHeight()
{
return (int) $this->height;
}






public function getWidth()
{
return (int) $this->width;
}







public function equals(self $dimension)
{
return $this->height === $dimension->getHeight() && $this->width === $dimension->getWidth();
}
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Support\Events\EventFiringWebDriver;
use Facebook\WebDriver\Support\Events\EventFiringWebElement;

interface WebDriverEventListener
{



public function beforeNavigateTo($url, EventFiringWebDriver $driver);




public function afterNavigateTo($url, EventFiringWebDriver $driver);

public function beforeNavigateBack(EventFiringWebDriver $driver);

public function afterNavigateBack(EventFiringWebDriver $driver);

public function beforeNavigateForward(EventFiringWebDriver $driver);

public function afterNavigateForward(EventFiringWebDriver $driver);

public function beforeFindBy(WebDriverBy $by, $element, EventFiringWebDriver $driver);

public function afterFindBy(WebDriverBy $by, $element, EventFiringWebDriver $driver);




public function beforeScript($script, EventFiringWebDriver $driver);




public function afterScript($script, EventFiringWebDriver $driver);

public function beforeClickOn(EventFiringWebElement $element);

public function afterClickOn(EventFiringWebElement $element);

public function beforeChangeValueOf(EventFiringWebElement $element);

public function afterChangeValueOf(EventFiringWebElement $element);

public function onException(WebDriverException $exception, EventFiringWebDriver $driver = null);
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchFrameException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnableToSetCookieException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class ElementNotSelectableException extends ElementNotInteractableException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class XPathLookupException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnknownErrorException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoStringException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;

use Exception;







class WebDriverException extends Exception implements PhpWebDriverExceptionInterface
{
private $results;





public function __construct($message, $results = null)
{
parent::__construct($message);
$this->results = $results;
}




public function getResults()
{
return $this->results;
}
























































public static function throwException($status_code, $message, $results)
{
if (is_string($status_code)) {

switch ($status_code) {
case 'element click intercepted':
throw new ElementClickInterceptedException($message, $results);
case 'element not interactable':
throw new ElementNotInteractableException($message, $results);
case 'insecure certificate':
throw new InsecureCertificateException($message, $results);
case 'invalid argument':
throw new InvalidArgumentException($message, $results);
case 'invalid cookie domain':
throw new InvalidCookieDomainException($message, $results);
case 'invalid element state':
throw new InvalidElementStateException($message, $results);
case 'invalid selector':
throw new InvalidSelectorException($message, $results);
case 'invalid session id':
throw new InvalidSessionIdException($message, $results);
case 'javascript error':
throw new JavascriptErrorException($message, $results);
case 'move target out of bounds':
throw new MoveTargetOutOfBoundsException($message, $results);
case 'no such alert':
throw new NoSuchAlertException($message, $results);
case 'no such cookie':
throw new NoSuchCookieException($message, $results);
case 'no such element':
throw new NoSuchElementException($message, $results);
case 'no such frame':
throw new NoSuchFrameException($message, $results);
case 'no such window':
throw new NoSuchWindowException($message, $results);
case 'no such shadow root':
throw new NoSuchShadowRootException($message, $results);
case 'script timeout':
throw new ScriptTimeoutException($message, $results);
case 'session not created':
throw new SessionNotCreatedException($message, $results);
case 'stale element reference':
throw new StaleElementReferenceException($message, $results);
case 'detached shadow root':
throw new DetachedShadowRootException($message, $results);
case 'timeout':
throw new TimeoutException($message, $results);
case 'unable to set cookie':
throw new UnableToSetCookieException($message, $results);
case 'unable to capture screen':
throw new UnableToCaptureScreenException($message, $results);
case 'unexpected alert open':
throw new UnexpectedAlertOpenException($message, $results);
case 'unknown command':
throw new UnknownCommandException($message, $results);
case 'unknown error':
throw new UnknownErrorException($message, $results);
case 'unknown method':
throw new UnknownMethodException($message, $results);
case 'unsupported operation':
throw new UnsupportedOperationException($message, $results);
default:
throw new UnrecognizedExceptionException($message, $results);
}
}

switch ($status_code) {
case 1:
throw new IndexOutOfBoundsException($message, $results);
case 2:
throw new NoCollectionException($message, $results);
case 3:
throw new NoStringException($message, $results);
case 4:
throw new NoStringLengthException($message, $results);
case 5:
throw new NoStringWrapperException($message, $results);
case 6:
throw new NoSuchDriverException($message, $results);
case 7:
throw new NoSuchElementException($message, $results);
case 8:
throw new NoSuchFrameException($message, $results);
case 9:
throw new UnknownCommandException($message, $results);
case 10:
throw new StaleElementReferenceException($message, $results);
case 11:
throw new ElementNotVisibleException($message, $results);
case 12:
throw new InvalidElementStateException($message, $results);
case 13:
throw new UnknownServerException($message, $results);
case 14:
throw new ExpectedException($message, $results);
case 15:
throw new ElementNotSelectableException($message, $results);
case 16:
throw new NoSuchDocumentException($message, $results);
case 17:
throw new UnexpectedJavascriptException($message, $results);
case 18:
throw new NoScriptResultException($message, $results);
case 19:
throw new XPathLookupException($message, $results);
case 20:
throw new NoSuchCollectionException($message, $results);
case 21:
throw new TimeoutException($message, $results);
case 22:
throw new NullPointerException($message, $results);
case 23:
throw new NoSuchWindowException($message, $results);
case 24:
throw new InvalidCookieDomainException($message, $results);
case 25:
throw new UnableToSetCookieException($message, $results);
case 26:
throw new UnexpectedAlertOpenException($message, $results);
case 27:
throw new NoAlertOpenException($message, $results);
case 28:
throw new ScriptTimeoutException($message, $results);
case 29:
throw new InvalidCoordinatesException($message, $results);
case 30:
throw new IMENotAvailableException($message, $results);
case 31:
throw new IMEEngineActivationFailedException($message, $results);
case 32:
throw new InvalidSelectorException($message, $results);
case 33:
throw new SessionNotCreatedException($message, $results);
case 34:
throw new MoveTargetOutOfBoundsException($message, $results);
default:
throw new UnrecognizedExceptionException($message, $results);
}
}
}
<?php

namespace Facebook\WebDriver\Exception;





class NoSuchCookieException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class IndexOutOfBoundsException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchCollectionException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchDocumentException extends NoSuchWindowException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class JavascriptErrorException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnknownServerException extends UnknownErrorException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnknownMethodException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnknownCommandException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;





class InvalidSessionIdException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class ExpectedException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;

class UnexpectedTagNameException extends WebDriverException
{




public function __construct(
$expected_tag_name,
$actual_tag_name
) {
parent::__construct(
sprintf(
'Element should have been "%s" but was "%s"',
$expected_tag_name,
$actual_tag_name
)
);
}
}
<?php

namespace Facebook\WebDriver\Exception;




class NoCollectionException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




interface PhpWebDriverExceptionInterface
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchWindowException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class StaleElementReferenceException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoScriptResultException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class IMENotAvailableException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchAlertException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnexpectedJavascriptException extends JavascriptErrorException
{
}
<?php

namespace Facebook\WebDriver\Exception;





class InsecureCertificateException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoAlertOpenException extends NoSuchAlertException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchElementException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoStringWrapperException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoStringLengthException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class ElementNotVisibleException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class TimeoutException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class InvalidArgumentException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class MoveTargetOutOfBoundsException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class ElementNotInteractableException extends WebDriverException
{
}
<?php declare(strict_types=1);

namespace Facebook\WebDriver\Exception\Internal;

use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;




class LogicException extends \LogicException implements PhpWebDriverExceptionInterface
{
public static function forError(string $message): self
{
return new self($message);
}

public static function forInvalidHttpMethod(string $url, string $httpMethod, array $params): self
{
return new self(
sprintf(
'The http method called for "%s" is "%s", but it has to be POST' .
' if you want to pass the JSON params %s',
$url,
$httpMethod,
json_encode($params)
)
);
}
}
<?php declare(strict_types=1);

namespace Facebook\WebDriver\Exception\Internal;

use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;




class IOException extends \LogicException implements PhpWebDriverExceptionInterface
{
public static function forFileError(string $message, string $path): self
{
return new self(sprintf($message . ' ("%s")', $path));
}
}
<?php

namespace Facebook\WebDriver\Exception\Internal;




class WebDriverCurlException extends UnexpectedResponseException
{
public static function forCurlError(string $httpMethod, string $url, string $curlError, ?array $params): self
{
$message = sprintf('Curl error thrown for http %s to %s', $httpMethod, $url);

if (!empty($params)) {
$message .= sprintf(' with params: %s', json_encode($params, JSON_UNESCAPED_SLASHES));
}

$message .= "\n\n" . $curlError;

return new self($message);
}
}
<?php declare(strict_types=1);

namespace Facebook\WebDriver\Exception\Internal;

use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;
use Symfony\Component\Process\Process;




class RuntimeException extends \RuntimeException implements PhpWebDriverExceptionInterface
{
public static function forError(string $message): self
{
return new self($message);
}

public static function forDriverError(Process $process): self
{
return new self(
sprintf(
'Error starting driver executable "%s": %s',
$process->getCommandLine(),
$process->getErrorOutput()
)
);
}
}
<?php declare(strict_types=1);

namespace Facebook\WebDriver\Exception\Internal;

use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;




class UnexpectedResponseException extends \RuntimeException implements PhpWebDriverExceptionInterface
{
public static function forError(string $message): self
{
return new self($message);
}

public static function forElementNotArray($response): self
{
return new self(
sprintf(
"Unexpected server response for getting an element. Expected array, but the response was: '%s'\n",
print_r($response, true)
)
);
}

public static function forJsonDecodingError(int $jsonLastError, string $rawResults): self
{
return new self(
sprintf(
"JSON decoding of remote response failed.\n" .
"Error code: %d\n" .
"The response: '%s'\n",
$jsonLastError,
$rawResults
)
);
}

public static function forCapabilitiesRetrievalError(\Exception $previousException): self
{
return new self(
sprintf(
'Existing Capabilities were not provided, and they also cannot be read from Selenium Grid'
. ' (error: "%s"). You are probably not using Selenium Grid, so to reuse the previous session,'
. ' Capabilities must be explicitly provided to createBySessionID() method.',
$previousException->getMessage()
)
);
}
}
<?php declare(strict_types=1);

namespace Facebook\WebDriver\Exception\Internal;

use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;




class DriverServerDiedException extends \RuntimeException implements PhpWebDriverExceptionInterface
{
public function __construct(\Exception $previous = null)
{
parent::__construct('The driver server has died.', 0, $previous);
}
}
<?php

namespace Facebook\WebDriver\Exception;




class InvalidCookieDomainException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;





class InvalidElementStateException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;

class UnrecognizedExceptionException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;





class ElementClickInterceptedException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class InvalidCoordinatesException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NullPointerException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnableToCaptureScreenException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class ScriptTimeoutException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class InvalidSelectorException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class DetachedShadowRootException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnsupportedOperationException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class UnexpectedAlertOpenException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class IMEEngineActivationFailedException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class SessionNotCreatedException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchShadowRootException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver\Exception;




class NoSuchDriverException extends WebDriverException
{
}
<?php

namespace Facebook\WebDriver;




interface WebDriverHasInputDevices
{



public function getKeyboard();




public function getMouse();
}
<?php

namespace Facebook\WebDriver;

use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;




interface WebDriverMouse
{



public function click(WebDriverCoordinates $where);




public function contextClick(WebDriverCoordinates $where);




public function doubleClick(WebDriverCoordinates $where);




public function mouseDown(WebDriverCoordinates $where);






public function mouseMove(
WebDriverCoordinates $where,
$x_offset = null,
$y_offset = null
);




public function mouseUp(WebDriverCoordinates $where);
}
<?php

namespace Facebook\WebDriver;




class WebDriverPoint
{
private $x;
private $y;

public function __construct($x, $y)
{
$this->x = $x;
$this->y = $y;
}






public function getX()
{
return (int) $this->x;
}






public function getY()
{
return (int) $this->y;
}








public function move($new_x, $new_y)
{
$this->x = $new_x;
$this->y = $new_y;

return $this;
}








public function moveBy($x_offset, $y_offset)
{
$this->x += $x_offset;
$this->y += $y_offset;

return $this;
}







public function equals(self $point)
{
return $this->x === $point->getX() &&
$this->y === $point->getY();
}
}
<?php

namespace Facebook\WebDriver\Firefox;

use Facebook\WebDriver\Local\LocalWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\Service\DriverCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;

class FirefoxDriver extends LocalWebDriver
{







public const PROFILE = 'firefox_profile';









public static function start(DesiredCapabilities $capabilities = null)
{
$service = FirefoxDriverService::createDefaultService();

return static::startUsingDriverService($service, $capabilities);
}









public static function startUsingDriverService(
FirefoxDriverService $service,
DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::firefox();
}

$executor = new DriverCommandExecutor($service);
$newSessionCommand = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()],
],
]
);

$response = $executor->execute($newSessionCommand);

$returnedCapabilities = DesiredCapabilities::createFromW3cCapabilities($response->getValue()['capabilities']);
$sessionId = $response->getSessionID();

return new static($executor, $sessionId, $returnedCapabilities, true);
}
}
<?php

namespace Facebook\WebDriver\Firefox;

use Facebook\WebDriver\Remote\Service\DriverService;

class FirefoxDriverService extends DriverService
{



public const WEBDRIVER_FIREFOX_DRIVER = 'WEBDRIVER_FIREFOX_DRIVER';




public const DEFAULT_EXECUTABLE = 'geckodriver';




public static function createDefaultService()
{
$pathToExecutable = getenv(static::WEBDRIVER_FIREFOX_DRIVER);
if ($pathToExecutable === false || $pathToExecutable === '') {
$pathToExecutable = static::DEFAULT_EXECUTABLE;
}

$port = 9515; 
$args = ['-p=' . $port];

return new static($pathToExecutable, $port, $args);
}
}
<?php

namespace Facebook\WebDriver\Firefox;







class FirefoxPreferences
{

public const WEBDRIVER_FIREFOX_PORT = 'webdriver_firefox_port';

public const READER_PARSE_ON_LOAD_ENABLED = 'reader.parse-on-load.enabled';

public const BROWSER_STARTUP_HOMEPAGE = 'browser.startup.homepage';

public const DEVTOOLS_JSONVIEW = 'devtools.jsonview.enabled';

private function __construct()
{
}
}
<?php

namespace Facebook\WebDriver\Firefox;

use Facebook\WebDriver\Exception\Internal\LogicException;
use ReturnTypeWillChange;






class FirefoxOptions implements \JsonSerializable
{

public const CAPABILITY = 'moz:firefoxOptions';

public const OPTION_ARGS = 'args';

public const OPTION_PREFS = 'prefs';

public const OPTION_PROFILE = 'profile';


private $options = [];

private $arguments = [];

private $preferences = [];

private $profile;

public function __construct()
{


$this->setPreference(FirefoxPreferences::READER_PARSE_ON_LOAD_ENABLED, false);

$this->setPreference(FirefoxPreferences::DEVTOOLS_JSONVIEW, false);
}









public function setOption($name, $value)
{
if ($name === self::OPTION_PREFS) {
throw LogicException::forError('Use setPreference() method to set Firefox preferences');
}
if ($name === self::OPTION_ARGS) {
throw LogicException::forError('Use addArguments() method to add Firefox arguments');
}
if ($name === self::OPTION_PROFILE) {
throw LogicException::forError('Use setProfile() method to set Firefox profile');
}

$this->options[$name] = $value;

return $this;
}









public function addArguments(array $arguments)
{
$this->arguments = array_merge($this->arguments, $arguments);

return $this;
}










public function setPreference($name, $value)
{
$this->preferences[$name] = $value;

return $this;
}





public function setProfile(FirefoxProfile $profile)
{
$this->profile = $profile;

return $this;
}




public function toArray()
{
$array = $this->options;
if (!empty($this->arguments)) {
$array[self::OPTION_ARGS] = $this->arguments;
}
if (!empty($this->preferences)) {
$array[self::OPTION_PREFS] = $this->preferences;
}
if (!empty($this->profile)) {
$array[self::OPTION_PROFILE] = $this->profile->encode();
}

return $array;
}

#[ReturnTypeWillChange]
public function jsonSerialize()
{
return new \ArrayObject($this->toArray());
}
}
<?php

namespace Facebook\WebDriver\Firefox;

use Facebook\WebDriver\Exception\Internal\IOException;
use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\Internal\RuntimeException;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;

class FirefoxProfile
{



private $preferences = [];



private $extensions = [];



private $extensions_datas = [];



private $rdf_file;





public function addExtension($extension)
{
$this->extensions[] = $extension;

return $this;
}





public function addExtensionDatas($extension_datas)
{
if (!is_dir($extension_datas)) {
return null;
}

$this->extensions_datas[basename($extension_datas)] = $extension_datas;

return $this;
}





public function setRdfFile($rdf_file)
{
if (!is_file($rdf_file)) {
return null;
}

$this->rdf_file = $rdf_file;

return $this;
}







public function setPreference($key, $value)
{
if (is_string($value)) {
$value = sprintf('"%s"', $value);
} else {
if (is_int($value)) {
$value = sprintf('%d', $value);
} else {
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
} else {
throw LogicException::forError(
'The value of the preference should be either a string, int or bool.'
);
}
}
}
$this->preferences[$key] = $value;

return $this;
}





public function getPreference($key)
{
if (array_key_exists($key, $this->preferences)) {
return $this->preferences[$key];
}

return null;
}




public function encode()
{
$temp_dir = $this->createTempDirectory('WebDriverFirefoxProfile');

if (isset($this->rdf_file)) {
copy($this->rdf_file, $temp_dir . DIRECTORY_SEPARATOR . 'mimeTypes.rdf');
}

foreach ($this->extensions as $extension) {
$this->installExtension($extension, $temp_dir);
}

foreach ($this->extensions_datas as $dirname => $extension_datas) {
mkdir($temp_dir . DIRECTORY_SEPARATOR . $dirname);
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($extension_datas, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$target_dir = $temp_dir . DIRECTORY_SEPARATOR . $dirname . DIRECTORY_SEPARATOR
. $iterator->getSubPathName();

if ($item->isDir()) {
mkdir($target_dir);
} else {
copy($item, $target_dir);
}
}
}

$content = '';
foreach ($this->preferences as $key => $value) {
$content .= sprintf("user_pref(\"%s\", %s);\n", $key, $value);
}
file_put_contents($temp_dir . '/user.js', $content);


$temp_zip = sys_get_temp_dir() . '/' . uniqid('WebDriverFirefoxProfileZip', false);

$zip = new ZipArchive();
$zip->open($temp_zip, ZipArchive::CREATE);

$dir = new RecursiveDirectoryIterator($temp_dir);
$files = new RecursiveIteratorIterator($dir);

$dir_prefix = preg_replace(
'#\\\\#',
'\\\\\\\\',
$temp_dir . DIRECTORY_SEPARATOR
);

foreach ($files as $name => $object) {
if (is_dir($name)) {
continue;
}

$path = preg_replace("#^{$dir_prefix}#", '', $name);
$zip->addFile($name, $path);
}
$zip->close();

$profile = base64_encode(file_get_contents($temp_zip));


$this->deleteDirectory($temp_dir);
unlink($temp_zip);

return $profile;
}






private function installExtension($extension, $profileDir)
{
$extensionCommonName = $this->parseExtensionName($extension);


$extensionDir = $profileDir . '/extensions/';
if (!is_dir($extensionDir) && !mkdir($extensionDir, 0777, true) && !is_dir($extensionDir)) {
throw IOException::forFileError(
'Cannot install Firefox extension - cannot create directory',
$extensionDir
);
}

if (!copy($extension, $extensionDir . $extensionCommonName . '.xpi')) {
throw IOException::forFileError(
'Cannot install Firefox extension - cannot copy file',
$extension
);
}
}







private function createTempDirectory($prefix = '')
{
$temp_dir = tempnam(sys_get_temp_dir(), $prefix);
if (file_exists($temp_dir)) {
unlink($temp_dir);
mkdir($temp_dir);
if (!is_dir($temp_dir)) {
throw IOException::forFileError(
'Cannot install Firefox extension - cannot create directory',
$temp_dir
);
}
}

return $temp_dir;
}




private function deleteDirectory($directory)
{
$dir = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS);
$paths = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);

foreach ($paths as $path) {
if ($path->isDir() && !$path->isLink()) {
rmdir($path->getPathname());
} else {
unlink($path->getPathname());
}
}

rmdir($directory);
}








private function extractTo($xpi, $target_dir)
{
$zip = new ZipArchive();
if (file_exists($xpi)) {
if ($zip->open($xpi)) {
$zip->extractTo($target_dir);
$zip->close();
} else {
throw IOException::forFileError('Failed to open the firefox extension.', $xpi);
}
} else {
throw IOException::forFileError('Firefox extension doesn\'t exist.', $xpi);
}

return $this;
}

private function parseExtensionName($extensionPath)
{
$temp_dir = $this->createTempDirectory();

$this->extractTo($extensionPath, $temp_dir);

$mozillaRsaPath = $temp_dir . '/META-INF/mozilla.rsa';
$mozillaRsaBinaryData = file_get_contents($mozillaRsaPath);
$mozillaRsaHex = bin2hex($mozillaRsaBinaryData);




$objectIdentifierHexMarker = '0603550403';

$firstMarkerPosInHex = strpos($mozillaRsaHex, $objectIdentifierHexMarker); 

$secondMarkerPosInHexString =
strpos($mozillaRsaHex, $objectIdentifierHexMarker, $firstMarkerPosInHex + 2); 

if ($secondMarkerPosInHexString === false) {
throw RuntimeException::forError('Cannot install extension. Cannot fetch extension commonName');
}


$commonNameStringPositionInBinary = ($secondMarkerPosInHexString + strlen($objectIdentifierHexMarker)) / 2;

$commonNameStringLength = ord($mozillaRsaBinaryData[$commonNameStringPositionInBinary + 1]);

$extensionCommonName = substr(
$mozillaRsaBinaryData,
$commonNameStringPositionInBinary + 2,
$commonNameStringLength
);

$this->deleteDirectory($temp_dir);

return $extensionCommonName;
}
}
<?php







namespace Flow\JSONPath;

class JSONPathToken
{



public const T_INDEX = 'index';

public const T_RECURSIVE = 'recursive';

public const T_QUERY_RESULT = 'queryResult';

public const T_QUERY_MATCH = 'queryMatch';

public const T_SLICE = 'slice';

public const T_INDEXES = 'indexes';

public string $type;

public mixed $value;




public function __construct(string $type, $value)
{
$this->validateType($type);

$this->type = $type;
$this->value = $value;
}




public function validateType(string $type): void
{
if (!\in_array($type, static::getTypes(), true)) {
throw new JSONPathException('Invalid token: ' . $type);
}
}

public static function getTypes(): array
{
return [
static::T_INDEX,
static::T_RECURSIVE,
static::T_QUERY_RESULT,
static::T_QUERY_MATCH,
static::T_SLICE,
static::T_INDEXES,
];
}




public function buildFilter(bool $options)
{
$filterClass = 'Flow\\JSONPath\\Filters\\' . \ucfirst($this->type) . 'Filter';

if (!\class_exists($filterClass)) {
throw new JSONPathException("No filter class exists for token [{$this->type}]");
}

return new $filterClass($this, $options);
}
}
<?php







namespace Flow\JSONPath;

use Exception;

class JSONPathException extends Exception
{
}
<?php







namespace Flow\JSONPath\Filters;

use Flow\JSONPath\AccessHelper;
use Flow\JSONPath\JSONPathException;

class RecursiveFilter extends AbstractFilter
{



public function filter($collection): array
{
$result = [];

$this->recurse($result, $collection);

return $result;
}




private function recurse(array &$result, array|object $data): void
{
$result[] = (array)$data;

if (AccessHelper::isCollectionType($data)) {
foreach (AccessHelper::arrayValues($data) as $value) {
if (AccessHelper::isCollectionType($value)) {
$this->recurse($result, $value);
}
}
}
}
}
<?php







namespace Flow\JSONPath\Filters;

use ArrayAccess;
use Flow\JSONPath\JSONPath;
use Flow\JSONPath\JSONPathToken;

abstract class AbstractFilter
{
protected JSONPathToken $token;

protected bool $magicIsAllowed = false;

public function __construct(JSONPathToken $token, int|bool $options = false)
{
$this->token = $token;
$this->magicIsAllowed = (bool)($options & JSONPath::ALLOW_MAGIC);
}

abstract public function filter(array|ArrayAccess $collection): array;
}
<?php







namespace Flow\JSONPath\Filters;

use Flow\JSONPath\AccessHelper;

class IndexesFilter extends AbstractFilter
{
public function filter($collection): array
{
$return = [];

foreach ($this->token->value as $index) {
if (AccessHelper::keyExists($collection, $index, $this->magicIsAllowed)) {
$return[] = AccessHelper::getValue($collection, $index, $this->magicIsAllowed);
}
}

return $return;
}
}
<?php







namespace Flow\JSONPath\Filters;

use Flow\JSONPath\AccessHelper;

class SliceFilter extends AbstractFilter
{
public function filter($collection): array
{
$length = \count($collection);
$start = $this->token->value['start'];
$end = $this->token->value['end'];
$step = $this->token->value['step'] ?: 1;

if ($start === null) {
$start = 0;
}

if ($start < 0) {
$start = $length + $start;
if ($start < 0) {
$start = 0;
}
}

if ($end === null) {

$end = $length;
}

if ($end < 0) {
$end = $length + $end;
}

$result = [];

for ($i = $start; $i < $end; $i += $step) {
$index = $i;

if ($i < 0) {
$index = $length + $i;
}

if (AccessHelper::keyExists($collection, $index, $this->magicIsAllowed)) {
$result[] = $collection[$index];
}
}

return $result;
}
}
<?php







namespace Flow\JSONPath\Filters;

use Flow\JSONPath\AccessHelper;
use RuntimeException;

class QueryMatchFilter extends AbstractFilter
{
protected const MATCH_QUERY_OPERATORS = '
      @(\.(?<key>[^\s<>!=]+)|\[["\']?(?<keySquare>.*?)["\']?\])
      (\s*(?<operator>==|=~|=|<>|!==|!=|>=|<=|>|<|in|!in|nin)\s*(?<comparisonValue>.+))?
    ';

public function filter($collection): array
{
\preg_match('/^' . static::MATCH_QUERY_OPERATORS . '$/x', $this->token->value, $matches);

if (!isset($matches[1])) {
throw new RuntimeException('Malformed filter query');
}

$key = $matches['key'] ?: $matches['keySquare'];

if ($key === '') {
throw new RuntimeException('Malformed filter query: key was not set');
}

$operator = $matches['operator'] ?? null;
$comparisonValue = $matches['comparisonValue'] ?? null;

if (\is_string($comparisonValue)) {
if (\str_starts_with($comparisonValue, "[") && \str_ends_with($comparisonValue, "]")) {
$comparisonValue = \substr($comparisonValue, 1, -1);
$comparisonValue = \preg_replace('/^[\'"]/', '', $comparisonValue);
$comparisonValue = \preg_replace('/[\'"]$/', '', $comparisonValue);
$comparisonValue = \preg_replace('/[\'"], *[\'"]/', ',', $comparisonValue);
$comparisonValue = \array_map('trim', \explode(",", $comparisonValue));
} else {
$comparisonValue = \preg_replace('/^[\'"]/', '', $comparisonValue);
$comparisonValue = \preg_replace('/[\'"]$/', '', $comparisonValue);

if (\strtolower($comparisonValue) === 'false') {
$comparisonValue = false;
} elseif (\strtolower($comparisonValue) === 'true') {
$comparisonValue = true;
} elseif (\strtolower($comparisonValue) === 'null') {
$comparisonValue = null;
}
}
}

$return = [];

foreach ($collection as $value) {
if (AccessHelper::keyExists($value, $key, $this->magicIsAllowed)) {
$value1 = AccessHelper::getValue($value, $key, $this->magicIsAllowed);

if ($operator === null && $value1) {
$return[] = $value;
}



if (($operator === '=' || $operator === '==') && $value1 == $comparisonValue) {
$return[] = $value;
}



if (($operator === '!=' || $operator === '!==' || $operator === '<>') && $value1 != $comparisonValue) {
$return[] = $value;
}

if ($operator === '=~' && @\preg_match($comparisonValue, $value1)) {
$return[] = $value;
}

if ($operator === '>' && $value1 > $comparisonValue) {
$return[] = $value;
}

if ($operator === '>=' && $value1 >= $comparisonValue) {
$return[] = $value;
}

if ($operator === '<' && $value1 < $comparisonValue) {
$return[] = $value;
}

if ($operator === '<=' && $value1 <= $comparisonValue) {
$return[] = $value;
}

if ($operator === 'in' && \is_array($comparisonValue) && \in_array($value1, $comparisonValue, false)) {
$return[] = $value;
}

if (
($operator === 'nin' || $operator === '!in')
&& \is_array($comparisonValue)
&& !\in_array($value1, $comparisonValue, false)
) {
$return[] = $value;
}
}
}

return $return;
}
}
<?php







namespace Flow\JSONPath\Filters;

use Flow\JSONPath\AccessHelper;
use Flow\JSONPath\JSONPathException;

class IndexFilter extends AbstractFilter
{



public function filter($collection): array
{
if (\is_array($this->token->value)) {
$result = [];
foreach ($this->token->value as $value) {
if (AccessHelper::keyExists($collection, $value, $this->magicIsAllowed)) {
$result[] = AccessHelper::getValue($collection, $value, $this->magicIsAllowed);
}
}

return $result;
}

if (AccessHelper::keyExists($collection, $this->token->value, $this->magicIsAllowed)) {
return [
AccessHelper::getValue($collection, $this->token->value, $this->magicIsAllowed),
];
}

if ($this->token->value === '*') {
return AccessHelper::arrayValues($collection);
}

if ($this->token->value === 'length') {
return [
\count($collection),
];
}

return [];
}
}
<?php







namespace Flow\JSONPath\Filters;

use Flow\JSONPath\AccessHelper;
use Flow\JSONPath\JSONPathException;

class QueryResultFilter extends AbstractFilter
{



public function filter($collection): array
{
\preg_match('/@\.(?<key>\w+)\s*(?<operator>[-+*\/])\s*(?<numeric>\d+)/', $this->token->value, $matches);

$matchKey = $matches['key'];

if (AccessHelper::keyExists($collection, $matchKey, $this->magicIsAllowed)) {
$value = AccessHelper::getValue($collection, $matchKey, $this->magicIsAllowed);
} elseif ($matches['key'] === 'length') {
$value = \count($collection);
} else {
return [];
}

$resultKey = match ($matches['operator']) {
'+' => $value + $matches['numeric'],
'*' => $value * $matches['numeric'],
'-' => $value - $matches['numeric'],
'/' => $value / $matches['numeric'],
default => throw new JSONPathException('Unsupported operator in expression'),
};

$result = [];

if (AccessHelper::keyExists($collection, $resultKey, $this->magicIsAllowed)) {
$result[] = AccessHelper::getValue($collection, $resultKey, $this->magicIsAllowed);
}

return $result;
}
}
<?php







namespace Flow\JSONPath;

use ArrayAccess;

class AccessHelper
{
public static function collectionKeys(mixed $collection): array
{
if (\is_object($collection)) {
return \array_keys(\get_object_vars($collection));
}

return \array_keys($collection);
}

public static function isCollectionType(mixed $collection): bool
{
return \is_array($collection) || \is_object($collection);
}

public static function keyExists(mixed $collection, $key, bool $magicIsAllowed = false): bool
{
if ($magicIsAllowed && \is_object($collection) && \method_exists($collection, '__get')) {
return true;
}

if (\is_int($key) && $key < 0) {
$key = \abs($key);
}

if (\is_array($collection)) {
return \array_key_exists($key, $collection);
}

if ($collection instanceof ArrayAccess) {
return $collection->offsetExists($key);
}

if (\is_object($collection)) {
return \property_exists($collection, (string)$key);
}

return false;
}




public static function getValue(mixed $collection, $key, bool $magicIsAllowed = false)
{
if (
$magicIsAllowed
&& \is_object($collection)
&& !$collection instanceof ArrayAccess && \method_exists($collection, '__get')
) {
$return = $collection->__get($key);
} elseif (\is_object($collection) && !$collection instanceof ArrayAccess) {
$return = $collection->{$key};
} elseif ($collection instanceof ArrayAccess) {
$return = $collection->offsetGet($key);
} elseif (\is_array($collection)) {
if (\is_int($key) && $key < 0) {
$return = \array_slice($collection, $key, 1)[0];
} else {
$return = $collection[$key];
}
} elseif (\is_int($key)) {
$return = self::getValueByIndex($collection, $key);
} else {
$return = $collection[$key];
}

return $return;
}





private static function getValueByIndex(mixed $collection, $key): mixed
{
$i = 0;

foreach ($collection as $val) {
if ($i === $key) {
return $val;
}

$i++;
}

if ($key < 0) {
$total = $i;
$i = 0;

foreach ($collection as $val) {
if ($i - $total === $key) {
return $val;
}

$i++;
}
}

return null;
}

public static function setValue(mixed &$collection, $key, $value)
{
if (\is_object($collection) && !$collection instanceof ArrayAccess) {
return $collection->{$key} = $value;
}

if ($collection instanceof ArrayAccess) {

return $collection->offsetSet($key, $value);
}

return $collection[$key] = $value;
}

public static function unsetValue(mixed &$collection, $key): void
{
if (\is_object($collection) && !$collection instanceof ArrayAccess) {
unset($collection->{$key});
}

if ($collection instanceof ArrayAccess) {
$collection->offsetUnset($key);
}

if (\is_array($collection)) {
unset($collection[$key]);
}
}




public static function arrayValues(array|object $collection): array|ArrayAccess
{
if (\is_array($collection)) {
return \array_values($collection);
}

if (\is_object($collection)) {
return \array_values((array)$collection);
}

throw new JSONPathException("Invalid variable type for arrayValues");
}
}
<?php







namespace Flow\JSONPath;

use ArrayAccess;
use Countable;
use Iterator;
use JsonSerializable;

class JSONPath implements ArrayAccess, Iterator, JsonSerializable, Countable
{
public const ALLOW_MAGIC = true;

protected static array $tokenCache = [];

protected mixed $data = [];

protected bool $options = false;

final public function __construct(mixed $data = [], bool $options = false)
{
$this->data = $data;
$this->options = $options;
}








public function find(string $expression): self
{
$tokens = $this->parseTokens($expression);
$collectionData = [$this->data];

foreach ($tokens as $token) {

$filter = $token->buildFilter($this->options);
$filteredDataList = [];

foreach ($collectionData as $value) {
if (AccessHelper::isCollectionType($value)) {
$filteredDataList[] = $filter->filter($value);
}
}

if (!empty($filteredDataList)) {
$collectionData = \array_merge(...$filteredDataList);
} else {
$collectionData = [];
}
}

return new static($collectionData, $this->options);
}

public function first(): mixed
{
$keys = AccessHelper::collectionKeys($this->data);

if (empty($keys)) {
return null;
}

$value = $this->data[$keys[0]] ?? null;

return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value;
}




public function last(): mixed
{
$keys = AccessHelper::collectionKeys($this->data);

if (empty($keys)) {
return null;
}

$value = $this->data[\end($keys)] ?: null;

return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value;
}




public function firstKey(): mixed
{
$keys = AccessHelper::collectionKeys($this->data);

if (empty($keys)) {
return null;
}

return $keys[0];
}




public function lastKey(): mixed
{
$keys = AccessHelper::collectionKeys($this->data);

if (empty($keys) || \end($keys) === false) {
return null;
}

return \end($keys);
}




public function parseTokens(string $expression): array
{
$cacheKey = \crc32($expression);

if (isset(static::$tokenCache[$cacheKey])) {
return static::$tokenCache[$cacheKey];
}

$lexer = new JSONPathLexer($expression);
$tokens = $lexer->parseExpression();

static::$tokenCache[$cacheKey] = $tokens;

return $tokens;
}

public function getData(): mixed
{
return $this->data;
}





public function __get($key)
{
return $this->offsetExists($key) ? $this->offsetGet($key) : null;
}




public function offsetExists($offset): bool
{
return AccessHelper::keyExists($this->data, $offset);
}




public function offsetGet($offset): mixed
{
$value = AccessHelper::getValue($this->data, $offset);

return AccessHelper::isCollectionType($value)
? new static($value, $this->options)
: $value;
}




public function offsetSet($offset, $value): void
{
if ($offset === null) {
$this->data[] = $value;
} else {
AccessHelper::setValue($this->data, $offset, $value);
}
}




public function offsetUnset($offset): void
{
AccessHelper::unsetValue($this->data, $offset);
}




public function jsonSerialize(): array
{
return $this->getData();
}




public function current(): mixed
{
$value = \current($this->data);

return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value;
}




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




public function key(): string|int|null
{
return \key($this->data);
}




public function valid(): bool
{
return \key($this->data) !== null;
}




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




public function count(): int
{
return \count($this->data);
}
}
<?php







namespace Flow\JSONPath;

class JSONPathLexer
{




public const MATCH_INDEX = '(?!-)[\-\w]+ | \*'; 

public const MATCH_INDEXES = '\s* -?\d+ [-?\d,\s]+'; 

public const MATCH_SLICE = '[-\d:]+ | :'; 

public const MATCH_QUERY_RESULT = '\s* \( .+? \) \s*'; 

public const MATCH_QUERY_MATCH = '\s* \?\(.+?\) \s*'; 

public const MATCH_INDEX_IN_SINGLE_QUOTES = '\s* \' (.+?)? \' \s*'; 

public const MATCH_INDEX_IN_DOUBLE_QUOTES = '\s* " (.+?)? " \s*'; 




protected string $expression = '';




protected int $expressionLength = 0;

public function __construct(string $expression)
{
$expression = \trim($expression);
$len = \strlen($expression);

if ($len > 1) {
if ($expression[0] === '$') {
$expression = \substr($expression, 1);
$len--;
}

if ($expression[0] !== '.' && $expression[0] !== '[') {
$expression = '.' . $expression;
$len++;
}

$this->expression = $expression;
$this->expressionLength = $len;
}
}




public function parseExpressionTokens(): array
{
$dotIndexDepth = 0;
$squareBracketDepth = 0;
$tokenValue = '';
$tokens = [];

for ($i = 0; $i < $this->expressionLength; $i++) {
$char = $this->expression[$i];

if (($squareBracketDepth === 0) && $char === '.') {
if ($this->lookAhead($i) === '.') {
$tokens[] = new JSONPathToken(JSONPathToken::T_RECURSIVE, null);
}

continue;
}

if ($char === '[') {
$squareBracketDepth++;

if ($squareBracketDepth === 1) {
continue;
}
}

if ($char === ']') {
$squareBracketDepth--;

if ($squareBracketDepth === 0) {
continue;
}
}




if ($squareBracketDepth > 0) {
$tokenValue .= $char;

if ($squareBracketDepth === 1 && $this->lookAhead($i) === ']') {
$tokens[] = $this->createToken($tokenValue);
$tokenValue = '';
}
}




if ($squareBracketDepth === 0) {
$tokenValue .= $char;


if ($char === '.' && $dotIndexDepth > 1) {
$tokens[] = $this->createToken($tokenValue);
$tokenValue = '';
continue;
}

if ($this->atEnd($i) || \in_array($this->lookAhead($i), ['.', '['])) {
$tokens[] = $this->createToken($tokenValue);
$tokenValue = '';
$dotIndexDepth--;
}
}
}

if ($tokenValue !== '') {
$tokens[] = $this->createToken($tokenValue);
}

return $tokens;
}

protected function lookAhead(int $pos, int $forward = 1): ?string
{
return $this->expression[$pos + $forward] ?? null;
}

protected function atEnd(int $pos): bool
{
return $pos === $this->expressionLength;
}




public function parseExpression(): array
{
return $this->parseExpressionTokens();
}




protected function createToken(string $value): JSONPathToken
{


$tokenValue = $value;


$ret = null;

if (\preg_match('/^(' . static::MATCH_INDEX . ')$/xu', $tokenValue, $matches)) {
if (\preg_match('/^-?\d+$/', $tokenValue)) {
$tokenValue = (int)$tokenValue;
}

$ret = new JSONPathToken(JSONPathToken::T_INDEX, $tokenValue);
} elseif (\preg_match('/^' . static::MATCH_INDEXES . '$/xu', $tokenValue, $matches)) {
$tokenValue = \explode(',', \trim($tokenValue, ','));

foreach ($tokenValue as $i => $v) {
$tokenValue[$i] = (int)\trim($v);
}

$ret = new JSONPathToken(JSONPathToken::T_INDEXES, $tokenValue);
} elseif (\preg_match('/^' . static::MATCH_SLICE . '$/xu', $tokenValue, $matches)) {
$parts = \explode(':', $tokenValue);
$tokenValue = [
'start' => isset($parts[0]) && $parts[0] !== '' ? (int)$parts[0] : null,
'end' => isset($parts[1]) && $parts[1] !== '' ? (int)$parts[1] : null,
'step' => isset($parts[2]) && $parts[2] !== '' ? (int)$parts[2] : null,
];

$ret = new JSONPathToken(JSONPathToken::T_SLICE, $tokenValue);
} elseif (\preg_match('/^' . static::MATCH_QUERY_RESULT . '$/xu', $tokenValue)) {
$tokenValue = \substr($tokenValue, 1, -1);

$ret = new JSONPathToken(JSONPathToken::T_QUERY_RESULT, $tokenValue);
} elseif (\preg_match('/^' . static::MATCH_QUERY_MATCH . '$/xu', $tokenValue)) {
$tokenValue = \substr($tokenValue, 2, -1);

$ret = new JSONPathToken(JSONPathToken::T_QUERY_MATCH, $tokenValue);
} elseif (
\preg_match('/^' . static::MATCH_INDEX_IN_SINGLE_QUOTES . '$/xu', $tokenValue, $matches)
|| \preg_match('/^' . static::MATCH_INDEX_IN_DOUBLE_QUOTES . '$/xu', $tokenValue, $matches)
) {
if (isset($matches[1])) {
$tokenValue = $matches[1];
$tokenValue = \trim($tokenValue);

$possibleArray = false;
if ($matches[0][0] === '"') {
$possibleArray = \explode('","', $tokenValue);
} elseif ($matches[0][0] === "'") {
$possibleArray = \explode("','", $tokenValue);
}
if ($possibleArray !== false && \count($possibleArray) > 1) {
$tokenValue = $possibleArray;
}
} else {
$tokenValue = '';
}

$ret = new JSONPathToken(JSONPathToken::T_INDEX, $tokenValue);
}

if ($ret !== null) {
return $ret;
}

throw new JSONPathException("Unable to parse token {$tokenValue} in expression: {$this->expression}");
}
}
<?php











namespace Humbug\SelfUpdate;

class VersionParser
{



private $versions;




private $modifier = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';




public function __construct(array $versions = [])
{
$this->versions = $versions;
}







public function getMostRecentStable()
{
return $this->selectRecentStable();
}







public function getMostRecentUnStable()
{
return $this->selectRecentUnstable();
}







public function getMostRecentAll()
{
return $this->selectRecentAll();
}







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








public function isPreRelease($version)
{
return ! $this->stable($version) && ! $this->development($version);
}








public function isUnstable($version)
{
return ! $this->stable($version);
}







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

private function selectRecentStable()
{
$candidates = [];
foreach ($this->versions as $version) {
if (! $this->stable($version)) {
continue;
}
$candidates[] = $version;
}
if (empty($candidates)) {
return false;
}

return $this->findMostRecent($candidates);
}

private function selectRecentUnstable()
{
$candidates = [];
foreach ($this->versions as $version) {
if ($this->stable($version) || $this->development($version)) {
continue;
}
$candidates[] = $version;
}
if (empty($candidates)) {
return false;
}

return $this->findMostRecent($candidates);
}

private function selectRecentAll()
{
$candidates = [];
foreach ($this->versions as $version) {
if ($this->development($version)) {
continue;
}
$candidates[] = $version;
}
if (empty($candidates)) {
return false;
}

return $this->findMostRecent($candidates);
}


private function findMostRecent(array $candidates)
{
$candidate = '';
foreach ($candidates as $version) {
if (version_compare($candidate, $version, '<')) {
$candidate = $version;
}
}

return $candidate;
}

private function stable($version)
{
$version = preg_replace('{#.+$}i', '', $version);
if ($this->development($version)) {
return false;
}
preg_match('{'.$this->modifier.'$}i', strtolower($version), $match);
if (! empty($match[3])) {
return false;
}
if (! empty($match[1])) {
if ('beta' === $match[1] || 'b' === $match[1]
|| 'alpha' === $match[1] || 'a' === $match[1]
|| 'rc' === $match[1]) {
return false;
}
}

return true;
}

private function development($version)
{
if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) {
return true;
}
if (1 == preg_match("/-\d+-[a-z0-9]{8,}$/", $version)) {
return true;
}

return false;
}
}
<?php











namespace Humbug\SelfUpdate\Strategy;

use function file_get_contents;
use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Exception\InvalidArgumentException;
use Humbug\SelfUpdate\Updater;

abstract class ShaStrategyAbstract implements StrategyInterface
{

const SUPPORTED_SCHEMES = [
'http',
'https',
'file',
];




protected $versionUrl;




protected $pharUrl;







public function download(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$result = file_get_contents($this->getPharUrl());
restore_error_handler();
if (false === $result) {
throw new HttpRequestException(sprintf(
'Request to URL failed: %s',
$this->getPharUrl()
));
}

file_put_contents($updater->getTempPharFile(), $result);
}






public function setPharUrl($url)
{
if (! $this->validateAllowedUrl($url)) {
throw new InvalidArgumentException(
sprintf('Invalid url passed as argument: %s.', $url)
);
}
$this->pharUrl = $url;
}






public function getPharUrl()
{
return $this->pharUrl;
}






public function setVersionUrl($url)
{
if (! $this->validateAllowedUrl($url)) {
throw new InvalidArgumentException(
sprintf('Invalid url passed as argument: %s.', $url)
);
}
$this->versionUrl = $url;
}






public function getVersionUrl()
{
return $this->versionUrl;
}

protected function validateAllowedUrl($url)
{
return
filter_var($url, FILTER_VALIDATE_URL)
&& in_array(parse_url($url, PHP_URL_SCHEME), self::SUPPORTED_SCHEMES);
}
}
<?php











namespace Humbug\SelfUpdate\Strategy;

use function file_get_contents;
use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Updater;




class ShaStrategy extends ShaStrategyAbstract
{






public function getCurrentRemoteVersion(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$version = file_get_contents($this->getVersionUrl());
restore_error_handler();
if (false === $version) {
throw new HttpRequestException(sprintf(
'Request to URL failed: %s',
$this->getVersionUrl()
));
}
if (empty($version)) {
throw new HttpRequestException(
'Version request returned empty response.'
);
}
if (! preg_match('%^[a-z0-9]{40}%', $version, $matches)) {
throw new HttpRequestException(
'Version request returned incorrectly formatted response.'
);
}

return $matches[0];
}







public function getCurrentLocalVersion(Updater $updater)
{
return sha1_file($updater->getLocalPharFile());
}
}
<?php











namespace Humbug\SelfUpdate\Strategy;

use function file_get_contents;
use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Exception\InvalidArgumentException;
use Humbug\SelfUpdate\Exception\JsonParsingException;
use Humbug\SelfUpdate\Updater;
use Humbug\SelfUpdate\VersionParser;

class GithubStrategy implements StrategyInterface
{
const API_URL = 'https://packagist.org/p2/%s.json';

const STABLE = 'stable';

const UNSTABLE = 'unstable';

const ANY = 'any';




private $localVersion;




private $remoteVersion;




private $remoteUrl;




private $pharName;




private $packageName;




private $stability = self::STABLE;







public function download(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$result = file_get_contents($this->remoteUrl);
restore_error_handler();
if (false === $result) {
throw new HttpRequestException(sprintf(
'Request to URL failed: %s',
$this->remoteUrl
));
}

file_put_contents($updater->getTempPharFile(), $result);
}







public function getCurrentRemoteVersion(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$packageUrl = $this->getApiUrl();
$package = json_decode(file_get_contents($packageUrl), true);
restore_error_handler();

if (null === $package || json_last_error() !== JSON_ERROR_NONE) {
throw new JsonParsingException(
'Error parsing JSON package data'
.(function_exists('json_last_error_msg') ? ': '.json_last_error_msg() : '')
);
}

$versions = array_column($package['packages'][$this->getPackageName()], 'version');
$versionParser = new VersionParser($versions);
if ($this->getStability() === self::STABLE) {
$this->remoteVersion = $versionParser->getMostRecentStable();
} elseif ($this->getStability() === self::UNSTABLE) {
$this->remoteVersion = $versionParser->getMostRecentUnstable();
} else {
$this->remoteVersion = $versionParser->getMostRecentAll();
}




if (! empty($this->remoteVersion)) {
$chosenVersion = array_filter($package['packages'][$this->getPackageName()], function (array $package) {
return $package['version'] === $this->remoteVersion;
})[0];

$this->remoteUrl = $this->getDownloadUrl($chosenVersion);
}

return $this->remoteVersion;
}







public function getCurrentLocalVersion(Updater $updater)
{
return $this->localVersion;
}






public function setCurrentLocalVersion($version)
{
$this->localVersion = $version;
}






public function setPackageName($name)
{
$this->packageName = $name;
}






public function getPackageName()
{
return $this->packageName;
}






public function setPharName($name)
{
$this->pharName = $name;
}






public function getPharName()
{
return $this->pharName;
}






public function setStability($stability)
{
if ($stability !== self::STABLE && $stability !== self::UNSTABLE && $stability !== self::ANY) {
throw new InvalidArgumentException(
'Invalid stability value. Must be one of "stable", "unstable" or "any".'
);
}
$this->stability = $stability;
}






public function getStability()
{
return $this->stability;
}

protected function getApiUrl()
{
return sprintf(self::API_URL, $this->getPackageName());
}


protected function getDownloadUrl(array $package)
{
$baseUrl = preg_replace(
'{\.git$}',
'',
$package['source']['url']
);

return sprintf(
'%s/releases/download/%s/%s',
$baseUrl,
$this->remoteVersion,
$this->getPharName()
);
}
}
<?php











namespace Humbug\SelfUpdate\Strategy;

use function file_get_contents;
use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Updater;

final class Sha256Strategy extends ShaStrategyAbstract
{






public function getCurrentRemoteVersion(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$version = file_get_contents($this->getVersionUrl());
restore_error_handler();
if (false === $version) {
throw new HttpRequestException(sprintf(
'Request to URL failed: %s',
$this->getVersionUrl()
));
}
if (empty($version)) {
throw new HttpRequestException(
'Version request returned empty response.'
);
}
if (! preg_match('%^[a-z0-9]{64}%', $version, $matches)) {
throw new HttpRequestException(
'Version request returned incorrectly formatted response.'
);
}

return $matches[0];
}







public function getCurrentLocalVersion(Updater $updater)
{
return hash_file('sha256', $updater->getLocalPharFile());
}
}
<?php

namespace Humbug\SelfUpdate\Strategy;

use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Updater;

abstract class DirectDownloadStrategyAbstract implements StrategyInterface
{

protected string $localVersion;

abstract public function getDownloadUrl(): string;


public function download(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$result = file_get_contents($this->getDownloadUrl());
restore_error_handler();
if (false === $result) {
throw new HttpRequestException(sprintf(
'Request to URL failed: %s',
$this->getDownloadUrl()
));
}

file_put_contents($updater->getTempPharFile(), $result);
}


public function getCurrentRemoteVersion(Updater $updater)
{
return 'latest';
}

public function setCurrentLocalVersion(string $version): void
{
$this->localVersion = $version;
}


public function getCurrentLocalVersion(Updater $updater)
{
return $this->localVersion;
}
}
<?php











namespace Humbug\SelfUpdate\Strategy;

use Humbug\SelfUpdate\Updater;

interface StrategyInterface
{






public function download(Updater $updater);







public function getCurrentRemoteVersion(Updater $updater);







public function getCurrentLocalVersion(Updater $updater);
}
<?php

namespace Humbug\SelfUpdate\Strategy;

use function file_get_contents;
use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Updater;

final class Sha512Strategy extends ShaStrategyAbstract
{






public function getCurrentRemoteVersion(Updater $updater)
{

set_error_handler([$updater, 'throwHttpRequestException']);
$version = file_get_contents($this->getVersionUrl());
restore_error_handler();
if (false === $version) {
throw new HttpRequestException(sprintf(
'Request to URL failed: %s',
$this->getVersionUrl()
));
}
if (empty($version)) {
throw new HttpRequestException(
'Version request returned empty response.'
);
}
if (! preg_match('%^[a-z0-9]{128}%', $version, $matches)) {
throw new HttpRequestException(
'Version request returned incorrectly formatted response.'
);
}

return $matches[0];
}







public function getCurrentLocalVersion(Updater $updater)
{
return hash_file('sha512', $updater->getLocalPharFile());
}
}
<?php











namespace Humbug\SelfUpdate;

use Humbug\SelfUpdate\Exception\FilesystemException;
use Humbug\SelfUpdate\Exception\HttpRequestException;
use Humbug\SelfUpdate\Exception\InvalidArgumentException;
use Humbug\SelfUpdate\Exception\NoSignatureException;
use Humbug\SelfUpdate\Exception\RuntimeException;
use Humbug\SelfUpdate\Strategy\GithubStrategy;
use Humbug\SelfUpdate\Strategy\Sha256Strategy;
use Humbug\SelfUpdate\Strategy\Sha512Strategy;
use Humbug\SelfUpdate\Strategy\ShaStrategy;
use Humbug\SelfUpdate\Strategy\ShaStrategyAbstract;
use Humbug\SelfUpdate\Strategy\StrategyInterface;

class Updater
{
const STRATEGY_SHA1 = 'sha1';

const STRATEGY_SHA256 = 'sha256';

const STRATEGY_SHA512 = 'sha512';

const STRATEGY_GITHUB = 'github';




protected $strategy;




protected $localPharFile;




protected $localPharFileBasename;




protected $localPubKeyFile;




protected $hasPubKey;




protected $tempDirectory;




protected $newVersion;




protected $oldVersion;




protected $backupExtension = '-old.phar';




protected $backupPath;




protected $restorePath;




protected $newVersionAvailable;








public function __construct($localPharFile = null, $hasPubKey = true, $strategy = self::STRATEGY_SHA1)
{
ini_set('phar.require_hash', '1');
$this->setLocalPharFile($localPharFile);
if (! is_bool($hasPubKey)) {
throw new InvalidArgumentException(
'Constructor parameter $hasPubKey must be boolean or null.'
);
} else {
$this->hasPubKey = $hasPubKey;
}
if ($this->hasPubKey) {
$this->setLocalPubKeyFile();
}
$this->setTempDirectory();
$this->setStrategy($strategy);
}






public function hasUpdate()
{
$this->newVersionAvailable = $this->newVersionAvailable();

return $this->newVersionAvailable;
}






public function update()
{
if ($this->newVersionAvailable === false
|| (! is_bool($this->newVersionAvailable) && ! $this->hasUpdate())) {
return false;
}
$this->backupPhar();
$this->downloadPhar();
$this->replacePhar();

return true;
}






public function rollback()
{
if (! $this->restorePhar()) {
return false;
}

return true;
}




public function setStrategy($strategy)
{
switch ($strategy) {
case self::STRATEGY_GITHUB:
$this->strategy = new GithubStrategy;
break;

case self::STRATEGY_SHA256:
$this->strategy = new Sha256Strategy;
break;

case self::STRATEGY_SHA512:
$this->strategy = new Sha512Strategy;
break;

default:
$this->strategy = new ShaStrategy;
break;
}
}

public function setStrategyObject(StrategyInterface $strategy)
{
$this->strategy = $strategy;
}

public function getStrategy()
{
return $this->strategy;
}






public function setBackupExtension($extension)
{
$this->backupExtension = $extension;
}






public function getBackupExtension()
{
return $this->backupExtension;
}

public function getLocalPharFile()
{
return $this->localPharFile;
}

public function getLocalPharFileBasename()
{
return $this->localPharFileBasename;
}

public function getLocalPubKeyFile()
{
return $this->localPubKeyFile;
}

public function getTempDirectory()
{
return $this->tempDirectory;
}

public function getTempPharFile()
{
return $this->getTempDirectory()
.'/'
.sprintf('%s.phar.temp', $this->getLocalPharFileBasename());
}

public function getNewVersion()
{
return $this->newVersion;
}

public function getOldVersion()
{
return $this->oldVersion;
}






public function setBackupPath($filePath)
{
$path = realpath(dirname($filePath));
if (! is_dir($path)) {
throw new FilesystemException(sprintf(
'The backup directory does not exist: %s.',
$path
));
}
if (! is_writable($path)) {
throw new FilesystemException(sprintf(
'The backup directory is not writeable: %s.',
$path
));
}
$this->backupPath = $filePath;
}






public function getBackupPath()
{
return $this->backupPath;
}






public function setRestorePath($filePath)
{
$path = realpath(dirname($filePath));
if (! file_exists($path)) {
throw new FilesystemException(sprintf(
'The restore phar does not exist: %s.',
$path
));
}
if (! is_readable($path)) {
throw new FilesystemException(sprintf(
'The restore file is not readable: %s.',
$path
));
}
$this->restorePath = $filePath;
}






public function getRestorePath()
{
return $this->restorePath;
}

public function throwRuntimeException($errno, $errstr)
{
if (E_USER_DEPRECATED === $errno) {
return;
}

throw new RuntimeException($errstr);
}

public function throwHttpRequestException($errno, $errstr)
{
if (E_USER_DEPRECATED === $errno) {
return;
}

throw new HttpRequestException($errstr);
}

protected function hasPubKey()
{
return $this->hasPubKey;
}

protected function newVersionAvailable()
{
$this->newVersion = $this->strategy->getCurrentRemoteVersion($this);
$this->oldVersion = $this->strategy->getCurrentLocalVersion($this);

if (! empty($this->newVersion) && ($this->newVersion !== $this->oldVersion)) {
return true;
}

return false;
}

protected function backupPhar()
{
$result = copy($this->getLocalPharFile(), $this->getBackupPharFile());
if ($result === false) {
$this->cleanupAfterError();
throw new FilesystemException(sprintf(
'Unable to backup %s to %s.',
$this->getLocalPharFile(),
$this->getBackupPharFile()
));
}
}

protected function downloadPhar()
{
$this->strategy->download($this);

if (! file_exists($this->getTempPharFile())) {
throw new FilesystemException(
'Creation of download file failed.'
);
}

$strategy = $this->getStrategy();

if ($strategy instanceof ShaStrategyAbstract) {
if ($strategy instanceof ShaStrategy) {
$tmpVersion = sha1_file($this->getTempPharFile());
$algo = 'SHA-1';
} elseif ($strategy instanceof Sha512Strategy) {
$tmpVersion = hash_file('sha512', $this->getTempPharFile());
$algo = 'SHA-512';
} else {
$tmpVersion = hash_file('sha256', $this->getTempPharFile());
$algo = 'SHA-256';
}

if ($tmpVersion !== $this->getNewVersion()) {
$this->cleanupAfterError();
throw new HttpRequestException(sprintf(
'Download file appears to be corrupted or outdated. The file '
.'received does not have the expected %s hash: %s.',
$algo,
$this->getNewVersion()
));
}
}

try {
$this->validatePhar($this->getTempPharFile());
} catch (\Exception $e) {
restore_error_handler();
$this->cleanupAfterError();
throw $e;
}
}

protected function replacePhar()
{
rename($this->getTempPharFile(), $this->getLocalPharFile());
}

protected function restorePhar()
{
$backup = $this->getRestorePharFile();
if (! file_exists($backup)) {
throw new RuntimeException(sprintf(
'The backup file does not exist: %s.',
$backup
));
}
$this->validatePhar($backup);

return rename($backup, $this->getLocalPharFile());
}

protected function getBackupPharFile()
{
if (null !== $this->getBackupPath()) {
return $this->getBackupPath();
}

return $this->getTempDirectory()
.'/'
.sprintf('%s%s', $this->getLocalPharFileBasename(), $this->getBackupExtension());
}

protected function getRestorePharFile()
{
if (null !== $this->getRestorePath()) {
return $this->getRestorePath();
}

return $this->getTempDirectory()
.'/'
.sprintf(
'%s%s',
$this->getLocalPharFileBasename(),
$this->getBackupExtension()
);
}

protected function getTempPubKeyFile()
{
return $this->getTempDirectory()
.'/'
.sprintf('%s.phar.temp.pubkey', $this->getLocalPharFileBasename());
}

protected function setLocalPharFile($localPharFile)
{
if (! is_null($localPharFile)) {
$localPharFile = realpath($localPharFile);
} else {
$localPharFile = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
}
if (! file_exists($localPharFile)) {
throw new RuntimeException(sprintf(
'The set phar file does not exist: %s.',
$localPharFile
));
}
if (! is_writable($localPharFile)) {
throw new FilesystemException(sprintf(
'The current phar file is not writeable and cannot be replaced: %s.',
$localPharFile
));
}
$this->localPharFile = $localPharFile;
$this->localPharFileBasename = basename($localPharFile, '.phar');
}

protected function setLocalPubKeyFile()
{
$localPubKeyFile = $this->getLocalPharFile().'.pubkey';
if (! file_exists($localPubKeyFile)) {
throw new RuntimeException(sprintf(
'The phar pubkey file does not exist: %s.',
$localPubKeyFile
));
}
$this->localPubKeyFile = $localPubKeyFile;
}

protected function setTempDirectory()
{
$tempDirectory = dirname($this->getLocalPharFile());
if (! is_writable($tempDirectory)) {
throw new FilesystemException(sprintf(
'The directory is not writeable: %s.',
$tempDirectory
));
}
$this->tempDirectory = $tempDirectory;
}

protected function validatePhar($phar)
{
$phar = realpath($phar);
if ($this->hasPubKey()) {
copy($this->getLocalPubKeyFile(), $phar.'.pubkey');
}
chmod($phar, fileperms($this->getLocalPharFile()));

set_error_handler([$this, 'throwRuntimeException']);
$phar = new \Phar($phar);
$signature = $phar->getSignature();
if ($this->hasPubKey() && strtolower($signature['hash_type']) !== 'openssl') {
throw new NoSignatureException(
'The downloaded phar file has no OpenSSL signature.'
);
}
restore_error_handler();
if ($this->hasPubKey()) {
@unlink($phar.'.pubkey');
}
unset($phar);
}

protected function cleanupAfterError()
{

@unlink($this->getTempPharFile());
@unlink($this->getTempPubKeyFile());
}
}
<?php









namespace Humbug\SelfUpdate\Exception;

interface ExceptionInterface
{
}
<?php









namespace Humbug\SelfUpdate\Exception;

class FilesystemException extends RuntimeException
{
}
<?php









namespace Humbug\SelfUpdate\Exception;

class NoSignatureException extends RuntimeException
{
}
<?php









namespace Humbug\SelfUpdate\Exception;

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









namespace Humbug\SelfUpdate\Exception;

class HttpRequestException extends RuntimeException
{
}
<?php









namespace Humbug\SelfUpdate\Exception;

class InvalidArgumentException extends \RuntimeException implements ExceptionInterface
{
}
<?php









namespace Humbug\SelfUpdate\Exception;

class JsonParsingException extends RuntimeException
{
}
I?F._HB    GBMB