#!/usr/bin/env php n assets/psalm-phar/dot-gitignore5p}eP sTassets/psalm-phar/composer.json5p}e+assets/psalm-phar/README.mdn5p}en+Epsalm5p}e((src/Psalm/PluginFileExtensionsSocket.php 5p}e _src/Psalm/Codebase.phpB 5p}eB xZsrc/Psalm/ErrorBaseline.php$5p}e$<6src/Psalm/Exception/ComplicatedExpressionException.phps5p}es{?u/src/Psalm/Exception/ConfigCreationException.phpl5p}el2#5src/Psalm/Exception/UnpopulatedClasslikeException.phpq5p}eq6src/Psalm/Exception/InvalidMethodOverrideException.phps5p}es/src/Psalm/Exception/UnanalyzedFileException.phpl5p}elP>)src/Psalm/Exception/RefactorException.phpf5p}ef>Rh,src/Psalm/Exception/FileIncludeException.phpi5p}eiJݤ.src/Psalm/Exception/DocblockParseException.phpe5p}ee^٤'src/Psalm/Exception/ConfigException.php^5p}e^G2src/Psalm/Exception/CircularReferenceException.phpo5p}eo9ػ2src/Psalm/Exception/IncorrectDocblockException.php~5p}e~ޤ3src/Psalm/Exception/UnpreparedAnalysisException.phpp5p}ep2*\.src/Psalm/Exception/ScopeAnalysisException.phpk5p}ekUg9src/Psalm/Exception/InvalidClasslikeOverrideException.phpv5p}evd>.src/Psalm/Exception/TypeParseTreeException.phpk5p}ekȤ/src/Psalm/Exception/ConfigNotFoundException.phpt5p}et=g5src/Psalm/Exception/UnresolvableConstantException.php5p}e%Ф%src/Psalm/Exception/CodeException.phpb5p}eb'$6src/Psalm/Exception/UnsupportedIssueToFixException.phps5p}es<src/Psalm/CodeLocation.php-5p}e-6$src/Psalm/FileManipulation.php5p}euN| src/Psalm/Type.php0r5p}e0r7src/Psalm/IssueBuffer.php}5p}e}5#src/Psalm/Node/VirtualUnionType.php5p}es$src/Psalm/Node/VirtualIdentifier.php&5p}e&<"src/Psalm/Node/VirtualMatchArm.php5p}esrc/Psalm/Node/VirtualName.php5p}e{zΤ(src/Psalm/Node/VirtualAttributeGroup.php5p}e2/+src/Psalm/Node/VirtualVarLikeIdentifier.php5p}e&msrc/Psalm/Node/VirtualArg.php5p}ejܶsrc/Psalm/Node/VirtualConst.php5p}e#src/Psalm/Node/VirtualNode.php5p}edښD#src/Psalm/Node/VirtualAttribute.php5p}ec3src/Psalm/Node/Scalar/VirtualEncapsedStringPart.php5p}e=1src/Psalm/Node/Scalar/MagicConst/VirtualTrait.php5p}eDZ1src/Psalm/Node/Scalar/MagicConst/VirtualClass.php5p}ei'src/Psalm/Node/Expr/VirtualFuncCall.php5p}eO+.src/Psalm/Node/Expr/VirtualClassConstFetch.php5p}e/d*ݤ)src/Psalm/Node/Expr/VirtualStaticCall.php5p}eu]$src/Psalm/Node/Expr/VirtualMatch.php5p}eB<&src/Psalm/Node/Expr/VirtualInclude.php5p}eeA1src/Psalm/Node/Expr/AssignOp/VirtualShiftLeft.php5p}e_@1src/Psalm/Node/Expr/AssignOp/VirtualBitwiseOr.php5p}ejWOw.src/Psalm/Node/Expr/AssignOp/VirtualConcat.php5p}e +src/Psalm/Node/Expr/AssignOp/VirtualDiv.php5p}e:+src/Psalm/Node/Expr/AssignOp/VirtualPow.php5p}eǗ2src/Psalm/Node/Expr/AssignOp/VirtualBitwiseXor.php5p}e|32src/Psalm/Node/Expr/AssignOp/VirtualBitwiseAnd.php5p}e¼-src/Psalm/Node/Expr/AssignOp/VirtualMinus.php5p}e+src/Psalm/Node/Expr/AssignOp/VirtualMod.php5p}eX[E+src/Psalm/Node/Expr/AssignOp/VirtualMul.php5p}e<2src/Psalm/Node/Expr/AssignOp/VirtualShiftRight.php5p}e{,src/Psalm/Node/Expr/AssignOp/VirtualPlus.php5p}e>0src/Psalm/Node/Expr/AssignOp/VirtualCoalesce.php5p}e+O)src/Psalm/Node/Expr/VirtualBitwiseNot.php5p}e%%src/Psalm/Node/Expr/VirtualPreInc.php5p}e4$src/Psalm/Node/Expr/VirtualError.php5p}e X%src/Psalm/Node/Expr/VirtualAssign.php5p}e/y1(src/Psalm/Node/Expr/VirtualShellExec.php5p}eW$src/Psalm/Node/Expr/VirtualArray.php5p}e'src/Psalm/Node/Expr/Cast/VirtualInt.php5p}ep[ǃ*src/Psalm/Node/Expr/Cast/VirtualDouble.php5p}eE~O(src/Psalm/Node/Expr/Cast/VirtualBool.php5p}e*src/Psalm/Node/Expr/Cast/VirtualString.php5p}e5)src/Psalm/Node/Expr/Cast/VirtualArray.php5p}eg]T*src/Psalm/Node/Expr/Cast/VirtualObject.php5p}e_)src/Psalm/Node/Expr/Cast/VirtualUnset.php5p}er֤(src/Psalm/Node/Expr/VirtualUnaryPlus.php5p}ein1src/Psalm/Node/Expr/VirtualNullsafeMethodCall.php5p}e&src/Psalm/Node/Expr/VirtualPostInc.php5p}e oo$src/Psalm/Node/Expr/VirtualYield.php5p}e],src/Psalm/Node/Expr/VirtualErrorSuppress.php5p}eT|w,src/Psalm/Node/Expr/VirtualArrayDimFetch.php5p}e9#src/Psalm/Node/Expr/VirtualExit.php5p}eK7פ2src/Psalm/Node/Expr/VirtualStaticPropertyFetch.php5p}e|_}"src/Psalm/Node/Expr/VirtualNew.php5p}e (src/Psalm/Node/Expr/VirtualAssignRef.php5p}e[$src/Psalm/Node/Expr/VirtualThrow.php5p}e)a<(src/Psalm/Node/Expr/VirtualArrayItem.php5p}eRv#src/Psalm/Node/Expr/VirtualList.php5p}e2src/Psalm/Node/Expr/BinaryOp/VirtualLogicalXor.php5p}e_)1src/Psalm/Node/Expr/BinaryOp/VirtualShiftLeft.php5p}e<1src/Psalm/Node/Expr/BinaryOp/VirtualBitwiseOr.php5p}ez .src/Psalm/Node/Expr/BinaryOp/VirtualConcat.php5p}eN11src/Psalm/Node/Expr/BinaryOp/VirtualIdentical.php5p}e+&~0src/Psalm/Node/Expr/BinaryOp/VirtualNotEqual.php5p}eHV4src/Psalm/Node/Expr/BinaryOp/VirtualNotIdentical.php5p}e y1src/Psalm/Node/Expr/BinaryOp/VirtualLogicalOr.php5p}emݤ+src/Psalm/Node/Expr/BinaryOp/VirtualDiv.php5p}eC9̤+src/Psalm/Node/Expr/BinaryOp/VirtualPow.php5p}e/?F2src/Psalm/Node/Expr/BinaryOp/VirtualBitwiseXor.php5p}e)/src/Psalm/Node/Expr/BinaryOp/VirtualSmaller.php5p}e5T)2src/Psalm/Node/Expr/BinaryOp/VirtualBitwiseAnd.php5p}elWJ1src/Psalm/Node/Expr/BinaryOp/VirtualSpaceship.php5p}ej2src/Psalm/Node/Expr/BinaryOp/VirtualLogicalAnd.php5p}eؗ-src/Psalm/Node/Expr/BinaryOp/VirtualMinus.php5p}eQ#+src/Psalm/Node/Expr/BinaryOp/VirtualMod.php5p}ev^+src/Psalm/Node/Expr/BinaryOp/VirtualMul.php5p}e=ƻ2src/Psalm/Node/Expr/BinaryOp/VirtualBooleanAnd.php5p}e!6src/Psalm/Node/Expr/BinaryOp/VirtualSmallerOrEqual.php5p}e=8]2src/Psalm/Node/Expr/BinaryOp/VirtualShiftRight.php5p}e,,src/Psalm/Node/Expr/BinaryOp/VirtualPlus.php5p}e^_-src/Psalm/Node/Expr/BinaryOp/VirtualEqual.php5p}eB2K6src/Psalm/Node/Expr/BinaryOp/VirtualGreaterOrEqual.php5p}eӟ0src/Psalm/Node/Expr/BinaryOp/VirtualCoalesce.php5p}e3$81src/Psalm/Node/Expr/BinaryOp/VirtualBooleanOr.php5p}eZʂ/src/Psalm/Node/Expr/BinaryOp/VirtualGreater.php5p}e@c>&src/Psalm/Node/Expr/VirtualClosure.php5p}eE[^"$src/Psalm/Node/Expr/VirtualClone.php5p}e^'src/Psalm/Node/Expr/VirtualVariable.php5p}eLI#src/Psalm/Node/Expr/VirtualEval.php5p}e% &src/Psalm/Node/Expr/VirtualPostDec.php5p}e:)src/Psalm/Node/Expr/VirtualConstFetch.php5p}eW@ݤ$src/Psalm/Node/Stmt/VirtualTrait.php5p}eX/%src/Psalm/Node/Stmt/VirtualElseIf.php5p}ea)src/Psalm/Node/Stmt/VirtualClassConst.php5p}eT/7#src/Psalm/Node/Stmt/VirtualElse.php5p}e\Ҥ"src/Psalm/Node/Stmt/VirtualFor.php5p}e7ЋĤ$src/Psalm/Node/Stmt/VirtualClass.php5p}e%src/Psalm/Node/Stmt/VirtualSwitch.php5p}e*"src/Psalm/Node/Stmt/VirtualUse.php5p}ef$src/Psalm/Node/Stmt/VirtualBreak.php5p}e&src/Psalm/Node/Stmt/VirtualDeclare.php5p}eJg*src/Psalm/Node/Stmt/VirtualClassMethod.php5p}e\ (src/Psalm/Node/Stmt/VirtualStaticVar.php5p}eJmD-src/Psalm/Node/Stmt/VirtualDeclareDeclare.php5p}e#$src/Psalm/Node/Stmt/VirtualLabel.php5p}e8)src/Psalm/Node/Stmt/VirtualInlineHTML.php5p}er17src/Psalm/Node/Stmt/TraitUseAdaptation/VirtualAlias.php5p}e"n<src/Psalm/Node/Stmt/TraitUseAdaptation/VirtualPrecedence.php5p}efĤ#src/Psalm/Node/Stmt/VirtualCase.php5p}e6'src/Psalm/Node/Stmt/VirtualFunction.php5p}ePQ'src/Psalm/Node/Stmt/VirtualTraitUse.php5p}ea-(src/Psalm/Node/Stmt/VirtualNamespace.php5p}e\љ&src/Psalm/Node/Stmt/VirtualForeach.php5p}ee$src/Psalm/Node/Stmt/VirtualConst.php5p}eڤ'src/Psalm/Node/Stmt/VirtualTryCatch.php5p}e^}'src/Psalm/Node/Stmt/VirtualContinue.php5p}eOd&src/Psalm/Node/Stmt/VirtualFinally.php5p}e}(src/Psalm/Node/Stmt/VirtualInterface.php5p}ejt%src/Psalm/Node/Stmt/VirtualGlobal.php5p}e !src/Psalm/Node/Stmt/VirtualDo.php5p}eԩ/src/Psalm/Node/Stmt/VirtualPropertyProperty.php5p}e:$+src/Psalm/Node/Stmt/VirtualHaltCompiler.php5p}e?]Ƥ$src/Psalm/Node/Stmt/VirtualWhile.php5p}e:R{V$src/Psalm/Node/Stmt/VirtualCatch.php5p}e}m%src/Psalm/Node/Stmt/VirtualStatic.php5p}eBjf!src/Psalm/Node/Stmt/VirtualIf.php5p}e\a%src/Psalm/Node/Stmt/VirtualUseUse.php5p}e7=)src/Psalm/Node/Stmt/VirtualExpression.php5p}e%src/Psalm/Node/Stmt/VirtualReturn.php5p}e1'src/Psalm/Node/Stmt/VirtualProperty.php5p}ep#src/Psalm/Node/Stmt/VirtualEcho.php5p}eYV$src/Psalm/Node/Stmt/VirtualThrow.php5p}eo_(#src/Psalm/Node/Stmt/VirtualGoto.php5p}ezä"src/Psalm/Node/Stmt/VirtualNop.php5p}ehC.$src/Psalm/Node/Stmt/VirtualUnset.php5p}eXƤ'src/Psalm/Node/Stmt/VirtualGroupUse.php5p}eu'src/Psalm/Node/Name/VirtualRelative.php5p}eB\-src/Psalm/Node/Name/VirtualFullyQualified.php5p}eJƨsrc/Psalm/Node/VirtualParam.php5p}e"U+src/Psalm/FileSource.php5p}e=;#src/Psalm/Progress/LongProgress.php" 5p}e" q$src/Psalm/Progress/DebugProgress.php5p}erF E&src/Psalm/Progress/DefaultProgress.php5p}e mm#src/Psalm/Progress/VoidProgress.php5p}e_src/Psalm/Progress/Progress.phpU5p}eUV-src/Psalm/SourceControl/SourceControlInfo.php|5p}e|T*src/Psalm/SourceControl/Git/RemoteInfo.php5p}e[EФ'src/Psalm/SourceControl/Git/GitInfo.php5p}euYl*src/Psalm/SourceControl/Git/CommitInfo.php 5p}e `/src/Psalm/NodeTypeProvider.php5p}e|src/Psalm/DocComment.php< 5p}e< csrc/Psalm/Aliases.php5p}esrc/Psalm/Config.phpR5p}eR!W?ɤ&src/Psalm/PluginRegistrationSocket.php 5p}e `N.src/Psalm/Report/ByIssueLevelAndTypeReport.php5p}eisrc/Psalm/Report/TextReport.php65p}e6Y'src/Psalm/Report/XmlReport.php5p}eCMT%src/Psalm/Report/CheckstyleReport.php5p}ey(src/Psalm/Report/GithubActionsReport.php5p}e "src/Psalm/Report/ReportOptions.php\5p}e\bx src/Psalm/Report/SarifReport.php% 5p}e% _$src/Psalm/Report/SonarqubeReport.php5p}eVˤ src/Psalm/Report/EmacsReport.phpS5p}eSQ$Y#src/Psalm/Report/PhpStormReport.phpv 5p}ev DK{&src/Psalm/Report/CodeClimateReport.phpZ 5p}eZ +  src/Psalm/Report/CountReport.php5p}e̚"src/Psalm/Report/CompactReport.php 5p}e .ޤ!src/Psalm/Report/PylintReport.php+5p}e+%/a&src/Psalm/Report/JsonSummaryReport.php5p}eTۤ"src/Psalm/Report/ConsoleReport.php'5p}e' ρ& src/Psalm/Report/JunitReport.php5p}eىnsrc/Psalm/Report/JsonReport.php5p}emࢶsrc/Psalm/Context.phpg5p}egNsrc/Psalm/Type/UnionTrait.php5p}e<src/Psalm/Type/Atomic.phpd5p}ed usrc/Psalm/Type/TypeVisitor.php5p}eʡbsrc/Psalm/Type/TypeNode.php5p}eY R!src/Psalm/Type/TaintKindGroup.php5p}esrc/Psalm/Type/Union.phpj$5p}ej$pJsrc/Psalm/Type/TaintKind.php5p}e\wksrc/Psalm/Type/MutableUnion.php85p}e84src/Psalm/Type/Reconciler.php5p}e`t%src/Psalm/Type/MutableTypeVisitor.php5p}eO&src/Psalm/Type/Atomic/GenericTrait.php5p}eV src/Psalm/Type/Atomic/TFloat.phpC5p}eCj6)src/Psalm/Type/Atomic/TNonEmptyString.php5p}e6(src/Psalm/Type/Atomic/TGenericObject.php5p}e|j%src/Psalm/Type/Atomic/TKeyedArray.phpQ5p}eQ w'src/Psalm/Type/Atomic/CallableTrait.phpr$5p}er$\)src/Psalm/Type/Atomic/TClosedResource.php5p}eyoJ(src/Psalm/Type/Atomic/TClassConstant.phpy5p}eyqݤ)src/Psalm/Type/Atomic/TNonFalsyString.php5p}e;#src/Psalm/Type/Atomic/TResource.php5p}eaf0src/Psalm/Type/Atomic/TTemplateIndexedAccess.php5p}e*(src/Psalm/Type/Atomic/TCallableArray.php5p}ecmפ1src/Psalm/Type/Atomic/TAnonymousClassInstance.php5p}e1Z&src/Psalm/Type/Atomic/TEmptyScalar.php-5p}e-a-̤"src/Psalm/Type/Atomic/TClosure.php 5p}e Dѩ)src/Psalm/Type/Atomic/TCallableObject.php5p}e 2&src/Psalm/Type/Atomic/TConditional.php5p}e 0Ǥ#src/Psalm/Type/Atomic/TCallable.phps 5p}es n&src/Psalm/Type/Atomic/TNamedObject.php5p}e /src/Psalm/Type/Atomic/TObjectWithProperties.php5p}eݷL)src/Psalm/Type/Atomic/TCallableString.php5p}e/src/Psalm/Type/Atomic/TTemplatePropertiesOf.php 5p}e 4o(src/Psalm/Type/Atomic/TNumericString.php5p}e;m"src/Psalm/Type/Atomic/TIntMask.php5p}eOzYT'src/Psalm/Type/Atomic/TPropertiesOf.php 5p}e aX src/Psalm/Type/Atomic/TMixed.phpF5p}eF-K.src/Psalm/Type/Atomic/HasIntersectionTrait.phpL5p}eLê-src/Psalm/Type/Atomic/TUnknownClassString.php5p}eE_!src/Psalm/Type/Atomic/TObject.php5p}eiY̤0src/Psalm/Type/Atomic/TNonspecificLiteralInt.phpC5p}eCil-src/Psalm/Type/Atomic/TCallableKeyedArray.php.5p}e.rQB*src/Psalm/Type/Atomic/TLowercaseString.phpx5p}ex٤+src/Psalm/Type/Atomic/TDependentGetType.php5p}eITsrc/Psalm/Type/Atomic/TList.php75p}e7xu;src/Psalm/Type/Atomic/TNonEmptyNonspecificLiteralString.php5p}e78$src/Psalm/Type/Atomic/TTypeAlias.php 5p}e @Ɂ(src/Psalm/Type/Atomic/TNonEmptyArray.php5p}ed)src/Psalm/Type/Atomic/TNonEmptyScalar.php85p}e8ge'src/Psalm/Type/Atomic/TCallableList.php5p}eR-src/Psalm/Type/Atomic/TLiteralClassString.php 5p}e :)src/Psalm/Type/Atomic/TClassStringMap.php5p}ej,src/Psalm/Type/Atomic/TTrue.php5p}e݆'src/Psalm/Type/Atomic/DependentType.php.5p}e.RI'src/Psalm/Type/Atomic/TEmptyNumeric.phpq5p}eqO̙6'src/Psalm/Type/Atomic/TLiteralFloat.php5p}eI&src/Psalm/Type/Atomic/TTraitString.php5p}eTsrc/Psalm/Type/Atomic/TBool.php>5p}e>$,src/Psalm/Type/Atomic/TDependentGetClass.php5p}e/l-src/Psalm/Type/Atomic/TTemplateParamClass.php5p}eL|'src/Psalm/Type/Atomic/TNonEmptyList.phpj5p}ej3V(src/Psalm/Type/Atomic/TLiteralString.phpB 5p}eB >i*src/Psalm/Type/Atomic/TTemplateValueOf.php5p}eCQĤ%src/Psalm/Type/Atomic/TLiteralInt.phpU5p}eUG" src/Psalm/Type/Atomic/Scalar.php5p}eJч"src/Psalm/Type/Atomic/TNumeric.php5p}e#src/Psalm/Type/Atomic/TIterable.php5p}e9#src/Psalm/Type/Atomic/TEnumCase.php 5p}e m, src/Psalm/Type/Atomic/TKeyOf.php9 5p}e9 z, src/Psalm/Type/Atomic/TArray.phpF5p}eF73src/Psalm/Type/Atomic/TNonspecificLiteralString.php5p}ees,!src/Psalm/Type/Atomic/TString.phpG5p}eG-7(src/Psalm/Type/Atomic/TTemplateParam.php5p}eN7&src/Psalm/Type/Atomic/TClassString.php5p}ḙ˺!src/Psalm/Type/Atomic/TScalar.php85p}e8MԤ(src/Psalm/Type/Atomic/TNonEmptyMixed.php5p}e src/Psalm/Type/Atomic/TNever.php5p}eĤsrc/Psalm/Type/Atomic/TInt.php;5p}e;[[%src/Psalm/Type/Atomic/TVoid.php5p}eXx+src/Psalm/Type/Atomic/TDependentListKey.php5p}eNyX'src/Psalm/Type/Atomic/TSingleLetter.php5p}eDg$src/Psalm/Type/Atomic/TIntMaskOf.php5p}e'0src/Psalm/Type/Atomic/TDependentGetDebugType.php5p}e|X#src/Psalm/Type/Atomic/TIntRange.php5p}e`"src/Psalm/Type/Atomic/TValueOf.php5p}e*#src/Psalm/Type/Atomic/TArrayKey.php5p}e" src/Psalm/Type/Atomic/TFalse.php5p}em 2src/Psalm/Type/Atomic/TNonEmptyLowercaseString.phpm5p}em(VMa%src/Psalm/Type/Atomic/TEmptyMixed.php5p}eFv(src/Psalm/Type/Atomic/TTemplateKeyOf.php5p}e)src/Psalm/Type/Atomic/TNull.phpn5p}en\ !|src/Psalm/CodeLocation/Raw.php5p}e4u-src/Psalm/CodeLocation/ParseErrorLocation.php5p}e4/src/Psalm/CodeLocation/DocblockTypeLocation.php5p}eK)src/Psalm/Storage/FunctionLikeStorage.php5p}eN>%&src/Psalm/Storage/ClassLikeStorage.phpI,5p}eI,Է+src/Psalm/Storage/FunctionLikeParameter.php5p}ee¤%src/Psalm/Storage/PropertyStorage.php. 5p}e. _Ҥ&src/Psalm/Storage/AttributeStorage.php5p}eJ#src/Psalm/Storage/MethodStorage.php5p}ea10src/Psalm/Storage/ImmutableNonCloneableTrait.php5p}eخ)src/Psalm/Storage/CustomMetadataTrait.php5p}e}ѕ+src/Psalm/Storage/Assertion/HasArrayKey.php_5p}e_"fg+src/Psalm/Storage/Assertion/IsCountable.php5p}ePV)&src/Psalm/Storage/Assertion/IsType.php5p}e0-^1src/Psalm/Storage/Assertion/IsNotLooselyEqual.phpU5p}eUl_/src/Psalm/Storage/Assertion/IsClassNotEqual.php5p}eO=p/*src/Psalm/Storage/Assertion/NotInArray.php$5p}e$R/(src/Psalm/Storage/Assertion/IsAClass.phpW5p}eWnar3src/Psalm/Storage/Assertion/NotNestedAssertions.php5p}eJ7h7src/Psalm/Storage/Assertion/DoesNotHaveAtLeastCount.php?5p}e?.src/Psalm/Storage/Assertion/IsNotIdentical.phpL5p}eLic4src/Psalm/Storage/Assertion/HasStringArrayAccess.php5p}e!0)src/Psalm/Storage/Assertion/IsNotType.php5p}eFb.src/Psalm/Storage/Assertion/IsLooselyEqual.php 5p}e MGw+src/Psalm/Storage/Assertion/IsIdentical.php5p}eDȚ*src/Psalm/Storage/Assertion/IsNotIsset.php/5p}e/#F&src/Psalm/Storage/Assertion/Truthy.php5p}eev3-src/Psalm/Storage/Assertion/IsGreaterThan.php5p}e@Ŏ,src/Psalm/Storage/Assertion/IsEqualIsset.php5p}e=m&&src/Psalm/Storage/Assertion/Empty_.php.5p}e.uؤ+src/Psalm/Storage/Assertion/IsNotAClass.php5p}e/src/Psalm/Storage/Assertion/HasAtLeastCount.php5p}e쎤-src/Psalm/Storage/Assertion/HasExactCount.phpE5p}eE2'src/Psalm/Storage/Assertion/InArray.php5p}e#*src/Psalm/Storage/Assertion/IsLessThan.php 5p}e hs(src/Psalm/Storage/Assertion/NonEmpty.php5p}e:Efx1src/Psalm/Storage/Assertion/NonEmptyCountable.phpf5p}ef6$?0src/Psalm/Storage/Assertion/NestedAssertions.php=5p}e=@%src/Psalm/Storage/Assertion/Falsy.php$5p}e$ÃQ1src/Psalm/Storage/Assertion/DoesNotHaveMethod.php5p}ee4src/Psalm/Storage/Assertion/NotNonEmptyCountable.phpy5p}eyE)src/Psalm/Storage/Assertion/HasMethod.php5p}eF/7,src/Psalm/Storage/Assertion/IsClassEqual.php5p}eF!.src/Psalm/Storage/Assertion/IsNotCountable.php5p}eSRl9src/Psalm/Storage/Assertion/HasIntOrStringArrayAccess.php 5p}e ZE\'src/Psalm/Storage/Assertion/IsIsset.php5p}eMw.src/Psalm/Storage/Assertion/ArrayKeyExists.php 5p}e n5src/Psalm/Storage/Assertion/DoesNotHaveExactCount.php<5p}e<ZϤ6src/Psalm/Storage/Assertion/IsGreaterThanOrEqualTo.phpK5p}eKr#src/Psalm/Storage/Assertion/Any.php5p}eq4src/Psalm/Storage/Assertion/ArrayKeyDoesNotExist.phpO5p}eOaDW3src/Psalm/Storage/Assertion/IsLessThanOrEqualTo.phpB5p}eBO%src/Psalm/Storage/FunctionStorage.php05p}e0|#Ĥ"src/Psalm/Storage/AttributeArg.php[5p}e[3h#src/Psalm/Storage/Possibilities.phpo5p}eos*src/Psalm/Storage/ClassConstantStorage.php 5p}e ͤ,src/Psalm/Storage/HasAttributesInterface.php5p}e src/Psalm/Storage/Assertion.php5p}eUG!src/Psalm/Storage/FileStorage.phpj5p}ejޫ5p}e>Ѓ 2src/Psalm/Plugin/PluginFileExtensionsInterface.php5p}eDsrc/Psalm/Plugin/EventHandler/PropertyExistenceProviderInterface.php5p}e+ǤCsrc/Psalm/Plugin/EventHandler/MethodVisibilityProviderInterface.php_5p}e_Dsrc/Psalm/Plugin/EventHandler/AfterFunctionLikeAnalysisInterface.phpf5p}ef4`Esrc/Psalm/Plugin/EventHandler/Event/MethodReturnTypeProviderEvent.php 5p}e &VdAsrc/Psalm/Plugin/EventHandler/Event/PropertyTypeProviderEvent.php5p}eKbIsrc/Psalm/Plugin/EventHandler/Event/AfterClassLikeExistenceCheckEvent.php5p}e>ۤKsrc/Psalm/Plugin/EventHandler/Event/AfterEveryFunctionCallAnalysisEvent.php5p}eKAsrc/Psalm/Plugin/EventHandler/Event/MethodParamsProviderEvent.php5p}eUDsrc/Psalm/Plugin/EventHandler/Event/AfterExpressionAnalysisEvent.php5p}ejZVFsrc/Psalm/Plugin/EventHandler/Event/AfterFunctionCallAnalysisEvent.php5p}e Ksrc/Psalm/Plugin/EventHandler/Event/DynamicFunctionStorageProviderEvent.phpg5p}eg'<src/Psalm/Plugin/EventHandler/Event/AddRemoveTaintsEvent.php:5p}e:MęFsrc/Psalm/Plugin/EventHandler/Event/AfterFunctionLikeAnalysisEvent.php' 5p}e'  5Dsrc/Psalm/Plugin/EventHandler/Event/AfterMethodCallAnalysisEvent.php 5p}e o$Fsrc/Psalm/Plugin/EventHandler/Event/FunctionExistenceProviderEvent.php5p}eC>src/Psalm/Plugin/EventHandler/Event/StringInterpreterEvent.phpd5p}edlaDsrc/Psalm/Plugin/EventHandler/Event/BeforeStatementAnalysisEvent.php&5p}e&Ej?src/Psalm/Plugin/EventHandler/Event/BeforeFileAnalysisEvent.php5p}e焤Gsrc/Psalm/Plugin/EventHandler/Event/PropertyVisibilityProviderEvent.php5p}e3Gsrc/Psalm/Plugin/EventHandler/Event/FunctionReturnTypeProviderEvent.php5p}eRP@@src/Psalm/Plugin/EventHandler/Event/AfterClassLikeVisitEvent.php5p}eXCsrc/Psalm/Plugin/EventHandler/Event/AfterCodebasePopulatedEvent.php5p}e@$Dsrc/Psalm/Plugin/EventHandler/Event/MethodExistenceProviderEvent.php5p}e&Csrc/Psalm/Plugin/EventHandler/Event/FunctionParamsProviderEvent.php5p}eP Fsrc/Psalm/Plugin/EventHandler/Event/PropertyExistenceProviderEvent.php5p}ebA]>src/Psalm/Plugin/EventHandler/Event/AfterFileAnalysisEvent.php5p}esrc/Psalm/Plugin/EventHandler/AfterClassLikeVisitInterface.phpQ5p}eQݷ<src/Psalm/Plugin/EventHandler/AfterFileAnalysisInterface.php!5p}e!o<src/Psalm/Plugin/EventHandler/StringInterpreterInterface.phpW5p}eWn2Asrc/Psalm/Plugin/EventHandler/AfterCodebasePopulatedInterface.php5p}e+m7Dsrc/Psalm/Plugin/EventHandler/FunctionExistenceProviderInterface.php5p}e^I*7src/Psalm/Plugin/EventHandler/RemoveTaintsInterface.php?5p}e?暤?src/Psalm/Plugin/EventHandler/PropertyTypeProviderInterface.phpj5p}ejYިAsrc/Psalm/Plugin/EventHandler/FunctionParamsProviderInterface.php5p}eUEsrc/Psalm/Plugin/EventHandler/FunctionReturnTypeProviderInterface.php5p}esBsrc/Psalm/Plugin/EventHandler/AfterExpressionAnalysisInterface.phpc5p}ec(u9src/Psalm/Plugin/EventHandler/BeforeAddIssueInterface.php5p}e١E4src/Psalm/Plugin/EventHandler/AddTaintsInterface.php75p}e7hBsrc/Psalm/Plugin/EventHandler/MethodExistenceProviderInterface.php5p}e'Dsrc/Psalm/Plugin/EventHandler/AfterFunctionCallAnalysisInterface.php5p}e,Csrc/Psalm/Plugin/EventHandler/MethodReturnTypeProviderInterface.php5p}eف Bsrc/Psalm/Plugin/EventHandler/BeforeStatementAnalysisInterface.php 5p}e V&Bsrc/Psalm/Plugin/EventHandler/AfterMethodCallAnalysisInterface.php5p}e<Asrc/Psalm/Plugin/EventHandler/AfterStatementAnalysisInterface.php]5p}e]&Isrc/Psalm/Plugin/EventHandler/DynamicFunctionStorageProviderInterface.php5p}e7ߍCsrc/Psalm/Plugin/EventHandler/BeforeExpressionAnalysisInterface.php[5p}e['ua8src/Psalm/Plugin/EventHandler/AfterAnalysisInterface.php5p}eAX=src/Psalm/Plugin/EventHandler/BeforeFileAnalysisInterface.php&5p}e& 5Esrc/Psalm/Plugin/EventHandler/PropertyVisibilityProviderInterface.phpg5p}eg/src/Psalm/Plugin/Shepherd.php'5p}e'}5*src/Psalm/Plugin/RegistrationInterface.php5p}e%դ,src/Psalm/Plugin/FileExtensionsInterface.php65p}e6j#src/Psalm/Plugin/ArgTypeInferer.phps5p}es.src/Psalm/Plugin/PluginEntryPointInterface.php5p}e*src/Psalm/Issue/UnrecognizedExpression.php5p}e0 $src/Psalm/Issue/NoEnumProperties.php5p}e(src/Psalm/Issue/PossiblyNullIterator.php5p}e%src/Psalm/Issue/DuplicateEnumCase.php5p}e%*Ť/src/Psalm/Issue/MixedStringOffsetAssignment.php5p}e>&src/Psalm/Issue/DeprecatedFunction.php5p}e_Ĥ(src/Psalm/Issue/PossiblyInvalidClone.php5p}ejŐ'src/Psalm/Issue/UnusedDocblockParam.php5p}eF src/Psalm/Issue/NullArgument.php5p}eMl"src/Psalm/Issue/TaintedInclude.php5p}e`3src/Psalm/Issue/UndefinedThisPropertyAssignment.php5p}e֤)src/Psalm/Issue/RedundantFunctionCall.php5p}e ,src/Psalm/Issue/PossiblyNullFunctionCall.php5p}e&src/Psalm/Issue/MixedPropertyFetch.php5p}eĤΤ*src/Psalm/Issue/TypeDoesNotContainType.php5p}ez`I,src/Psalm/Issue/InvalidDocblockParamName.php5p}eZ"src/Psalm/Issue/UndefinedTrace.php5p}e$src/Psalm/Issue/ClassIssue.php5p}enۤ#src/Psalm/Issue/MixedIssueTrait.php5p}eq(src/Psalm/Issue/UndefinedMagicMethod.php5p}eڥ2src/Psalm/Issue/PossiblyNullPropertyAssignment.php?5p}e?**src/Psalm/Issue/DuplicateEnumCaseValue.php5p}ee(src/Psalm/Issue/InvalidPropertyFetch.php5p}eRFO)src/Psalm/Issue/TooManyTemplateParams.php5p}e^ src/Psalm/Issue/TaintedInput.php5p}e )src/Psalm/Issue/DirectConstructorCall.php5p}eQ_&src/Psalm/Issue/ImpureFunctionCall.php5p}ee?e1src/Psalm/Issue/MismatchingDocblockReturnType.php5p}eOy*src/Psalm/Issue/NullPropertyAssignment.php05p}e06'src/Psalm/Issue/PossiblyUnusedParam.php5p}eH&src/Psalm/Issue/InheritorViolation.php5p}e'5L?*src/Psalm/Issue/MixedArrayTypeCoercion.php5p}eљ&2src/Psalm/Issue/InvalidInterfaceImplementation.php5p}ex#7src/Psalm/Issue/PossiblyNullPropertyAssignmentValue.php5p}e0+src/Psalm/Issue/MixedReturnTypeCoercion.php5p}ec+!src/Psalm/Issue/InvalidGlobal.php5p}eY@.&src/Psalm/Issue/AbstractMethodCall.php5p}ed(src/Psalm/Issue/MissingTemplateParam.php5p}e9=௤#src/Psalm/Issue/InvalidToString.php5p}e +src/Psalm/Issue/PossiblyNullArrayOffset.php5p}exm+src/Psalm/Issue/UnhandledMatchCondition.php5p}eCah(src/Psalm/Issue/ParadoxicalCondition.php5p}e :src/Psalm/Issue/PossiblyInvalidPropertyAssignmentValue.php5p}eT'src/Psalm/Issue/ContinueOutsideLoop.php5p}e KA/src/Psalm/Issue/UndefinedMagicPropertyFetch.php5p}e6K(src/Psalm/Issue/ImpureStaticVariable.php5p}e2B+src/Psalm/Issue/FalsableReturnStatement.php5p}e&src/Psalm/Issue/InvalidStringClass.php5p}e5J&src/Psalm/Issue/PrivateFinalMethod.php5p}em)src/Psalm/Issue/UninitializedProperty.php5p}e[J413src/Psalm/Issue/PossiblyUndefinedIntArrayOffset.php5p}eIMΤsrc/Psalm/Issue/NoValue.php5p}e`w%src/Psalm/Issue/NonStaticSelfCall.php5p}eR-src/Psalm/Issue/DocblockTypeContradiction.php5p}e21'src/Psalm/Issue/UnusedBaselineEntry.php5p}e )&src/Psalm/Issue/InvalidArrayAccess.php5p}eͤ"src/Psalm/Issue/InvalidOperand.php5p}egū!src/Psalm/Issue/ArgumentIssue.php5p}e,(src/Psalm/Issue/InvalidTemplateParam.php5p}e!a*src/Psalm/Issue/UndefinedDocblockClass.php5p}e(&src/Psalm/Issue/PsalmInternalError.php5p}ea3/src/Psalm/Issue/OverriddenInterfaceConstant.php5p}eQ$src/Psalm/Issue/ImpureMethodCall.php5p}e src/Psalm/Issue/NullIterator.php5p}eQyh#src/Psalm/Issue/NullArrayOffset.php5p}e6src/Psalm/Issue/PluginIssue.php]5p}e]䕿H-src/Psalm/Issue/InvalidPropertyAssignment.php5p}e&f.'src/Psalm/Issue/UnresolvableInclude.php5p}e $k8src/Psalm/Issue/MethodSignatureMustProvideReturnType.php5p}e11썤/src/Psalm/Issue/PossiblyInvalidFunctionCall.php5p}eң` src/Psalm/Issue/InvalidThrow.php5p}e234src/Psalm/Issue/NonInvariantDocblockPropertyType.php5p}e2 ¤&src/Psalm/Issue/RawObjectIteration.php5p}e4@bsrc/Psalm/Issue/CodeIssue.php' 5p}e' j&src/Psalm/Issue/UnusedForeachValue.php5p}ep!src/Psalm/Issue/ForbiddenCode.php5p}ey61src/Psalm/Issue/LessSpecificClassConstantType.php5p}ef$src/Psalm/Issue/InvalidAttribute.php5p}eŻ#src/Psalm/Issue/MixedAssignment.php5p}e¿ src/Psalm/Issue/InvalidScope.php5p}eߏǤsrc/Psalm/Issue/MissingFile.php|5p}e|,3src/Psalm/Issue/MismatchingDocblockPropertyType.php5p}eg42src/Psalm/Issue/InvalidPropertyAssignmentValue.php5p}em.src/Psalm/Issue/PossiblyInvalidArrayAccess.php5p}eQr<*src/Psalm/Issue/OverriddenMethodAccess.php5p}eˡsrc/Psalm/Issue/NullOperand.php5p}eEwV!src/Psalm/Issue/PropertyIssue.php5p}ę+src/Psalm/Issue/MixedInferredReturnType.php5p}e)b/src/Psalm/Issue/PossiblyNullArrayAssignment.php5p}ec"=&src/Psalm/Issue/UnusedFunctionCall.php5p}eOԅ2src/Psalm/Issue/RedundantCastGivenDocblockType.php5p}eYߤ!src/Psalm/Issue/VariableIssue.php5p}e"vG%src/Psalm/Issue/MixedFunctionCall.php5p}eӭ&src/Psalm/Issue/InvalidArrayOffset.php5p}ePZ!src/Psalm/Issue/InternalClass.php5p}e(src/Psalm/Issue/ImpureStaticProperty.php5p}e0i/src/Psalm/Issue/UndefinedPropertyAssignment.php5p}e+M70src/Psalm/Issue/PossiblyUndefinedArrayOffset.php5p}e1src/Psalm/Issue/MethodIssue.php5p}eLup6src/Psalm/Issue/ImplementationRequirementViolation.php5p}e-Ϥ0src/Psalm/Issue/ConstructorSignatureMismatch.php5p}eyg֤0src/Psalm/Issue/PossiblyInvalidPropertyFetch.php5p}eY9k'src/Psalm/Issue/MissingPropertyType.php5p}esrc/Psalm/Issue/TaintedHtml.php5p}e$/src/Psalm/Issue/LessSpecificReturnStatement.php5p}e^s8src/Psalm/Issue/RedundantPropertyInitializationCheck.php5p}e֕0src/Psalm/Issue/ReferenceConstraintViolation.php5p}e-src/Psalm/Issue/RedundantIdentityWithTrue.php5p}ey5src/Psalm/Issue/PossiblyInvalidPropertyAssignment.php5p}eZϙ.src/Psalm/Issue/PossiblyInvalidDocblockTag.php5p}eT-5src/Psalm/Issue/UnsupportedPropertyReferenceUsage.php5p}exN!ڤ-src/Psalm/Issue/PossiblyUnusedReturnValue.php5p}e&src/Psalm/Issue/DeprecatedProperty.php5p}eh z(src/Psalm/Issue/PossiblyNullArgument.php5p}e4>c%src/Psalm/Issue/MissingDependency.php5p}eӬ(src/Psalm/Issue/InvalidEnumCaseValue.php5p}eܤ%src/Psalm/Issue/TaintedUserSecret.php5p}ensrc/Psalm/Issue/CheckType.php5p}e>#C'src/Psalm/Issue/InvalidFunctionCall.php5p}eN(src/Psalm/Issue/InvalidNamedArgument.php5p}e)פ src/Psalm/Issue/ReservedWord.php5p}e=F6src/Psalm/Issue/PossiblyUndefinedStringArrayOffset.php5p}eu-src/Psalm/Issue/InaccessibleClassConstant.php5p}e*src/Psalm/Issue/PossiblyFalseReference.php5p}eWϤ)src/Psalm/Issue/PossiblyFalseArgument.php5p}e]/src/Psalm/Issue/UnimplementedAbstractMethod.php5p}e 5src/Psalm/Issue/ReferenceReusedFromConfusingScope.php5p}eݪD#src/Psalm/Issue/InvalidIterator.php5p}e.Q-src/Psalm/Issue/PossiblyInvalidMethodCall.php5p}e-u src/Psalm/Issue/TaintedSql.php~5p}e~=%src/Psalm/Issue/UndefinedVariable.php5p}eG6#src/Psalm/Issue/DeprecatedTrait.php5p}eIݰ*src/Psalm/Issue/TypeDoesNotContainNull.php5p}e淽Ф(src/Psalm/Issue/InaccessibleProperty.php5p}e@src/Psalm/Issue/TaintedEval.php5p}e&sxP)src/Psalm/Issue/MissingThrowsDocblock.php5p}eHjQ,src/Psalm/Issue/OverriddenPropertyAccess.php5p}el *!src/Psalm/Issue/ComplexMethod.php5p}e0ZB+src/Psalm/Issue/InvalidStaticInvocation.php5p}e0src/Psalm/Issue/MismatchingDocblockParamType.php5p}e#src/Psalm/Issue/UndefinedMethod.php5p}e%Ӥ%src/Psalm/Issue/MissingReturnType.php5p}e׊"src/Psalm/Issue/InternalMethod.php5p}e!1'src/Psalm/Issue/PossiblyNullOperand.php5p}e R['src/Psalm/Issue/InvalidParamDefault.php5p}e q:/src/Psalm/Issue/ImpureByReferenceAssignment.php5p}e|ҍ src/Psalm/Issue/FalseOperand.php5p}e(src/Psalm/Issue/PossiblyUnusedMethod.php5p}e#src/Psalm/Issue/InvalidDocblock.php5p}eQ}J!src/Psalm/Issue/TaintedCookie.php5p}e5R0src/Psalm/Issue/TraitMethodSignatureMismatch.php5p}e7¤&src/Psalm/Issue/RedundantCondition.php5p}eX|Ϥ!src/Psalm/Issue/RedundantCast.php5p}eb&src/Psalm/Issue/MissingConstructor.php5p}eQ"src/Psalm/Issue/UnusedProperty.php5p}eU$src/Psalm/Issue/DeprecatedMethod.php5p}e+src/Psalm/Issue/UndefinedAttributeClass.php5p}e7src/Psalm/Issue/RedundantConditionGivenDocblockType.php5p}eD<"src/Psalm/Issue/DuplicateClass.php5p}e77*.src/Psalm/Issue/UnsafeGenericInstantiation.php5p}e&2src/Psalm/Issue/InvalidConstantAssignmentValue.php5p}e Z+src/Psalm/Issue/PossiblyNullArrayAccess.php85p}e8*'M%src/Psalm/Issue/ParamNameMismatch.php5p}eT#src/Psalm/Issue/StringIncrement.php5p}ekq'src/Psalm/Issue/ImpurePropertyFetch.php5p}eȤ%src/Psalm/Issue/UnusedConstructor.php5p}eي1src/Psalm/Issue/ExtensionRequirementViolation.php5p}eI*src/Psalm/Issue/LessSpecificReturnType.php5p}eyp"src/Psalm/Issue/ParentNotFound.php5p}eޤ.src/Psalm/Issue/UndefinedThisPropertyFetch.php5p}eD#src/Psalm/Issue/DeprecatedClass.php5p}eHn-src/Psalm/Issue/MixedPropertyTypeCoercion.php5p}ep/=̤.src/Psalm/Issue/ConstantDeclarationInTrait.php5p}e7l+src/Psalm/Issue/OverriddenFinalConstant.php5p}ew7&src/Psalm/Issue/ClassConstantIssue.php}5p}e}`0ڤsrc/Psalm/Issue/ParseError.php5p}e'src/Psalm/Issue/DeprecatedInterface.php5p}eD,src/Psalm/Issue/ImpurePropertyAssignment.php5p}eh)&src/Psalm/Issue/InvalidExtendClass.php5p}ea Ȥsrc/Psalm/Issue/RiskyCast.php5p}eW˖*src/Psalm/Issue/InvalidPassByReference.php5p}e$src/Psalm/Issue/IfThisIsMismatch.php5p}eaŔ*src/Psalm/Issue/InvalidArrayAssignment.php5p}ej!+src/Psalm/Issue/UndefinedGlobalVariable.php5p}eGW턤2src/Psalm/Issue/PossiblyInvalidArrayAssignment.php5p}e src/Psalm/Issue/Trace.php5p}e)%src/Psalm/Issue/UndefinedConstant.php5p}eȊФ"src/Psalm/Issue/DuplicateParam.php5p}eM src/Psalm/Issue/InvalidCatch.php5p}eUYje-src/Psalm/Issue/PossiblyUndefinedVariable.php5p}e$(src/Psalm/Issue/ArgumentTypeCoercion.php5p}eӖ$src/Psalm/Issue/UnusedMethodCall.php5p}er src/Psalm/Issue/InvalidClone.php5p}e?3src/Psalm/Issue/PossiblyUndefinedGlobalVariable.php5p}eߤ%src/Psalm/Issue/MutableDependency.php5p}e5mݤsrc/Psalm/Issue/MixedIssue.php5p}e!Ϥ%src/Psalm/Issue/InvalidMethodCall.php5p}eHE!src/Psalm/Issue/InvalidParent.php5p}e7#src/Psalm/Issue/DuplicateMethod.php5p}eʤU&src/Psalm/Issue/DeprecatedConstant.php5p}ep'+դ5src/Psalm/Issue/MethodSignatureMustOmitReturnType.php5p}ei$src/Psalm/Issue/MixedArrayOffset.php5p}eI(src/Psalm/Issue/PossiblyFalseOperand.php5p}eΤsrc/Psalm/Issue/TaintedFile.php5p}e¤&src/Psalm/Issue/UndefinedInterface.php5p}eo4src/Psalm/Issue/InvalidTraversableImplementation.php5p}e+src/Psalm/Issue/PossiblyInvalidIterator.php5p}e|;B+src/Psalm/Issue/MissingClosureParamType.php5p}eR*src/Psalm/Issue/InvalidEnumBackingType.php5p}eor+src/Psalm/Issue/NamedArgumentNotAllowed.php5p}e2G{)src/Psalm/Issue/NoInterfaceProperties.php5p}eX7fsrc/Psalm/Issue/TaintedSSRF.php5p}ej_%src/Psalm/Issue/UndefinedFunction.php5p}eN,src/Psalm/Issue/MissingClosureReturnType.php5p}eKI*src/Psalm/Issue/InvalidReturnStatement.php5p}eqaT*src/Psalm/Issue/UndefinedPropertyFetch.php5p}eMn*src/Psalm/Issue/InterfaceInstantiation.php5p}e^ᔤ$src/Psalm/Issue/LoopInvalidation.php5p}e2'src/Psalm/Issue/UnusedPsalmSuppress.php5p}eġ)"src/Psalm/Issue/UndefinedTrait.php5p}eϤ%src/Psalm/Issue/CircularReference.php5p}eYj+src/Psalm/Issue/MethodSignatureMismatch.php5p}eūM&src/Psalm/Issue/InaccessibleMethod.php5p}e-_&src/Psalm/Issue/UnusedClosureParam.php5p}eR 0/%src/Psalm/Issue/InvalidEnumMethod.php5p}esxP*src/Psalm/Issue/InvalidLiteralArgument.php5p}eT.src/Psalm/Issue/UncaughtThrowInGlobalScope.php5p}e(src/Psalm/Issue/PropertyTypeCoercion.php5p}eNi#src/Psalm/Issue/MixedMethodCall.php5p}e?Eͤ#src/Psalm/Issue/TaintedCallable.php5p}e\ˤ*src/Psalm/Issue/PossiblyUnusedProperty.php5p}e#src/Psalm/Issue/NullArrayAccess.php)5p}e)bNw!src/Psalm/Issue/TaintedHeader.php5p}e[ݤsrc/Psalm/Issue/MixedClone.php5p}e֤$src/Psalm/Issue/NullFunctionCall.php5p}eјu:src/Psalm/Issue/RedundantFunctionCallGivenDocblockType.php5p}eR+src/Psalm/Issue/PossiblyInvalidArgument.php5p}eo#!src/Psalm/Issue/FunctionIssue.php5p}eCN-src/Psalm/Issue/MixedArgumentTypeCoercion.php5p}e'src/Psalm/Issue/PossiblyInvalidCast.php5p}el:(src/Psalm/Issue/MixedArrayAssignment.php5p}e4$src/Psalm/Issue/MissingParamType.php5p}ej-src/Psalm/Issue/InvalidNullableReturnType.php5p}eDH)src/Psalm/Issue/TaintedTextWithQuotes.php5p}epLX9#src/Psalm/Issue/UnevaluatedCode.php5p}e" src/Psalm/Issue/TaintedShell.php5p}eQ!src/Psalm/Issue/NullReference.php5p}eФ"src/Psalm/Issue/UnusedVariable.php5p}e&+src/Psalm/Issue/MixedPropertyAssignment.php5p}eX.src/Psalm/Issue/MissingImmutableAnnotation.php5p}e/m src/Psalm/Issue/MixedOperand.php5p}e|\.src/Psalm/Issue/PossiblyInvalidArrayOffset.php5p}euۤsrc/Psalm/Issue/UnusedParam.php5p}e/i src/Psalm/Issue/InvalidClass.php5p}ee%src/Psalm/Issue/NullPropertyFetch.php+5p}e+$src/Psalm/Issue/AssignmentToVoid.php5p}evx(#src/Psalm/Issue/InvalidArgument.php5p}edYԪsrc/Psalm/Issue/UnusedClass.php5p}e%src/Psalm/Issue/InvalidReturnType.php5p}e(0src/Psalm/Issue/AmbiguousConstantInheritance.php5p}eK+src/Psalm/Issue/PossiblyUndefinedMethod.php5p}e src/Psalm/Issue/UnusedMethod.php5p}ecѳ"src/Psalm/Issue/ImpureVariable.php5p}ek .src/Psalm/Issue/PossibleRawObjectIteration.php5p}eM!(src/Psalm/Issue/UnresolvableConstant.php5p}eЯt$src/Psalm/Issue/MixedArrayAccess.php5p}e\OѤ-src/Psalm/Issue/PossiblyNullPropertyFetch.php:5p}e:o%src/Psalm/Issue/DuplicateConstant.php5p}e$\src/Psalm/Issue/ConfigIssue.php5p}eµ !src/Psalm/Issue/TaintedCustom.php5p}eiT1src/Psalm/Issue/ImplementedReturnTypeMismatch.php5p}e5$src/Psalm/Issue/InternalProperty.php5p}e`w'src/Psalm/Issue/MissingDocblockType.php5p}eL.,src/Psalm/Issue/NonInvariantPropertyType.php5p}ec !src/Psalm/Issue/MixedArgument.php5p}ex ,src/Psalm/Issue/InvalidClassConstantType.php5p}e4N)src/Psalm/Issue/PossiblyNullReference.php5p}e2(src/Psalm/Issue/ImplicitToStringCast.php5p}enUv$src/Psalm/Issue/TooManyArguments.php5p}eU0)src/Psalm/Issue/AbstractInstantiation.php5p}e%ɤ%src/Psalm/Issue/DuplicateFunction.php5p}eҴ8K#src/Psalm/Issue/ComplexFunction.php5p}eE)src/Psalm/Issue/PossiblyFalseIterator.php5p}efG&src/Psalm/Issue/TaintedUnserialize.php5p}e>ͤsrc/Psalm/Issue/TaintedLdap.php5p}e:+src/Psalm/Issue/NullableReturnStatement.php5p}e )0src/Psalm/Issue/UnimplementedInterfaceMethod.php5p}e2%src/Psalm/Issue/UnusedReturnValue.php5p}eY0src/Psalm/Issue/ImplementedParamTypeMismatch.php5p}ee'src/Psalm/Issue/UnsafeInstantiation.php5p}ew&-src/Psalm/Issue/InvalidFalsableReturnType.php5p}e1D#src/Psalm/Issue/TooFewArguments.php5p}eiH8$src/Psalm/Issue/EmptyArrayAccess.php5p}eRXR%src/Psalm/Issue/DuplicateArrayKey.php5p}e6%src/Psalm/Issue/InvalidTypeImport.php5p}eR5src/Psalm/Issue/LessSpecificImplementedReturnType.php5p}e!3(src/Psalm/Issue/MixedReturnStatement.php5p}e4src/Psalm/Issue/MoreSpecificImplementedParamType.php5p}e¤-src/Psalm/Issue/UnsupportedReferenceUsage.php5p}ey6)src/Psalm/Issue/UnrecognizedStatement.php5p}e5w4src/Psalm/Issue/UndefinedMagicPropertyAssignment.php5p}e޾פ,src/Psalm/Issue/UndefinedInterfaceMethod.php5p}ehL*src/Psalm/Issue/MoreSpecificReturnType.php5p}e,src/Psalm/Issue/UnnecessaryVarAnnotation.php5p}e6zb]/src/Psalm/Issue/PropertyNotSetInConstructor.php5p}e 0"src/Psalm/Issue/UndefinedClass.php5p}e*src/Psalm/Issue/PossiblyInvalidOperand.php5p}eȤ2src/Psalm/Issue/ConflictingReferenceConstraint.php5p}etN)src/Psalm/Issue/InvalidScalarArgument.php5p}e@A9src/Psalm/Issue/InvalidCast.php5p}eye'src/Psalm/Issue/TaintedSystemSecret.php5p}e8src/Psalm/Issue/PossiblyFalsePropertyAssignmentValue.php5p}eJ8?src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.phpp5p}eptQ.src/Psalm/Internal/Codebase/TaintFlowGraph.php<5p}e<_-src/Psalm/Internal/Codebase/DataFlowGraph.phpR5p}eR)src/Psalm/Internal/Codebase/Populator.phpޜ5p}eޜ6src/Psalm/Internal/Codebase/InternalCallMapHandler.php?45p}e?4&Zc8src/Psalm/Internal/Codebase/StorageByPatternResolver.php5p}eI#u3src/Psalm/Internal/Codebase/ImpureFunctionsList.php5p}e95src/Psalm/Internal/Codebase/ReferenceMapGenerator.php5p}e&-*src/Psalm/Internal/Codebase/Reflection.phpG5p}eG{m*src/Psalm/Internal/Codebase/Properties.php6)5p}e6)f4src/Psalm/Internal/Codebase/ConstantTypeResolver.php15p}e1~+src/Psalm/Internal/Codebase/PropertyMap.php5p}e{Asrc/Psalm/Internal/Codebase/AssertionsFromInheritanceResolver.php=5p}e=X*src/Psalm/Internal/Codebase/ClassLikes.phpA5p}eA,(src/Psalm/Internal/Codebase/Analyzer.php5p}eQܽ)src/Psalm/Internal/Codebase/Functions.php?5p}e?e'src/Psalm/Internal/Codebase/Methods.php5p}el ݹ0src/Psalm/Internal/Codebase/VariableUseGraph.php5p}eu'('src/Psalm/Internal/Codebase/Scanner.phpjf5p}ejf#GN0src/Psalm/Internal/TypeVisitor/TypeLocalizer.php) 5p}e) I8src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php5p}e^/9src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php5p}eR05src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php 5p}e |;src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php5p}ea4src/Psalm/Internal/TypeVisitor/ClasslikeReplacer.phpp5p}epRc8src/Psalm/Internal/TypeVisitor/ContainsStaticVisitor.phpr5p}er=Ĥ.src/Psalm/Internal/TypeVisitor/TypeScanner.php 5p}e eᆤ.src/Psalm/Internal/TypeVisitor/TypeChecker.php-5p}e->src/Psalm/Internal/TypeVisitor/CanContainObjectTypeVisitor.php5p}e  src/Psalm/Internal/CliUtils.php@5p}e@**src/Psalm/Internal/Fork/PsalmRestarter.php5p}e~'src/Psalm/Internal/Fork/ForkMessage.phpA5p}eA/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php5p}e+ src/Psalm/Internal/Fork/Pool.php @5p}e @g ,3src/Psalm/Internal/Fork/ForkProcessErrorMessage.phpr5p}erQ2src/Psalm/Internal/Fork/ForkProcessDoneMessage.php5p}e:3src/Psalm/Internal/PhpTraverser/CustomTraverser.php5p}e\src/Psalm/Internal/Cache.php 5p}e Ӥ'src/Psalm/Internal/IncludeCollector.php5p}e src/Psalm/Internal/Json/Json.php5p}e3~5src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php5p}eб}%src/Psalm/Internal/Diff/AstDiffer.php] 5p}e] &&src/Psalm/Internal/Diff/FileDiffer.php!5p}e!_$src/Psalm/Internal/Diff/DiffElem.php*5p}e*C0src/Psalm/Internal/Diff/FileStatementsDiffer.php5p}ejo*1src/Psalm/Internal/Diff/ClassStatementsDiffer.php*5p}e*/|$src/Psalm/Internal/DataFlow/Path.php5p}e~ˌ+src/Psalm/Internal/DataFlow/TaintSource.php5p}e(,src/Psalm/Internal/DataFlow/DataFlowNode.phpX 5p}eX ]H)src/Psalm/Internal/DataFlow/TaintSink.php5p}eF#src/Psalm/Internal/Cli/Refactor.php''5p}e''*o)src/Psalm/Internal/Cli/LanguageServer.php75p}e7 src/Psalm/Internal/Cli/Psalm.php!5p}e!c"src/Psalm/Internal/Cli/Psalter.phpH5p}eHW!!src/Psalm/Internal/Cli/Plugin.php"5p}e"48'src/Psalm/Internal/MethodIdentifier.phpN5p}eNxf/src/Psalm/Internal/Algebra/FormulaGenerator.phpy05p}ey0O_)src/Psalm/Internal/Scope/FinallyScope.phpz5p}ez3ޤ&src/Psalm/Internal/Scope/CaseScope.php5p}e!٤$src/Psalm/Internal/Scope/IfScope.php5p}eb&src/Psalm/Internal/Scope/LoopScope.php5p}e|H/src/Psalm/Internal/Scope/IfConditionalScope.phpc5p}ec@k(src/Psalm/Internal/Scope/SwitchScope.php5p}eKA7src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php5p}e5Esrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php5p}e#@src/Psalm/Internal/Scanner/UnresolvedConstant/EnumValueFetch.php5p}e,Ǥ:src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php5p}e) ?src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php5p}e+Dsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.phpF5p}eFՖäDsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php!5p}e!ѤCsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php5p}e Fsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php#5p}e#gFsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php5p}eΧE?src/Psalm/Internal/Scanner/UnresolvedConstant/EnumNameFetch.php5p}e=src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php5p}e̓d>src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php5p}e3פIsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php&5p}e&ܯؤFsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php5p}e_Csrc/Psalm/Internal/Scanner/UnresolvedConstant/EnumPropertyFetch.php5p}eAmc=src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php|5p}e|@m<src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php5p}e>2Lsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php)5p}e)kնFsrc/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php5p}e9|bBsrc/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php5p}eK *src/Psalm/Internal/Scanner/FileScanner.php 5p}e ?X2src/Psalm/Internal/Scanner/PhpStormMetaScanner.phpL85p}eL8h!-src/Psalm/Internal/Scanner/ParsedDocblock.php 5p}e z<1src/Psalm/Internal/Scanner/VarDocblockComment.phpC5p}eC:src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php5p}eC-src/Psalm/Internal/Scanner/DocblockParser.php(5p}e(6src/Psalm/Internal/Scanner/FunctionDocblockComment.php 5p}e ;usrc/Psalm/Internal/Clause.php5p}eCCsrc/Psalm/Internal/Composer.phpj5p}ejp$src/Psalm/Internal/RuntimeCaches.php5p}e9V_,src/Psalm/Internal/Type/ParseTreeCreator.phpq5p}eq%<src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php5p}ep3C7src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php}5p}e})src/Psalm/Internal/Type/TypeTokenizer.php55p}e5f&0I4src/Psalm/Internal/Type/ParseTree/KeyedArrayTree.phpp5p}ep^l] 1src/Psalm/Internal/Type/ParseTree/GenericTree.phpm5p}emK5src/Psalm/Internal/Type/ParseTree/MethodParamTree.php5p}ew Τ6src/Psalm/Internal/Type/ParseTree/IntersectionTree.php5p}e) Ϥ2src/Psalm/Internal/Type/ParseTree/NullableTree.php5p}eT_3src/Psalm/Internal/Type/ParseTree/FieldEllipsis.php5p}eWkH+src/Psalm/Internal/Type/ParseTree/Value.php[5p}e[Y 4src/Psalm/Internal/Type/ParseTree/TemplateAsTree.php5p}eʫ.2src/Psalm/Internal/Type/ParseTree/CallableTree.phpn5p}en,Lm7src/Psalm/Internal/Type/ParseTree/CallableParamTree.php5p}e4.ʤ5src/Psalm/Internal/Type/ParseTree/ConditionalTree.php5p}e*VKv>src/Psalm/Internal/Type/ParseTree/MethodWithReturnTypeTree.php5p}e57src/Psalm/Internal/Type/ParseTree/EncapsulationTree.php5p}eցU֤@src/Psalm/Internal/Type/ParseTree/CallableWithReturnTypeTree.php5p}exo;94src/Psalm/Internal/Type/ParseTree/TemplateIsTree.php^5p}e^Ϥ*src/Psalm/Internal/Type/ParseTree/Root.php5p}eLj֤0src/Psalm/Internal/Type/ParseTree/MethodTree.phpF5p}eF<src/Psalm/Internal/Type/ParseTree/KeyedArrayPropertyTree.phpR5p}eRp/src/Psalm/Internal/Type/ParseTree/UnionTree.php5p}e5~F7src/Psalm/Internal/Type/ParseTree/IndexedAccessTree.phpM5p}eMO%"5src/Psalm/Internal/Type/SimpleAssertionReconciler.php^5p}e^pŤ%src/Psalm/Internal/Type/TypeAlias.php?5p}e?4%src/Psalm/Internal/Type/ParseTree.php~5p}e~U-n5src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.phpN5p}eNb} 7src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php<5p}e<&4src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php5p}e)%+src/Psalm/Internal/Type/TypeCombination.php 5p}e `&src/Psalm/Internal/Type/TypeParser.php(5p}e(fӤ6src/Psalm/Internal/Type/NegatedAssertionReconciler.php2?5p}e2?48src/Psalm/Internal/Type/TemplateInferredTypeReplacer.phpA5p}eAap f)src/Psalm/Internal/Type/TemplateBound.php5p}e|/src/Psalm/Internal/Type/AssertionReconciler.phpg5p}eg (src/Psalm/Internal/Type/TypeExpander.php|5p}e| H(src/Psalm/Internal/Type/TypeCombiner.php5p}es*src/Psalm/Internal/Type/TemplateResult.php+ 5p}e+ eF4src/Psalm/Internal/Type/ClosedInheritanceToUnion.php5 5p}e5 $5&;src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php95p}e9nnq;src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php5p}e>쎤Isrc/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php`15p}e`1Ssrc/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php5p}e)Ssrc/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.phpV5p}eVӤVsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php$5p}e$GZsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.phpE35p}eE3P qpZsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php~q5p}e~qgIDbsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.phpT5p}eT#nȲ_src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php5p}eT=NZsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php5p}eۤ`src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php5p}eEZsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php5p}eAᢤ]src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.phpH5p}eH-xSsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php5p}e3CդSsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgInfo.php5p}eOU*Osrc/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php5p}e󔄤Fsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php$p5p}e$p_p.Lsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.phpE5p}eErMsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php-5p}e-KOsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php;5p}e;~2 Vsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgHandler.php6,5p}e6,e+ɤMsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php45p}e4ͮK`src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php5p}ethsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.phpRQ5p}eRQ+Ksrc/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php5p}eKsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php5p}e 65Xsrc/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.phpe5p}ee]7Ysrc/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php5p}eFJCsrc/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php&5p}e& easrc/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.phpk(5p}ek(@ csrc/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.phpy5p}ey{h/Xsrc/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php5p}e,Qsrc/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php5p}e \Hsrc/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php5p}e 1Fsrc/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.phpI5p}eIEsrc/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php*5p}e*!-8src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php5p}e]ߣԤ=src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.phpFE5p}eFEE8src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php 5p}e 7\,=9src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php 5p}e Bsrc/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php15p}e1pkؤFsrc/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php-5p}e-[W!<src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.phpcA5p}ecAa \?src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.phpm45p}em4JjS>src/Psalm/Internal/Analyzer/Statements/Block/WhileAnalyzer.phpK5p}eK?src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php5p}eN.t;src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php5p}eX7ۤ=src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.phpX5p}eXZBCsrc/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php;X5p}e;X0@n@src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php5p}e|.ŤDsrc/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php35p}e3ĠBsrc/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.phpb=5p}eb=^{Fsrc/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php!55p}e!5Ԛ<src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php5p}e:+9src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php>5p}e>Vj7src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php 5p}e o.ι9src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php\T5p}e\T-src/Psalm/Internal/Analyzer/TraitAnalyzer.php5p}e4'?@src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php}!5p}e}!?src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php{5p}e{uv|(src/Psalm/Internal/Analyzer/CanAlias.phpL5p}eL4p2src/Psalm/Internal/Analyzer/AttributesAnalyzer.phpk05p}ek0'y~.src/Psalm/Internal/Analyzer/MethodAnalyzer.php[&5p}e[&0/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php5p}e"ä-src/Psalm/Internal/Analyzer/ClassAnalyzer.php-5p}e-n:1src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php\5p}e\+1src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php5p}e1src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php#5p}e#G.src/Psalm/Internal/Analyzer/SourceAnalyzer.php|5p}e|ĉ\/src/Psalm/Internal/Analyzer/CommentAnalyzer.php>5p}e>I0src/Psalm/Internal/Analyzer/MethodComparator.php5p}ePդ5src/Psalm/Internal/FileManipulation/CodeMigration.php5p}e @src/Psalm/Internal/FileManipulation/ClassDocblockManipulator.php 5p}e _p/>src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php5p}ezCsrc/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php5p}eCuCsrc/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.phpJ5p}eJ,4src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.phpf5p}ef9<src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.phpK5p}eK3dFsrc/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.phpjb5p}ejbW =src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php|/5p}e|/聴ĤCsrc/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.phpS5p}eS'd=src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php5p}e?v2>src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php:E5p}e:E蓤Csrc/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.phpÝ5p}eÝQ@src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php15p}e1[hjGsrc/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php 5p}e C4src/Psalm/Internal/PhpVisitor/NodeCleanerVisitor.phpi5p}eifF}9src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php5p}e0,̤-src/Psalm/Internal/PhpVisitor/TraitFinder.phpn 5p}en f34src/Psalm/Internal/PhpVisitor/YieldTypeCollector.php5p}ec 6src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php5p}eE*Y9src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php 5p}e `6src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php5p}eZM¤4src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php_5p}e_~$5src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php5p}e:y9src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php 5p}e qP0src/Psalm/Internal/PhpVisitor/CloningVisitor.php 5p}e PO6src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php^35p}e^3S4src/Psalm/Internal/PhpVisitor/NodeCounterVisitor.php@5p}e@oN72src/Psalm/Internal/PhpVisitor/ReflectorVisitor.phpV5p}eV9 :src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php5p}e8g8src/Psalm/Internal/Provider/FileStorageCacheProvider.php5p}e[V4src/Psalm/Internal/Provider/ProjectCacheProvider.phpI5p}eIjӤ,src/Psalm/Internal/Provider/FileProvider.php5p}e:s^5src/Psalm/Internal/Provider/FileReferenceProvider.php5p}em,7src/Psalm/Internal/Provider/MethodExistenceProvider.phpp5p}epi\0src/Psalm/Internal/Provider/NodeDataProvider.php5p}eRͤ=src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php%5p}e%괤3src/Psalm/Internal/Provider/FileStorageProvider.php 5p}e Ӥ0src/Psalm/Internal/Provider/FakeFileProvider.phpY 5p}eY p4src/Psalm/Internal/Provider/MethodParamsProvider.php 5p}e y 8src/Psalm/Internal/Provider/MethodVisibilityProvider.php]5p}e]}>:src/Psalm/Internal/Provider/FileReferenceCacheProvider.php.5p}e. )src/Psalm/Internal/Provider/Providers.php 5p}e pV!3src/Psalm/Internal/Provider/ParserCacheProvider.php+5p}e+]Tsrc/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php5p}eS''2src/Psalm/Internal/Provider/StatementsProvider.phpI85p}eI8src/Psalm/Internal/Provider/DynamicFunctionStorageProvider.php 5p}e 5Ksrc/Psalm/Internal/Provider/ParamsProvider/ArrayMultisortParamsProvider.php#5p}e#wHsrc/Psalm/Internal/Provider/ParamsProvider/ArrayFilterParamsProvider.php5p}e7ڔ8src/Psalm/Internal/Provider/ClassLikeStorageProvider.php(5p}e(A9src/Psalm/Internal/Provider/PropertyExistenceProvider.php5p}e7[i9src/Psalm/Internal/Provider/FunctionExistenceProvider.phph5p}eh4src/Psalm/Internal/Provider/PropertyTypeProvider.php5p}e4PfLsrc/Psalm/Internal/Provider/ReturnTypeProvider/DirnameReturnTypeProvider.php1 5p}e1 Psrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php%5p}e%Qsrc/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php5p}eAbRsrc/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php5p}e<}Msrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php5p}e1Qsrc/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php 5p}e Hsrc/Psalm/Internal/Provider/ReturnTypeProvider/PowReturnTypeProvider.php 5p}e /sTsrc/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php5p}e}Msrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php 5p}e O{Isrc/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.phpB5p}eBwCSsrc/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php 5p}e DNsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php5p}e?-Ssrc/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php5p}e0Psrc/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php5p}e󨭤Osrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php'5p}e'|)Ksrc/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.phpu5p}eu STsrc/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php5p}e{Nsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php5p}e:Osrc/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php5p}eUPsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php$5p}e$ mtPsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php )5p}e )Msrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php2B5p}e2BܤLsrc/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.phpe 5p}ee 8Isrc/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php5p}ejSsrc/Psalm/Internal/Provider/ReturnTypeProvider/DateTimeModifyReturnTypeProvider.php[5p}e[4Qsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.phpe5p}ee3nPNsrc/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php5p}eU(Vsrc/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php 5p}e L֤Wsrc/Psalm/Internal/Provider/ReturnTypeProvider/MbInternalEncodingReturnTypeProvider.php: 5p}e: T Ksrc/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.phpX5p}eX=-Qsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php8 5p}e8 V)fRsrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillKeysReturnTypeProvider.php> 5p}e> B32Osrc/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php 5p}e Jsrc/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php5p}e gEsrc/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.phpg5p}egXsrc/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php5p}eMsrc/Psalm/Internal/Provider/ReturnTypeProvider/BasenameReturnTypeProvider.php5p}e[Msrc/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php(5p}e(Jsrc/Psalm/Internal/Provider/ReturnTypeProvider/RoundReturnTypeProvider.php= 5p}e= k;*z[src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php5p}eL Ksrc/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php9 5p}e9 `=Lsrc/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php845p}e84 Y܃Ksrc/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.phpH5p}eH栦Osrc/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php 5p}e r56src/Psalm/Internal/Provider/FunctionParamsProvider.phpc 5p}ec 588src/Psalm/Internal/Provider/MethodReturnTypeProvider.php 5p}e y:src/Psalm/Internal/Provider/PropertyVisibilityProvider.php5p}eܤCsrc/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php 5p}e x0src/Psalm/Internal/LanguageServer/PathMapper.php5p}e4src/Psalm/Internal/LanguageServer/ProtocolReader.php<5p}e<o1f9src/Psalm/Internal/LanguageServer/Server/TextDocument.php`D5p}e`D6src/Psalm/Internal/LanguageServer/Server/Workspace.php5p}ex9src/Psalm/Internal/LanguageServer/ClientConfiguration.php 5p}e Vz:src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php_5p}e_~:8src/Psalm/Internal/LanguageServer/PHPMarkdownContent.phpi5p}eioJ4src/Psalm/Internal/LanguageServer/LanguageServer.php5p}eZF1src/Psalm/Internal/LanguageServer/IdGenerator.phpU5p}eU>2src/Psalm/Internal/LanguageServer/EmitterTrait.php5p}em6src/Psalm/Internal/LanguageServer/EmitterInterface.php5p}ex04src/Psalm/Internal/LanguageServer/LanguageClient.php5p}eupGsrc/Psalm/Internal/LanguageServer/Provider/FileStorageCacheProvider.php5p}eׂCsrc/Psalm/Internal/LanguageServer/Provider/ProjectCacheProvider.php5p}e[ΤLsrc/Psalm/Internal/LanguageServer/Provider/ClassLikeStorageCacheProvider.phph5p}eh)ԁIsrc/Psalm/Internal/LanguageServer/Provider/FileReferenceCacheProvider.php5p}e@ߤBsrc/Psalm/Internal/LanguageServer/Provider/ParserCacheProvider.phpq 5p}eq fd4src/Psalm/Internal/LanguageServer/ProtocolWriter.php5p}eդ-src/Psalm/Internal/LanguageServer/Message.phpJ5p}eJ\0U.src/Psalm/Internal/LanguageServer/Progress.php5p}e͹Τ9src/Psalm/Internal/LanguageServer/Client/TextDocument.php5p}eϤGsrc/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.phpc5p}ecoDsrc/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php5p}e >src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php 5p}e vo6src/Psalm/Internal/LanguageServer/Client/Workspace.php5p}evm./src/Psalm/Internal/LanguageServer/Reference.php5p}el~3src/Psalm/Internal/LanguageServer/ClientHandler.php 5p}e Oܤ:src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php5p}eU#src/Psalm/Internal/VersionUtils.php 5p}e # *src/Psalm/Internal/ReferenceConstraint.php5p}eisrc/Psalm/Internal/Algebra.php`5p}e`-<src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php 5p}e Asrc/Psalm/Internal/ExecutionEnvironment/SystemCommandExecutor.php5p}eƤۤ>src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php)5p}e)5M&src/Psalm/Internal/EventDispatcher.phpD:5p}eD:I#src/Psalm/Internal/ErrorHandler.phpA 5p}eA ßNu$src/Psalm/FileBasedPluginAdapter.php5p}ex[/vendor/fidry/cpu-core-counter/src/Diagnoser.php5p}eŤAvendor/fidry/cpu-core-counter/src/Finder/SkipOnOSFamilyFinder.php5p}eV$,r>vendor/fidry/cpu-core-counter/src/Finder/_NProcessorFinder.php5p}euAvendor/fidry/cpu-core-counter/src/Finder/OnlyOnOSFamilyFinder.php5p}ec>vendor/fidry/cpu-core-counter/src/Finder/NullCpuCoreFinder.php5p}e7T j;vendor/fidry/cpu-core-counter/src/Finder/FinderRegistry.phpi5p}eiG:vendor/fidry/cpu-core-counter/src/Finder/CpuInfoFinder.php5p}em?vendor/fidry/cpu-core-counter/src/Finder/DummyCpuCoreFinder.phpE5p}eEȤ?vendor/fidry/cpu-core-counter/src/Finder/WmicPhysicalFinder.php5p}e[.:vendor/fidry/cpu-core-counter/src/Finder/CpuCoreFinder.php5p}e~?vendor/fidry/cpu-core-counter/src/Finder/LscpuLogicalFinder.phpQ5p}eQ^eդ8vendor/fidry/cpu-core-counter/src/Finder/NProcFinder.php.5p}e._=vendor/fidry/cpu-core-counter/src/Finder/HwPhysicalFinder.php~5p}e~n<vendor/fidry/cpu-core-counter/src/Finder/HwLogicalFinder.phpz5p}ez->vendor/fidry/cpu-core-counter/src/Finder/WmicLogicalFinder.php5p}e@vendor/fidry/cpu-core-counter/src/Finder/ProcOpenBasedFinder.php* 5p}e* 9h=vendor/fidry/cpu-core-counter/src/Finder/NProcessorFinder.php5p}eI>@vendor/fidry/cpu-core-counter/src/Finder/LscpuPhysicalFinder.php|5p}e|v>vendor/fidry/cpu-core-counter/src/Executor/ProcessExecutor.php5p}e?vendor/fidry/cpu-core-counter/src/Executor/ProcOpenExecutor.php5p}eu4vendor/fidry/cpu-core-counter/src/CpuCoreCounter.phpw5p}ewS =vendor/fidry/cpu-core-counter/src/NumberOfCpuCoreNotFound.php(5p}e(8[d.vendor/fidry/cpu-core-counter/bin/diagnose.phpx5p}exSUΤ-vendor/fidry/cpu-core-counter/bin/execute.phpw5p}ewؤvendor/nikic/php-parser/LICENSE5p}e*%vendor/nikic/php-parser/bin/php-parsez5p}ezS#Ѥ@vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php5p}eoĤMvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php5p}e(ݤSvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php5p}e>Ovendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php5p}eN|Mvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php5p}e{yKMvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php5p}eSvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php5p}e<GKvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php5p}e?5]vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.phpt5p}etk6Svendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.phpj5p}ejLWvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php 5p}e $dڤPvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php5p}e"Wvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php)5p}e)zOvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php5p}eTg[vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php5p}e{9vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.phpN$5p}eN$Z4vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php 5p}e `DN/vendor/nikic/php-parser/lib/PhpParser/Error.php5p}e 05vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php5p}ezy5vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.php5p}e7/vendor/nikic/php-parser/lib/PhpParser/Lexer.phpZ5p}eZIvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php85p}e8-Dvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php5p}e9Kvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php5p}e h^Mvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php5p}e_0Bvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.phpy'5p}ey')PDvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php?5p}e??PFvendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.phpn5p}en&Gc.vendor/nikic/php-parser/lib/PhpParser/Node.php5p}e,Ԥ9vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.php 5p}e  3vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php5p}elͅ3vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php5p}e(8vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php5p}e,&n7vendor/nikic/php-parser/lib/PhpParser/Node/MatchArm.php5p}ea@vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php5p}e{2=vendor/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php5p}eBvendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php5p}e~F ?vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php5p}e>28vendor/nikic/php-parser/lib/PhpParser/Node/Attribute.phpu5p}euk5vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.phpz5p}ez5Gvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.phpr5p}er Evendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.phpn5p}enppGvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.phpt5p}etȿvDvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.phpk5p}ekEvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.phpn5p}enLKvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php~5p}e~Jvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php{5p}e{ZbޤGvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.phpr5p}erm6@vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php5p}e$t=vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php5p}eV\>vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php5p}em4=vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php 5p}e #Hvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php5p}eO =vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.php5p}eG:vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.phpq5p}eqAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php 5p}e C%=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php5p}e59vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php 5p}e \[=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php5p}e#:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php5p}eTY-Avendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php5p}e>:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php5p}eˤ=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.phpf5p}efp=ߤAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.phpk5p}ekKNFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php5p}e ФAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php5p}e4;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php 5p}e Q`6:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.phpz5p}ez;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php 5p}e :vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.phpV5p}eV?@8vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php_5p}e_[y9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php#5p}e#G<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php5p}e"'Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php5p}e\-Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php5p}e?ߜEvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php5p}eNk]Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php5p}eDgs@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php5p}eN@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php5p}eBvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php5p}eGeICvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php5p}eA@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php5p}eIcAAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php5p}eFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php5p}eGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php5p}e[Xo@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php5p}eR7:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php5p}e#wCvendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.phpD5p}eD9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php5p}ef'|9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php5p}eJx'8vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php5p}e-P?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php5p}e S?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php5p}eu@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php5p}e.uH?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php5p}e%mu=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php5p}e78@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php5p}e##>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php5p}eu<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php5p}eG:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php5p}eGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.phpb5p}eb[;b;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php5p}ehD<:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php5p}e0<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php`5p}e`>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php5p}ej=<=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php5p}e2u>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php5p}e* >vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php5p}e:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php5p}efUG>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php5p}e^<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php5p}e=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php5p}ed Ivendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php5p}e#>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php5p}e|:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php5p}eƤ?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php5p}ex8Kvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.phpw5p}ewpʤKvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.phpw5p}ew]JFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.phpn5p}en19Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.phpl5p}el@BGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.phpn5p}en%lcEvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.phpk5p}ek}"Evendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.phpk5p}ekKNBvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.phpe5p}eebmAIvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.phpt5p}etElGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.phpn5p}enmn@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.phpa5p}ea @vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php`5p}e`9`1Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.phpp5p}epb Bvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.phpd5p}edW*&>Cvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.phpf5p}efdP*@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php`5p}e`jAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.phpb5p}ebVFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpm5p}emXFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.phpn5p}en[(Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.phpp5p}ep?Dvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.phph5p}ehYhbGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.phpo5p}eo`Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.phpm5p}em2#,~Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpo5p}eozLC@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php`5p}e`,aUFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.phpm5p}em 4Dvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.phph5p}eh >vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php5p}eM- ":vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php5p}eݞ^>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php5p}e:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php55p}e5s6<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.phpb5p}ebꦦ<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php5p}eg;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php5p}e aV';vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php5p}e62vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php]5p}e]z3<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php5p}eQ1:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php!5p}e!r>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php 5p}e ʛXBvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php5p}eQY};vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php5p}e}ڤ7vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php^5p}e^aŋ3:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.phpc5p}ec5r8vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php5p}e-X;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.phpS5p}eSMx<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php 5p}e x~;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.phpg5p}eg-`7vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.phpX5p}eXt:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php5p}e[)]Qvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpx5p}ex$Lvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php_5p}e_a9:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php5p}e%J9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php5p}eQ]c<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.phpB5p}eBlI9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php5p}eN>=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php 5p}e q؃>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php5p}e4;@vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php%5p}e%Hh<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php75p}e7H|R>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php5p}eH髽9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php[5p}e[Ф<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php5p}ee=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php5p}e*>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php5p}e&N :vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php5p}e ƏFvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php85p}e8 >vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php5p}eҜ́;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php5p}eV:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Throw_.php5p}e/n/=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.phpY 5p}eY MѤ=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php5p}e=;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php5p}egDDvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php5p}e=$n:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php5p}e:ê?9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php5p}eB <vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php5p}en!9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php=5p}e=^Jڤ:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php5p}eRz9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php5p}e<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php5p}e%9D7vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php`5p}e`WmS<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php5p}eD٤:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php5p}e8vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php\5p}e\]Fb?vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php*5p}e* ;vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php5p}e45vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php5p}et<vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php5p}eLFBvendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php5p}e 74vendor/nikic/php-parser/lib/PhpParser/Node/Param.php5p}eSؐ3vendor/nikic/php-parser/lib/PhpParser/Node/Name.php>5p}e>rzR@vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.phpХ5p}eХG͑8vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php+5p}e+W!:vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php5p}e^88vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php!5p}e!7<vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.phpB5p}eBv6vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php*5p}e* L:vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php5p}e4=vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php 5p}e !8vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php5p}eOs̤<vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php" 5p}e" gN7vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php 5p}e y<vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php5p}ek2Dvendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php5p}e|;vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php5p}eW/8vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php5p}e̤>vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php5p}eF7vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php5p}eƁ䍤:vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php5p}en~5vendor/nikic/php-parser/lib/PhpParser/NameContext.php+&5p}e+&"[5vendor/nikic/php-parser/lib/PhpParser/Parser/Php5.phpa,5p}ea,7vendor/nikic/php-parser/lib/PhpParser/Parser/Tokens.php55p}e5AǤ5vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.phpcU5p}ecUV9vendor/nikic/php-parser/lib/PhpParser/Parser/Multiple.php5p}e<vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php%5p}e%Zo(?vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php5p}e;?vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php5p}eR[Avendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php5p}eŤ=vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php5p}e?j4vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php5p}ew80vendor/nikic/php-parser/lib/PhpParser/Parser.php5p}eDؤ6vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.phpi5p}ei!}j8vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php%5p}e%9&D5vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php/ 5p}e/ 7~פ7vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.phpl'5p}el'n-v8vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php5p}eԐ1vendor/nikic/php-parser/lib/PhpParser/Builder.php5p}eC!+7vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php 5p}e ;1vendor/nikic/php-parser/lib/PhpParser/Comment.php5p}eE6vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php>5p}e>(ӛ>vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php $5p}e $1Lvendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php5p}eGQ;vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.phpF5p}eF1|D9vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php<5p}e<RTvendor/autoload.php5p}e\yvendor/psr/log/LICENSE=5p}e=pO#vendor/psr/log/Psr/Log/LogLevel.phpP5p}ePz3vendor/psr/log/Psr/Log/InvalidArgumentException.phpw5p}ewİ&vendor/psr/log/Psr/Log/LoggerTrait.phpf 5p}ef ho/%vendor/psr/log/Psr/Log/NullLogger.php5p}e8y)vendor/psr/log/Psr/Log/AbstractLogger.php0 5p}e0 =$p*vendor/psr/log/Psr/Log/LoggerInterface.php9 5p}e9  /vendor/psr/log/Psr/Log/LoggerAwareInterface.php@5p}e@/ݪ+vendor/psr/log/Psr/Log/LoggerAwareTrait.php5p}e0[vendor/psr/container/LICENSEy5p}eyOp/vendor/psr/container/src/ContainerInterface.php&5p}e&7G2X8vendor/psr/container/src/ContainerExceptionInterface.php5p}eS 7vendor/psr/container/src/NotFoundExceptionInterface.php5p}e)ZĤFvendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php5p}e9@vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.phpW[5p}eW[!nBvendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php5p}e8@vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpU5p}eU95(vendor/symfony/polyfill-mbstring/LICENSE,5p}e,H.vendor/symfony/polyfill-mbstring/bootstrap.php[ 5p}e[ `z-vendor/symfony/polyfill-mbstring/Mbstring.php|5p}e|T0vendor/symfony/polyfill-mbstring/bootstrap80.php&5p}e&XA6'vendor/symfony/polyfill-ctype/Ctype.php5p}e}פ%vendor/symfony/polyfill-ctype/LICENSE,5p}e,+vendor/symfony/polyfill-ctype/bootstrap.php45p}e4UV`-vendor/symfony/polyfill-ctype/bootstrap80.phpp5p}ep?vendor/symfony/service-contracts/ServiceSubscriberInterface.php5p}e=;vendor/symfony/service-contracts/ServiceSubscriberTrait.php5p}e/=vendor/symfony/service-contracts/ServiceProviderInterface.php5p}e.@@vendor/symfony/service-contracts/Attribute/SubscribedService.phpB5p}eBī7vendor/symfony/service-contracts/Attribute/Required.php5p}eAN"8vendor/symfony/service-contracts/ServiceLocatorTrait.php5p}e"Ĥ(vendor/symfony/service-contracts/LICENSE)5p}e)5古3vendor/symfony/service-contracts/ResetInterface.php5p}e`@2vendor/symfony/polyfill-intl-grapheme/Grapheme.php&5p}e&V-vendor/symfony/polyfill-intl-grapheme/LICENSE,5p}e,H3vendor/symfony/polyfill-intl-grapheme/bootstrap.php 5p}e 5vendor/symfony/polyfill-intl-grapheme/bootstrap80.phpI 5p}eI 5Q1vendor/symfony/deprecation-contracts/function.php45p}e4"O,vendor/symfony/deprecation-contracts/LICENSE)5p}e)23vendor/symfony/console/Event/ConsoleSignalEvent.php5p}e1ﯤ6vendor/symfony/console/Event/ConsoleTerminateEvent.phpz5p}ez8]2vendor/symfony/console/Event/ConsoleErrorEvent.php15p}e1(&RJ4vendor/symfony/console/Event/ConsoleCommandEvent.phpA5p}eAj-vendor/symfony/console/Event/ConsoleEvent.php5p}em+,vendor/symfony/console/Question/Question.php5p}eͤ2vendor/symfony/console/Question/ChoiceQuestion.php5p}er 8vendor/symfony/console/Question/ConfirmationQuestion.php-5p}e-ʤ!vendor/symfony/console/Cursor.phpG5p}eG߿6=vendor/symfony/console/Exception/CommandNotFoundException.php5p}egm!z=vendor/symfony/console/Exception/InvalidArgumentException.php5p}eVD?vendor/symfony/console/Exception/NamespaceNotFoundException.php5p}e@;vendor/symfony/console/Exception/InvalidOptionException.php5p}eyf:vendor/symfony/console/Exception/MissingInputException.php5p}e$֤7vendor/symfony/console/Exception/ExceptionInterface.php5p}e]es3vendor/symfony/console/Exception/LogicException.php5p}euYԤ5vendor/symfony/console/Exception/RuntimeException.php5p}e",2vendor/symfony/console/CI/GithubActionReporter.php 5p}e !2j.vendor/symfony/console/Command/HelpCommand.phpB 5p}eB (N.vendor/symfony/console/Command/LazyCommand.phpc5p}ecXŝ0vendor/symfony/console/Command/LockableTrait.php;5p}e;=aߤ=vendor/symfony/console/Command/SignalableCommandInterface.php5p}eDV.vendor/symfony/console/Command/ListCommand.php9 5p}e9 +*vendor/symfony/console/Command/Command.phpP5p}ePQj2vendor/symfony/console/Command/CompleteCommand.php!5p}e!3^8vendor/symfony/console/Command/DumpCompletionCommand.php5p}eN(vendor/symfony/console/ConsoleEvents.php5p}e @vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php5p}e!v-vendor/symfony/console/Tester/TesterTrait.php5p}e[x/vendor/symfony/console/Tester/CommandTester.phpV 5p}eV ާ3vendor/symfony/console/Tester/ApplicationTester.php 5p}e ä9vendor/symfony/console/Tester/CommandCompletionTester.php\5p}e\s66vendor/symfony/console/EventListener/ErrorListener.phpy 5p}ey . .vendor/symfony/console/Attribute/AsCommand.phpR5p}eRsvG/vendor/symfony/console/Helper/ProcessHelper.php5p}edhM-vendor/symfony/console/Helper/ProgressBar.php6H5p}e6Hm'0vendor/symfony/console/Helper/QuestionHelper.phpN5p}eNi+vendor/symfony/console/Helper/HelperSet.phpO 5p}eO ґ0vendor/symfony/console/Helper/TableCellStyle.phpl5p}el(vendor/symfony/console/Helper/Helper.php5p}eҿ۪'vendor/symfony/console/Helper/Table.phpr5p}er(vendor/symfony/console/Helper/Dumper.phpj5p}ej6vendor/symfony/console/Helper/DebugFormatterHelper.php` 5p}e` e1vendor/symfony/console/Helper/FormatterHelper.php 5p}e %ঁ3vendor/symfony/console/Helper/ProgressIndicator.php5p}eGy1vendor/symfony/console/Helper/HelperInterface.phpa5p}ea}7vendor/symfony/console/Helper/SymfonyQuestionHelper.phpc 5p}ec `=KM,vendor/symfony/console/Helper/TableStyle.php05p}e0c~0vendor/symfony/console/Helper/TableSeparator.php)5p}e)s&2vendor/symfony/console/Helper/InputAwareHelper.php-5p}e-Gu2vendor/symfony/console/Helper/DescriptorHelper.php 5p}e C3+vendor/symfony/console/Helper/TableRows.phpZ5p}eZ$8+vendor/symfony/console/Helper/TableCell.php5p}ec 0vendor/symfony/console/Resources/completion.bash 5p}e z(4vendor/symfony/console/Resources/bin/hiddeninput.exe$5p}e$v;vendor/symfony/console/Completion/CompletionSuggestions.phpv5p}ev&0vendor/symfony/console/Completion/Suggestion.php5p}er$Τ5vendor/symfony/console/Completion/CompletionInput.phpo 5p}eo u!xFvendor/symfony/console/Completion/Output/CompletionOutputInterface.php5p}eAvendor/symfony/console/Completion/Output/BashCompletionOutput.php!5p}e!G #vendor/symfony/console/Terminal.phpq5p}eq^=ؤ3vendor/symfony/console/SingleCommandApplication.phpG5p}eGg,uvendor/symfony/console/LICENSE,5p}e,U&vendor/symfony/console/Application.php5p}eGפ8vendor/symfony/console/SignalRegistry/SignalRegistry.phpH5p}eH^?vendor/symfony/console/CommandLoader/ContainerCommandLoader.php"5p}e"mF=vendor/symfony/console/CommandLoader/FactoryCommandLoader.phpm5p}emV?vendor/symfony/console/CommandLoader/CommandLoaderInterface.php5p}e;e,vendor/symfony/console/Input/InputOption.php55p}e5g-y.vendor/symfony/console/Input/InputArgument.php 5p}e [Ǥ/vendor/symfony/console/Input/InputInterface.php5p}e՝9vendor/symfony/console/Input/StreamableInputInterface.php~5p}e~/ڤ*vendor/symfony/console/Input/ArgvInput.php05p}e0,vendor/symfony/console/Input/StringInput.php 5p}e z+vendor/symfony/console/Input/ArrayInput.php5p}e'"&vendor/symfony/console/Input/Input.phpH5p}eH50vendor/symfony/console/Input/InputDefinition.php`.5p}e`.3(a4vendor/symfony/console/Input/InputAwareInterface.phpP5p}eP[m0vendor/symfony/console/Descriptor/Descriptor.php: 5p}e: 08vendor/symfony/console/Descriptor/MarkdownDescriptor.php5p}e<vendor/symfony/console/Descriptor/ApplicationDescription.php45p}e4JQD4vendor/symfony/console/Descriptor/JsonDescriptor.php5p}eo\9vendor/symfony/console/Descriptor/DescriptorInterface.phpY5p}eYn3vendor/symfony/console/Descriptor/XmlDescriptor.phpE'5p}eE'4vendor/symfony/console/Descriptor/TextDescriptor.php.25p}e.2 YʣDvendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php5p}e_N&1vendor/symfony/console/Output/OutputInterface.php| 5p}e| _aˢ0vendor/symfony/console/Output/BufferedOutput.phpg5p}eg#w/vendor/symfony/console/Output/ConsoleOutput.php/5p}e/6vendor/symfony/console/Output/ConsoleSectionOutput.php5p}e8vendor/symfony/console/Output/ConsoleOutputInterface.php55p}e5=6.vendor/symfony/console/Output/StreamOutput.php 5p}e |O(vendor/symfony/console/Output/Output.phpr5p}erMK/75vendor/symfony/console/Output/TrimmedBufferOutput.phpz5p}ez ,vendor/symfony/console/Output/NullOutput.phpD 5p}eD Я_ vendor/symfony/console/Color.phpb5p}eb/vendor/symfony/console/Style/StyleInterface.phpS 5p}eS r{C ,vendor/symfony/console/Style/OutputStyle.php` 5p}e` ʏ-vendor/symfony/console/Style/SymfonyStyle.php&:5p}e&:-rFvendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php5p}eF%=vendor/symfony/console/Formatter/NullOutputFormatterStyle.php5p}e[8vendor/symfony/console/Formatter/NullOutputFormatter.phps5p}esxBvendor/symfony/console/Formatter/OutputFormatterStyleInterface.phpk5p}ekj4vendor/symfony/console/Formatter/OutputFormatter.php 5p}e IPs9vendor/symfony/console/Formatter/OutputFormatterStyle.php 5p}e 5>vendor/symfony/console/Formatter/OutputFormatterStyleStack.php5 5p}e5 3=vendor/symfony/console/Formatter/OutputFormatterInterface.phpH5p}eHM)/vendor/symfony/console/Logger/ConsoleLogger.php5p}eUGƤTvendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php5p}eoFLvendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php.5p}e.ćXvendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php5p}eMwWRvendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php=5p}e=\NFvendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php5p}es6vendor/symfony/polyfill-intl-normalizer/Normalizer.phpO$5p}eO$z/vendor/symfony/polyfill-intl-normalizer/LICENSE,5p}e,H5vendor/symfony/polyfill-intl-normalizer/bootstrap.php05p}e0c7vendor/symfony/polyfill-intl-normalizer/bootstrap80.php+5p}e+8?vendor/symfony/polyfill-php73/Resources/stubs/JsonException.phph5p}ehC%vendor/symfony/polyfill-php73/LICENSE,5p}e,+vendor/symfony/polyfill-php73/bootstrap.php5p}eِD3'vendor/symfony/polyfill-php73/Php73.php5p}e(vendor/symfony/filesystem/Filesystem.php/u5p}e/uq@vendor/symfony/filesystem/Exception/InvalidArgumentException.php5p}eI3vendor/symfony/filesystem/Exception/IOException.php5p}e:vendor/symfony/filesystem/Exception/ExceptionInterface.php5p}eu2=vendor/symfony/filesystem/Exception/FileNotFoundException.php5p}eYPx<vendor/symfony/filesystem/Exception/IOExceptionInterface.php5p}e?̤8vendor/symfony/filesystem/Exception/RuntimeException.php5p}eQ牤"vendor/symfony/filesystem/Path.phpf5p}ef 1(!vendor/symfony/filesystem/LICENSE,5p}e,U2vendor/symfony/string/Slugger/SluggerInterface.php5p}eu^.vendor/symfony/string/Slugger/AsciiSlugger.phpA5p}eA[ (vendor/symfony/string/AbstractString.phpO5p}eO$By&<vendor/symfony/string/Exception/InvalidArgumentException.php5p}eyn^6vendor/symfony/string/Exception/ExceptionInterface.phpg5p}egY4 n4vendor/symfony/string/Exception/RuntimeException.php5p}e44h)vendor/symfony/string/CodePointString.php5p}e.h<vendor/symfony/string/Resources/data/wcswidth_table_wide.php#5p}e#{Ƥ<vendor/symfony/string/Resources/data/wcswidth_table_zero.php5p}e o-vendor/symfony/string/Resources/functions.phpt5p}et^Evendor/symfony/string/LICENSE,5p}e,զ_Ϥ'vendor/symfony/string/UnicodeString.php25p}e2\ פ$vendor/symfony/string/ByteString.php<5p}e<ä$vendor/symfony/string/LazyString.php|5p}e|QU6vendor/symfony/string/Inflector/InflectorInterface.phpZ5p}eZ7Ȥ3vendor/symfony/string/Inflector/FrenchInflector.php@5p}e@m)4vendor/symfony/string/Inflector/EnglishInflector.php?5p}e?=ۤ/vendor/symfony/string/AbstractUnicodeString.phpri5p}eri*vendor/symfony/polyfill-php80/PhpToken.php5p}e]f<vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php5p}et]\ڤ:vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.phpw5p}ew=7T8<vendor/symfony/polyfill-php80/Resources/stubs/ValueError.phpa5p}eau8&Evendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.phpj5p}ejT;vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php5p}eMK<%vendor/symfony/polyfill-php80/LICENSE,5p}e, K'vendor/symfony/polyfill-php80/Php80.php 5p}e cH+vendor/symfony/polyfill-php80/bootstrap.php5p}e,+.vendor/phpdocumentor/reflection-common/LICENSE95p}e9*2Ȑ6vendor/phpdocumentor/reflection-common/src/Element.php5p}eg3vendor/phpdocumentor/reflection-common/src/File.php5p}e}6=vendor/phpdocumentor/reflection-common/src/ProjectFactory.phpn5p}eng4vendor/phpdocumentor/reflection-common/src/Fqsen.php5p}eT`~7vendor/phpdocumentor/reflection-common/src/Location.php5p}e X6vendor/phpdocumentor/reflection-common/src/Project.php5p}ep 0vendor/phpdocumentor/reflection-docblock/LICENSE85p}e8ʤ@vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php0%5p}e0%@0rDvendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php5p}ez|Lvendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php5p}evDvendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.phpI5p}eI0oLvendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.phpE25p}eE2"OEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php5p}eKe=vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tag.php5p}e-դFvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php 5p}e .Evendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php 5p}e XXLvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php5p}e;Nvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.phpJ5p}eJi$ȤRvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.php5p}eCvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php5p}e Gvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.phpC5p}eCq%Fvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php 5p}e 649Hvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php<5p}e<ʤCvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php 5p}e Evendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php" 5p}e" ~RIvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.phpJ5p}eJ9'Jvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php5p}eY%iEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php 5p}e Bvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php 5p}e Cvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php:5p}e:!Ivendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php6 5p}e6 1A/Fvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.phpx5p}exkU=Dvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php 5p}e 2Svendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php-5p}e-B;Evendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php 5p}e ~ qKvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.phpQ5p}eQ_ԚФDvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.phpx5p}exRLvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.phpO5p}eO)Fvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php 5p}e =GFvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php5p}ecFEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.phpy5p}eytĤWvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php5p}e@caL]vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php5p}erGvendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.phpJ5p}eJX҇NHvendor/phpdocumentor/reflection-docblock/src/Exception/PcreException.php5p}e*Ivendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php5p}e6vendor/phpdocumentor/reflection-docblock/src/Utils.php 5p}e '9vendor/phpdocumentor/reflection-docblock/src/DocBlock.php5p}eI*vendor/phpdocumentor/type-resolver/LICENSE85p}e8ʤ6vendor/phpdocumentor/type-resolver/src/Types/Void_.php%5p}e%rƤ8vendor/phpdocumentor/type-resolver/src/Types/Integer.php5p}e׋Bvendor/phpdocumentor/type-resolver/src/Types/CallableParameter.phpo5p}eo:;vendor/phpdocumentor/type-resolver/src/Types/Collection.php5p}e̤:vendor/phpdocumentor/type-resolver/src/Types/Resource_.php5p}e`9vendor/phpdocumentor/type-resolver/src/Types/ArrayKey.php5p}eD7vendor/phpdocumentor/type-resolver/src/Types/Array_.php5p}e0S9vendor/phpdocumentor/type-resolver/src/Types/Compound.php%5p}e%}@:vendor/phpdocumentor/type-resolver/src/Types/Iterable_.phpN5p}eN%"C46vendor/phpdocumentor/type-resolver/src/Types/Self_.php5p}e݂9vendor/phpdocumentor/type-resolver/src/Types/Nullable.phpu5p}euDä8vendor/phpdocumentor/type-resolver/src/Types/Object_.php5p}e8267vendor/phpdocumentor/type-resolver/src/Types/Never_.php5p}eK'48vendor/phpdocumentor/type-resolver/src/Types/String_.php5p}eQ?<vendor/phpdocumentor/type-resolver/src/Types/ClassString.phpk5p}ekӥ=vendor/phpdocumentor/type-resolver/src/Types/Intersection.php45p}e4L_;vendor/phpdocumentor/type-resolver/src/Types/Expression.phpF5p}eF\ 7vendor/phpdocumentor/type-resolver/src/Types/Float_.php5p}eeW?vendor/phpdocumentor/type-resolver/src/Types/AggregatedType.php 5p}e Ť7vendor/phpdocumentor/type-resolver/src/Types/Scalar.php5p}eK&6:vendor/phpdocumentor/type-resolver/src/Types/Callable_.php5p}e5vendor/phpdocumentor/type-resolver/src/Types/This.phpw5p}ew'8vendor/phpdocumentor/type-resolver/src/Types/Context.php 5p}e ]78vendor/phpdocumentor/type-resolver/src/Types/Static_.php#5p}e# =vendor/phpdocumentor/type-resolver/src/Types/AbstractList.php5p}eK?vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php,75p}e,7Mdɤ8vendor/phpdocumentor/type-resolver/src/Types/Parent_.php5p}e 2@vendor/phpdocumentor/type-resolver/src/Types/InterfaceString.php5p}e7vendor/phpdocumentor/type-resolver/src/Types/Mixed_.php5p}eV 8vendor/phpdocumentor/type-resolver/src/Types/Boolean.php5p}e=66vendor/phpdocumentor/type-resolver/src/Types/Null_.php5p}eb/vendor/phpdocumentor/type-resolver/src/Type.php5p}e+8vendor/phpdocumentor/type-resolver/src/FqsenResolver.php 5p}e iAvendor/phpdocumentor/type-resolver/src/PseudoTypes/FloatValue.php5p}eyMNBvendor/phpdocumentor/type-resolver/src/PseudoTypes/StringValue.php5p}e'睰=vendor/phpdocumentor/type-resolver/src/PseudoTypes/False_.php5p}ee=t4Nvendor/phpdocumentor/type-resolver/src/PseudoTypes/NonEmptyLowercaseString.php5p}e5 HAvendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShape.phpa5p}eâKCvendor/phpdocumentor/type-resolver/src/PseudoTypes/NonEmptyList.php5p}e?6Hvendor/phpdocumentor/type-resolver/src/PseudoTypes/HtmlEscapedString.php5p}eR Cvendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerValue.php5p}eoƤFvendor/phpdocumentor/type-resolver/src/PseudoTypes/ConstExpression.php5p}e?vendor/phpdocumentor/type-resolver/src/PseudoTypes/Numeric_.php`5p}e`fդDvendor/phpdocumentor/type-resolver/src/PseudoTypes/LiteralString.php5p}e"˴HDvendor/phpdocumentor/type-resolver/src/PseudoTypes/NumericString.php5p}egRBvendor/phpdocumentor/type-resolver/src/PseudoTypes/TraitString.php5p}e|Cvendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerRange.phpA5p}eAäFvendor/phpdocumentor/type-resolver/src/PseudoTypes/NegativeInteger.php5p}e{KEvendor/phpdocumentor/type-resolver/src/PseudoTypes/CallableString.php5p}e,c<vendor/phpdocumentor/type-resolver/src/PseudoTypes/List_.php5p}e7B?Evendor/phpdocumentor/type-resolver/src/PseudoTypes/NonEmptyString.php5p}e4|Evendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShapeItem.php5p}eZFvFvendor/phpdocumentor/type-resolver/src/PseudoTypes/PositiveInteger.php5p}e{vP<vendor/phpdocumentor/type-resolver/src/PseudoTypes/True_.php5p}eFvendor/phpdocumentor/type-resolver/src/PseudoTypes/LowercaseString.php5p}e[S5vendor/phpdocumentor/type-resolver/src/PseudoType.php5p}eb4p7vendor/phpdocumentor/type-resolver/src/TypeResolver.phphW5p}ehW#bp$vendor/doctrine/deprecations/LICENSE)5p}e)"0Fvendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.phpl$5p}el$$Uvendor/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php 5p}e Vx,9vendor/felixfbecker/language-server-protocol/package.jsonN5p}eN2W>4vendor/felixfbecker/language-server-protocol/LICENSE5p}e>9>vendor/felixfbecker/language-server-protocol/package-lock.jsonN5p}eNj\Evendor/felixfbecker/language-server-protocol/src/ReferenceContext.php5p}eZIFvendor/felixfbecker/language-server-protocol/src/CodeActionContext.php,5p}e,Y?vendor/felixfbecker/language-server-protocol/src/LogMessage.php65p}e68_vendor/felixfbecker/language-server-protocol/src/DocumentSymbolClientCapabilitiesSymbolKind.php5p}e2Qvendor/felixfbecker/language-server-protocol/src/CodeActionClientCapabilities.php 5p}e Kvendor/felixfbecker/language-server-protocol/src/TextDocumentIdentifier.php5p}ePwvendor/felixfbecker/language-server-protocol/src/CodeActionClientCapabilitiesCodeActionLiteralSupportcodeActionKind.php5p}eF`Bvendor/felixfbecker/language-server-protocol/src/MarkupContent.php5p}ekeMvendor/felixfbecker/language-server-protocol/src/RenameClientCapabilities.php5p}em%@vendor/felixfbecker/language-server-protocol/src/SaveOptions.php'5p}e'>)Jvendor/felixfbecker/language-server-protocol/src/CodeActionTriggerKind.php5p}eid|Lvendor/felixfbecker/language-server-protocol/src/TextDocumentSyncOptions.php5p}ewˌGvendor/felixfbecker/language-server-protocol/src/CodeActionDisabled.php5p}eGmvendor/felixfbecker/language-server-protocol/src/CompletionClientCapabilitiesCompletionItemResolveSupport.php5p}eZ]vendor/felixfbecker/language-server-protocol/src/SemanticTokensClientCapabilitiesRequests.php5p}eodOvendor/felixfbecker/language-server-protocol/src/CompletionItemLabelDetails.php~5p}e~`Jvendor/felixfbecker/language-server-protocol/src/DocumentHighlightKind.php5p}e/ivendor/felixfbecker/language-server-protocol/src/CompletionClientCapabilitiesCompletionItemTagSupport.php5p}eܢIvendor/felixfbecker/language-server-protocol/src/ReferenceInformation.php`5p}e`Aޤcvendor/felixfbecker/language-server-protocol/src/PublishDiagnosticsClientCapabilitiesTagSupport.php5p}e2aUvendor/felixfbecker/language-server-protocol/src/ImplementationClientCapabilities.php5p}eW0?vendor/felixfbecker/language-server-protocol/src/Diagnostic.php 5p}e )R{Fvendor/felixfbecker/language-server-protocol/src/CompletionOptions.php5p}ezONNvendor/felixfbecker/language-server-protocol/src/MonikerClientCapabilities.phpA5p}eAwFvendor/felixfbecker/language-server-protocol/src/DocumentHighlight.php5p}e5@vendor/felixfbecker/language-server-protocol/src/TokenFormat.php5p}e1>@vendor/felixfbecker/language-server-protocol/src/MessageType.phpk5p}ek:AAvendor/felixfbecker/language-server-protocol/src/MarkedString.php5p}e פEvendor/felixfbecker/language-server-protocol/src/ChangeAnnotation.php5p}eVvendor/felixfbecker/language-server-protocol/src/WorkspaceSymbolClientCapabilities.php5p}evendor/felixfbecker/language-server-protocol/src/FileEvent.php5p}eiXvendor/felixfbecker/language-server-protocol/src/DocumentHighlightClientCapabilities.php5p}eZ%jXvendor/felixfbecker/language-server-protocol/src/CodeLensWorkspaceClientCapabilities.php5p}eV񂵤Gvendor/felixfbecker/language-server-protocol/src/ServerCapabilities.php 5p}e 1Evendor/felixfbecker/language-server-protocol/src/SymbolDescriptor.php5p}e$8Tvendor/felixfbecker/language-server-protocol/src/DocumentOnTypeFormattingOptions.php5p}eʸ_vendor/felixfbecker/language-server-protocol/src/CodeActionClientCapabilitiesResolveSupport.php5p}eE֤Ovendor/felixfbecker/language-server-protocol/src/InitializeResultServerInfo.php5p}e Yvendor/felixfbecker/language-server-protocol/src/DocumentFormattingClientCapabilities.php5p}eչBvendor/felixfbecker/language-server-protocol/src/DiagnosticTag.php@5p}e@sJvendor/felixfbecker/language-server-protocol/src/ExecuteCommandOptions.phpg5p}eg&`Yvendor/felixfbecker/language-server-protocol/src/ShowMessageRequestClientCapabilities.php5p}e2~{Cvendor/felixfbecker/language-server-protocol/src/CompletionList.php5p}eT2S^vendor/felixfbecker/language-server-protocol/src/DocumentRangeFormattingClientCapabilities.php5p}eM!SNvendor/felixfbecker/language-server-protocol/src/SymbolLocationInformation.phpz5p}ezV̦_vendor/felixfbecker/language-server-protocol/src/DocumentSymbolClientCapabilitiesTagSupport.phpK5p}eKE$Fvendor/felixfbecker/language-server-protocol/src/CompletionContext.php$5p}e$}Bvendor/felixfbecker/language-server-protocol/src/SignatureHelp.php$5p}e${iTvendor/felixfbecker/language-server-protocol/src/DocumentColorClientCapabilities.php5p}e _vendor/felixfbecker/language-server-protocol/src/CompletionClientCapabilitiesCompletionList.php5p}e$>vendor/felixfbecker/language-server-protocol/src/ErrorCode.phpi5p}ei0>vendor/felixfbecker/language-server-protocol/src/SymbolTag.php-5p}e-T>Uvendor/felixfbecker/language-server-protocol/src/TypeDefinitionClientCapabilities.php5p}esFvendor/felixfbecker/language-server-protocol/src/PackageDescriptor.php5p}eqJEvendor/felixfbecker/language-server-protocol/src/InsertTextFormat.php5p}e!)?vendor/felixfbecker/language-server-protocol/src/ClientInfo.php 5p}e abIvendor/felixfbecker/language-server-protocol/src/SignatureHelpOptions.php5p}eи?vendor/felixfbecker/language-server-protocol/src/MarkupKind.php$5p}e$0Dvendor/felixfbecker/language-server-protocol/src/CodeLensOptions.phpq5p}eqGvendor/felixfbecker/language-server-protocol/src/CompletionItemKind.php5p}e (4Ovendor/felixfbecker/language-server-protocol/src/CodeLensClientCapabilities.php5p}e*zhIvendor/felixfbecker/language-server-protocol/src/TextDocumentSyncKind.php>5p}e>_)@Tvendor/felixfbecker/language-server-protocol/src/CallHierarchyClientCapabilities.phpH5p}eH8QDvendor/felixfbecker/language-server-protocol/src/WorkspaceFolder.php5p}eK¤Svendor/felixfbecker/language-server-protocol/src/ShowDocumentClientCapabilities.phpV5p}eV|`vendor/felixfbecker/language-server-protocol/src/WorkspaceSymbolClientCapabilitiesTagSupport.php5p}e ?:vendor/felixfbecker/language-server-protocol/src/Hover.php5p}e+;]Jvendor/felixfbecker/language-server-protocol/src/CompletionTriggerKind.php65p}e6>{_vendor/felixfbecker/language-server-protocol/src/CompletionClientCapabilitiesCompletionItem.php5p}e@l0Svendor/felixfbecker/language-server-protocol/src/TextDocumentClientCapabilities.php+5p}e+Ckvendor/felixfbecker/language-server-protocol/src/WorkspaceEditClientCapabilitiesChangeAnnotationSupport.php5p}eͣ&Wvendor/felixfbecker/language-server-protocol/src/TextDocumentSyncClientCapabilities.phpJ5p}eJ\Cvendor/felixfbecker/language-server-protocol/src/CompletionItem.php5p}ek6Rvendor/felixfbecker/language-server-protocol/src/PrepareSupportDefaultBehavior.php 5p}e G<Rvendor/felixfbecker/language-server-protocol/src/DeclarationClientCapabilities.php5p}e:`vendor/felixfbecker/language-server-protocol/src/WorkspaceSymbolClientCapabilitiesSymbolKind.php5p}enΤCvendor/felixfbecker/language-server-protocol/src/CodeActionKind.php5p}e1|vendor/felixfbecker/language-server-protocol/src/SignatureHelpClientCapabilitiesSignatureInformationParameterInformation.php!5p}e!e/Uvendor/felixfbecker/language-server-protocol/src/SemanticTokensClientCapabilities.php5p}eYQYvendor/felixfbecker/language-server-protocol/src/RegularExpressionsClientCapabilities.php[5p}e[L;Bvendor/felixfbecker/language-server-protocol/src/WorkspaceEdit.php> 5p}e> ~=a:vendor/felixfbecker/language-server-protocol/src/Range.php5p}e+]=vendor/felixfbecker/language-server-protocol/src/Location.php)5p}e)bdvendor/felixfbecker/language-server-protocol/src/WorkspaceSymbolClientCapabilitiesResolveSupport.php5p}eFvendor/felixfbecker/language-server-protocol/src/MessageActionItem.phpq5p}eqFvendor/felixfbecker/language-server-protocol/src/SymbolInformation.php5p}erPvendor/felixfbecker/language-server-protocol/src/ClientCapabilitiesWorkspace.phpi5p}ei*=vendor/felixfbecker/language-server-protocol/src/LogTrace.php5p}e#GUvendor/felixfbecker/language-server-protocol/src/SelectionRangeClientCapabilities.phpJ5p}eJNjoSvendor/felixfbecker/language-server-protocol/src/DocumentLinkClientCapabilities.php5p}e&@3b<vendor/felixfbecker/language-server-protocol/src/Command.php5p}e]Ivendor/felixfbecker/language-server-protocol/src/SignatureInformation.php5p}eG^vendor/felixfbecker/language-server-protocol/src/SemanticTokensWorkspaceClientCapabilities.php5p}e/=Tvendor/felixfbecker/language-server-protocol/src/VersionedTextDocumentIdentifier.php5p}eN^vendor/felixfbecker/language-server-protocol/src/ClientCapabilitiesWorkspaceFileOperations.phpn5p}en:Pvendor/felixfbecker/language-server-protocol/src/ReferenceClientCapabilities.phpw5p}ewrСNvendor/felixfbecker/language-server-protocol/src/ClientCapabilitiesGeneral.php5p}e~N-\vendor/felixfbecker/language-server-protocol/src/DidChangeWatchedFilesClientCapabilities.php5p}eF Hvendor/felixfbecker/language-server-protocol/src/DependencyReference.php5p}e4_Tvendor/felixfbecker/language-server-protocol/src/SignatureHelpClientCapabilities.php5p}e8&Yvendor/felixfbecker/language-server-protocol/src/LinkedEditingRangeClientCapabilities.phpL5p}eL8ivendor/felixfbecker/language-server-protocol/src/CodeActionClientCapabilitiesCodeActionLiteralSupport.php5p}eNTvendor/felixfbecker/language-server-protocol/src/WorkspaceEditClientCapabilities.php5p}ecktvendor/felixfbecker/language-server-protocol/src/CompletionClientCapabilitiesCompletionItemInsertTextModeSupport.php5p}e@9Jvendor/felixfbecker/language-server-protocol/src/ResourceOperationKind.php5p}e*==vendor/felixfbecker/language-server-protocol/src/TextEdit.phpz5p}ezSy泤?vendor/felixfbecker/language-server-protocol/src/CodeAction.php5p}e=vendor/felixfbecker/language-server-protocol/src/Position.php5p}eHHvendor/felixfbecker/language-server-protocol/src/FailureHandlingKind.php=5p}e=dWhvendor/felixfbecker/language-server-protocol/src/SignatureHelpClientCapabilitiesSignatureInformation.php5p}el4Qvendor/felixfbecker/language-server-protocol/src/CompletionClientCapabilities.phpl5p}elXDvendor/felixfbecker/language-server-protocol/src/CodeDescription.php5p}e5'uSQvendor/felixfbecker/language-server-protocol/src/DefinitionClientCapabilities.phpo5p}eoEFvendor/felixfbecker/language-server-protocol/src/CompletionItemTag.php5p}er9䌤]vendor/felixfbecker/language-server-protocol/src/DidChangeConfigurationClientCapabilities.php5p}eQvendor/felixfbecker/language-server-protocol/src/DiagnosticRelatedInformation.php5p}e"+Svendor/felixfbecker/language-server-protocol/src/FoldingRangeClientCapabilities.php5p}eeSFvendor/felixfbecker/language-server-protocol/src/FormattingOptions.php5p}e<^Gvendor/felixfbecker/language-server-protocol/src/ContentChangeEvent.phpf5p}ef0-vendor/felixfbecker/advanced-json-rpc/LICENSE5p}e>95vendor/felixfbecker/advanced-json-rpc/lib/Request.php5p}e Ƥ3vendor/felixfbecker/advanced-json-rpc/lib/Error.php5p}eT$:vendor/felixfbecker/advanced-json-rpc/lib/Notification.php5p}e'6vendor/felixfbecker/advanced-json-rpc/lib/Response.php5p}el7٤8vendor/felixfbecker/advanced-json-rpc/lib/Dispatcher.php5p}eդ;vendor/felixfbecker/advanced-json-rpc/lib/ErrorResponse.phpr5p}er[{7vendor/felixfbecker/advanced-json-rpc/lib/ErrorCode.php 5p}e 8=vendor/felixfbecker/advanced-json-rpc/lib/SuccessResponse.phph5p}eh65vendor/felixfbecker/advanced-json-rpc/lib/Message.php5p}efvendor/webmozart/assert/LICENSE<5p}e<t}8vendor/webmozart/assert/src/InvalidArgumentException.phpq5p}eqU&vendor/webmozart/assert/src/Assert.php5p}e_פ%vendor/webmozart/assert/src/Mixin.php=5p}e=jQvendor/sebastian/diff/LICENSE 5p}e a1@vendor/sebastian/diff/src/Exception/InvalidArgumentException.php5p}ez#ܤ1vendor/sebastian/diff/src/Exception/Exception.phpy5p}ey'>vendor/sebastian/diff/src/Exception/ConfigurationException.phpL5p}eL`Q"vendor/sebastian/diff/src/Diff.phpy5p}eyBt"vendor/sebastian/diff/src/Line.php[5p}e[%2Ovendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php 5p}e >@vendor/sebastian/diff/src/LongestCommonSubsequenceCalculator.php5p}e #vendor/sebastian/diff/src/Chunk.phpn5p}en\Mvendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php? 5p}e? mϑ$vendor/sebastian/diff/src/Parser.php 5p}e ?<?vendor/sebastian/diff/src/Output/AbstractChunkOutputBuilder.php5p}eg]=vendor/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php\5p}e\ 0Cvendor/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php(5p}e(%?vendor/sebastian/diff/src/Output/DiffOutputBuilderInterface.php#5p}e#OѤ:vendor/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php5p}eU$vendor/sebastian/diff/src/Differ.php:$5p}e:$v'vendor/dnoegel/php-xdg-base-dir/LICENSE!5p}e!W&+vendor/dnoegel/php-xdg-base-dir/src/Xdg.php 5p}e 2դ vendor/amphp/byte-stream/LICENSE65p}e6\>W+vendor/amphp/byte-stream/lib/LineReader.php 5p}e Ư5vendor/amphp/byte-stream/lib/ResourceOutputStream.php0*5p}e0*0vendor/amphp/byte-stream/lib/ZlibInputStream.php 5p}e V-vendor/amphp/byte-stream/lib/OutputBuffer.php5p}e' ޤAvendor/amphp/byte-stream/lib/Base64/Base64EncodingInputStream.php5p}e#Avendor/amphp/byte-stream/lib/Base64/Base64DecodingInputStream.phpP5p}eP`/Bvendor/amphp/byte-stream/lib/Base64/Base64EncodingOutputStream.php5p}e0Bvendor/amphp/byte-stream/lib/Base64/Base64DecodingOutputStream.php5p}eZ0vendor/amphp/byte-stream/lib/StreamException.phpf5p}ef܌%/vendor/amphp/byte-stream/lib/InMemoryStream.php5p}eM1vendor/amphp/byte-stream/lib/PendingReadError.php5p}ey!/vendor/amphp/byte-stream/lib/IteratorStream.php5p}e\*vendor/amphp/byte-stream/lib/functions.php5p}e$r-vendor/amphp/byte-stream/lib/OutputStream.phpJ5p}eJuD 1vendor/amphp/byte-stream/lib/InputStreamChain.php5p}eߤ0vendor/amphp/byte-stream/lib/ClosedException.phpq5p}eqԤ,vendor/amphp/byte-stream/lib/InputStream.phpO5p}eO?w¤(vendor/amphp/byte-stream/lib/Message.phpp5p}epDi4vendor/amphp/byte-stream/lib/ResourceInputStream.php<5p}e<=G(vendor/amphp/byte-stream/lib/Payload.php 5p}e o1vendor/amphp/byte-stream/lib/ZlibOutputStream.phpg 5p}eg Nvendor/amphp/amp/LICENSEq5p}eqrg1vendor/amphp/amp/lib/TimeoutCancellationToken.php35p}e3 Ҥ+vendor/amphp/amp/lib/CancelledException.phpk5p}ekafom0vendor/amphp/amp/lib/CancellationTokenSource.php=5p}e=<9vendor/amphp/amp/lib/Loop/UnsupportedFeatureException.php(5p}e(p¤$vendor/amphp/amp/lib/Loop/Driver.phpCg5p}eCg Ŗ*vendor/amphp/amp/lib/Loop/NativeDriver.php85p}e8(1vendor/amphp/amp/lib/Loop/InvalidWatcherError.php*5p}e*7j:+vendor/amphp/amp/lib/Loop/DriverFactory.php5p}e*&+vendor/amphp/amp/lib/Loop/TracingDriver.phpi5p}ei!)vendor/amphp/amp/lib/Loop/EventDriver.phpL%5p}eL%y&vendor/amphp/amp/lib/Loop/UvDriver.php(5p}e(欤%vendor/amphp/amp/lib/Loop/Watcher.php5p}eL &vendor/amphp/amp/lib/Loop/EvDriver.php!5p}e!ɤ1vendor/amphp/amp/lib/Loop/Internal/TimerQueue.phpJ5p}eJv[)vendor/amphp/amp/lib/TimeoutException.php5p}e"_Ȥ vendor/amphp/amp/lib/Success.php5p}e0J!vendor/amphp/amp/lib/Deferred.php)5p}e)#2vendor/amphp/amp/lib/CombinedCancellationToken.php5p}e!vendor/amphp/amp/lib/Producer.php5p}e){+ vendor/amphp/amp/lib/Promise.php5p}e% vendor/amphp/amp/lib/Delayed.php5p}eDQ$vendor/amphp/amp/lib/LazyPromise.php5p}etФ.vendor/amphp/amp/lib/NullCancellationToken.php5p}ea͉&vendor/amphp/amp/lib/CallableMaker.php 5p}e t vendor/amphp/amp/lib/Loop.phpF5p}eFw vendor/amphp/amp/lib/Failure.php5p}e`!vendor/amphp/amp/lib/Iterator.php\5p}e\sj~-vendor/amphp/amp/lib/MultiReasonException.php5p}eǼM"vendor/amphp/amp/lib/Coroutine.php)5p}e);y*vendor/amphp/amp/lib/CancellationToken.php5p}eӷԤ*vendor/amphp/amp/lib/InvalidYieldError.php5p}e`Pmvendor/amphp/amp/lib/Struct.php5p}e4H"vendor/amphp/amp/lib/functions.php]5p}e]m vendor/amphp/amp/lib/Emitter.php5p}eG1vendor/amphp/amp/lib/Internal/ResolutionQueue.php 5p}e u,1vendor/amphp/amp/lib/Internal/PrivateIterator.php5p}e֕0vendor/amphp/amp/lib/Internal/PrivatePromise.php15p}e1[*vendor/amphp/amp/lib/Internal/Producer.php,5p}e,6|)r-vendor/amphp/amp/lib/Internal/Placeholder.php+5p}e++*+vendor/amphp/amp/lib/Internal/functions.php 5p}e \ڤvendor/composer/semver/LICENSE5p}eBh/vendor/composer/semver/src/CompilingMatcher.php5 5p}e5 ,=vendor/composer/semver/src/Constraint/MatchNoneConstraint.php5p}eJ~9vendor/composer/semver/src/Constraint/MultiConstraint.phpD#5p}eD#[iǤ=vendor/composer/semver/src/Constraint/ConstraintInterface.php5p}e c<vendor/composer/semver/src/Constraint/MatchAllConstraint.php5p}e4vendor/composer/semver/src/Constraint/Constraint.php05p}e0yqŤ/vendor/composer/semver/src/Constraint/Bound.phpI 5p}eI u,vendor/composer/semver/src/VersionParser.phpT5p}eT(߳(vendor/composer/semver/src/Intervals.phpN5p}eN)~k'vendor/composer/semver/src/Interval.php5p}eB<ݤ%vendor/composer/semver/src/Semver.php\ 5p}e\ A")vendor/composer/semver/src/Comparator.phpj 5p}ej "vendor/composer/autoload_files.php5p}ekP%vendor/composer/InstalledVersions.phpK?5p}eK?4 vendor/composer/pcre/LICENSE5p}ehg^3vendor/composer/pcre/src/MatchWithOffsetsResult.php5p}ed)4vendor/composer/pcre/src/MatchStrictGroupsResult.php5p}euA7vendor/composer/pcre/src/MatchAllStrictGroupsResult.php5p}e$"vendor/composer/pcre/src/Regex.php5p}e}]Ƥ*vendor/composer/pcre/src/ReplaceResult.php5p}e{f=֤!vendor/composer/pcre/src/Preg.phpD5p}eD{y9vendor/composer/pcre/src/UnexpectedNullMatchException.php5p}e!t+vendor/composer/pcre/src/MatchAllResult.php5p}eL-(vendor/composer/pcre/src/MatchResult.php5p}e*vendor/composer/pcre/src/PcreException.php 5p}e ZOT6vendor/composer/pcre/src/MatchAllWithOffsetsResult.php5p}eˆ!vendor/composer/autoload_psr4.php 5p}e B C&vendor/composer/xdebug-handler/LICENSE)5p}e)#;^4vendor/composer/xdebug-handler/src/XdebugHandler.phpVN5p}eVN)x.vendor/composer/xdebug-handler/src/Process.phpR 5p}eR 2?-vendor/composer/xdebug-handler/src/Status.php5p}eΌ0vendor/composer/xdebug-handler/src/PhpConfig.php5p}e̊%vendor/composer/autoload_classmap.php5p}eKD#vendor/composer/autoload_static.php,5p}e,yMvendor/composer/LICENSE.5p}e. vendor/composer/ClassLoader.php?5p}e?2@u!vendor/composer/autoload_real.php5p}eԇ0vendor/composer/installed.phpU5p}eUM"vendor/composer/platform_check.php5p}eqK\'vendor/composer/autoload_namespaces.php5p}e/t%vendor/netresearch/jsonmapper/LICENSER(5p}eR((}:vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php05p}e0k0vendor/netresearch/jsonmapper/src/JsonMapper.phps5p}es7$vendor/phpstan/phpdoc-parser/LICENSE.5p}e.-0vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php5p}ed24vendor/phpstan/phpdoc-parser/src/Printer/Printer.php5p}eO5vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php5p}e;G~3vendor/phpstan/phpdoc-parser/src/Printer/Differ.phpr5p}er{;Evendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFloatNode.php5p}eP}!Dvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.phpE5p}eEtEvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.phpG5p}eG&@vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php5p}eHPvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php6 5p}e6 =Nvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/DoctrineConstExprStringNode.php5p}ez=Dvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNullNode.phpE5p}eEXMEvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.phpP5p}eP Ivendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php5p}e\Avendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstFetchNode.php5p}e*ĔFvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprStringNode.php5p}eäGvendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php5p}e0U4vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php 5p}e nCvendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php%5p}e%>;-vendor/phpstan/phpdoc-parser/src/Ast/Node.php5p}e7vendor/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php5p}eBnɲ<vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php5p}e62vendor/phpstan/phpdoc-parser/src/Ast/Attribute.phpP5p}eP&mKvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php5p}e6rkDvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.phpQ5p}eQZɧMvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php5p}e ᤤIvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php 5p}e Kvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php5p}el Jvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php5p}ee4lFFvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArray.php5p}eiBvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php5p}eHuBvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php5p}eYh?vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.phpf5p}efQٝ?vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php5p}e! +>vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php5p}e@vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php5p}e[:vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocNode.php$5p}e$ui Bvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php5p}eCvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/SelfOutTagValueNode.php5p}ea(uBvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php5p}e&#Cvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php5p}eJJQ2Cvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php5p}e/#Kvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php5p}e8UBvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php5p}e t=vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php+5p}e+P-Avendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php5p}e'׌zDvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamOutTagValueNode.phpV5p}eVO'Jvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php5p}e#ۤEvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasTagValueNode.php5p}e.C0Fvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/DeprecatedTagValueNode.php5p}eADvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php55p}e5Ivendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypelessParamTagValueNode.php5p}e xCvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php5p}e֩Avendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php5p}e iFvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php5p}e!Hvendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php 5p}e ,ڤBvendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php5p}e(P2=vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.phpy5p}eyMvendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeForParameterNode.php5p}eP@vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php5p}e$6vendor/phpstan/phpdoc-parser/src/Ast/Type/TypeNode.php5p}eB*>vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php&5p}e&+Gvendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php5p}eS2=vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.phpB5p}eB:vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php75p}e7$Bvendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php5p}eϰB>vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php5p}e;3<vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php5p}e˨qAvendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeNode.php5p}ed=vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php\5p}e\[";vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php5p}e`)@vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php5p}eIQ0;vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php>5p}e>h ;vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php5p}e\Avendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php!5p}e!wyV6vendor/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php4*5p}e4*d;vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php 5p}e F~hm;vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php 5p}e of<6vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php|5p}e|!;vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php&5p}e&T8vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php5p}e!9vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php#5p}e#xa-vendor/spatie/array-to-xml/src/ArrayToXml.php5p}eP"dictionaries/ManualPropertyMap.php5p}e[ۤ<dictionaries/scripts/update_signaturemap_from_other_tool.php5p}e}!dictionaries/CallMap_71_delta.php5p}e0!dictionaries/CallMap_73_delta.php'5p}e'JsƤ$dictionaries/ImpureFunctionsList.php5p}esƓ!dictionaries/CallMap_72_delta.php{L5p}e{L1>dictionaries/PropertyMap.php}E5p}e}E i#dictionaries/CallMap_historical.php5p}eȡ|!dictionaries/CallMap_81_delta.php5p}eFt!dictionaries/CallMap_74_delta.php_5p}e_A!dictionaries/CallMap_83_delta.phpR5p}eR'SU٤dictionaries/CallMap.phpO5p}eO5%dictionaries/InternalTaintSinkMap.phpY5p}eYa!dictionaries/CallMap_82_delta.php 5p}e [!dictionaries/CallMap_80_delta.php.s5p}e.s.X8 config.xsdS5p}eSUŞphar-versions.php5p}ec"stubs/CoreGenericIterators.phpstubj5p}ejQstubs/SPL.phpstubti5p}eti=lstubs/Php74.phpstub5p}estubs/extensions/xdebug.phpstub5p}eȤstubs/extensions/redis.phpstubP5p}eP8Ƥ stubs/extensions/decimal.phpstubA5p}eApstubs/extensions/random.phpstubG 5p}eG Yf'stubs/extensions/apcu.phpstub5p}eͤstubs/extensions/gmp.phpstub5p}erUstubs/extensions/ds.phpstub4c5p}e4cNQ٤stubs/extensions/pdo.phpstubK5p}eKSE stubs/extensions/rdkafka.phpstubv5p}evGstubs/extensions/geos.phpstub5p}eSstubs/extensions/ffi.phpstub$5p}e$GGstubs/extensions/dom.phpstub]5p}e]ѡۆ"stubs/extensions/simplexml.phpstub5p}eؿ stubs/extensions/mysqli.phpstub5p}e1T stubs/extensions/ibm_db2.phpstub5p}e3^:stubs/extensions/soap.phpstub{+5p}e{+lN stubs/extensions/mongodb.phpstubF5p}eF๐ߤ#stubs/CoreGenericAttributes.phpstubR5p}eRbbstubs/phpparser.phpstub/5p}e/ifĤ"stubs/CoreImmutableClasses.phpstubE5p}eEXy;stubs/Php80.phpstub;5p}e;2"stubs/CoreGenericFunctions.phpstub/5p}e/? stubs/CoreGenericClasses.phpstub=5p}e=VBˤstubs/Reflection.phpstub85p}e8(-stubs/Php81.phpstub 5p}e stubs/Php82.phpstub5p}ej.box/src/Printer.php 5p}e )8AV!.box/src/IsExtensionFulfilled.phpv5p}ev'0Ҥ.box/src/Terminal.php 5p}e %V".box/src/RequirementCollection.php5p}eWb?.box/src/Requirement.phpm5p}emB".box/src/IsPhpVersionFulfilled.php5p}e!83.box/src/IsFulfilled.phpv5p}ev%*H.box/src/Checker.php5p}e)W>.box/src/IO.php 5p}e [l%.box/vendor/autoload.php5p}es>.box/vendor/composer/semver/src/Constraint/MultiConstraint.php~5p}e~5[eB.box/vendor/composer/semver/src/Constraint/ConstraintInterface.php5p}ewQ9.box/vendor/composer/semver/src/Constraint/Constraint.php'&5p}e'&`1.box/vendor/composer/semver/src/VersionParser.php95p}e9Wz *.box/vendor/composer/semver/src/Semver.php5p}eׯ..box/vendor/composer/semver/src/Comparator.phpu5p}eu 74&.box/vendor/composer/autoload_psr4.php65p}e60Ӥ*.box/vendor/composer/autoload_classmap.php, 5p}e, ӽǤ(.box/vendor/composer/autoload_static.phpK5p}eK?.box/vendor/composer/LICENSE.5p}e. $.box/vendor/composer/ClassLoader.php>5p}e>5Ky&.box/vendor/composer/autoload_real.php5p}eyi\,.box/vendor/composer/autoload_namespaces.php5p}et!פ.box/bin/check-requirements.php5p}e^ʨ.box/.requirements.php5p}ezC1docs/running_psalm/issues/InvalidPropertyFetch.md5p}e`&9docs/running_psalm/issues/MismatchingDocblockParamType.md5p}eX7docs/running_psalm/issues/PossiblyInvalidArrayAccess.md5p}eci,0docs/running_psalm/issues/DeprecatedInterface.mdA5p}eA7U;docs/running_psalm/issues/PossiblyInvalidArrayAssignment.md5p}e,1docs/running_psalm/issues/MixedArrayAssignment.md5p}e;l2docs/running_psalm/issues/UninitializedProperty.md5p}e })docs/running_psalm/issues/InvalidThrow.md5p}e)Ԥ+docs/running_psalm/issues/DuplicateClass.md5p}e-docs/running_psalm/issues/LoopInvalidation.md5p}e|1docs/running_psalm/issues/MissingTemplateParam.md5p}e:,docs/running_psalm/issues/DuplicateMethod.md5p}e)4docs/running_psalm/issues/UndefinedGlobalVariable.mdn5p}ens)docs/running_psalm/issues/InvalidClone.mdz5p}ezx&%.docs/running_psalm/issues/UndefinedVariable.md5p}e_.docs/running_psalm/issues/InvalidEnumMethod.md5p}eŇѤ/docs/running_psalm/issues/InvalidArrayOffset.md5p}eyyڤ(docs/running_psalm/issues/UnusedClass.md5p}eHYڤ5docs/running_psalm/issues/MissingClosureReturnType.md5p}emj.docs/running_psalm/issues/MissingReturnType.md5p}eLƤ*docs/running_psalm/issues/MixedArgument.md5p}efD,docs/running_psalm/issues/NullArrayOffset.md5p}e%S6docs/running_psalm/issues/PossiblyInvalidMethodCall.md5p}e~ 4docs/running_psalm/issues/MethodSignatureMismatch.md'5p}e'd:0docs/running_psalm/issues/UnusedDocblockParam.mdP5p}ePao٪4docs/running_psalm/issues/NamedArgumentNotAllowed.md5p}e8Ѥ/docs/running_psalm/issues/UnusedFunctionCall.md5p}e=q)docs/running_psalm/issues/InvalidClass.mdb5p}ebn;6docs/running_psalm/issues/PossiblyUndefinedVariable.md5p}e *docs/running_psalm/issues/ComplexMethod.mdc5p}ecX6docs/running_psalm/issues/MixedArgumentTypeCoercion.md&5p}e&>@#,docs/running_psalm/issues/DeprecatedClass.md"5p}e"2C@docs/running_psalm/issues/RedundantConditionGivenDocblockType.md{5p}e{tޤ*docs/running_psalm/issues/RedundantCast.md5p}e /18docs/running_psalm/issues/UndefinedPropertyAssignment.md5p}euȐ2docs/running_psalm/issues/TooManyTemplateParams.mdE5p}eELQڤ1docs/running_psalm/issues/ImpureStaticVariable.md5p}e :docs/running_psalm/issues/LessSpecificClassConstantType.mdK5p}eKi7a^Adocs/running_psalm/issues/MethodSignatureMustProvideReturnType.md5p}e&0docs/running_psalm/issues/UnusedBaselineEntry.md5p}el_9docs/running_psalm/issues/TraitMethodSignatureMismatch.md5p}e,>docs/running_psalm/issues/UnsupportedPropertyReferenceUsage.mdp5p}epF3docs/running_psalm/issues/InvalidEnumBackingType.md5p}e8K.docs/running_psalm/issues/InvalidTypeImport.md5p}eK.docs/running_psalm/issues/CircularReference.md5p}ep/docs/running_psalm/issues/UnusedForeachValue.md5p}eW4docs/running_psalm/issues/UndefinedAttributeClass.md5p}e]2/docs/running_psalm/issues/UndefinedInterface.md5p}eg@vV)docs/running_psalm/issues/NullArgument.md5p}eϥŤ0docs/running_psalm/issues/UnusedPsalmSuppress.md5p}eW0,docs/running_psalm/issues/MixedAssignment.md5p}e=8docs/running_psalm/issues/OverriddenInterfaceConstant.md5p}eN6docs/running_psalm/issues/InvalidNullableReturnType.md5p}eDŤ)docs/running_psalm/issues/ReservedWord.md|5p}e|>9docs/running_psalm/issues/ReferenceConstraintViolation.md5p}e sϤ(docs/running_psalm/issues/NullOperand.md5p}ep$0docs/running_psalm/issues/PossiblyUnusedParam.md5p}e6ˤ+docs/running_psalm/issues/ImpureVariable.md5p}eȉ.docs/running_psalm/issues/TaintedUserSecret.md5p}eZ;docs/running_psalm/issues/InvalidInterfaceImplementation.md5p}ei;R>docs/running_psalm/issues/MethodSignatureMustOmitReturnType.md5p}e#/docs/running_psalm/issues/MissingConstructor.md5p}eKX?docs/running_psalm/issues/ImplementationRequirementViolation.md5p}eդ0docs/running_psalm/issues/UnresolvableInclude.md5p}e8docs/running_psalm/issues/UndefinedMagicPropertyFetch.md5p}e*docs/running_psalm/issues/TaintedCookie.md5p}e6docs/running_psalm/issues/PossiblyUnusedReturnValue.md 5p}e 0 2docs/running_psalm/issues/InvalidScalarArgument.md5p}ea 3docs/running_psalm/issues/PossiblyInvalidOperand.md5p}eL/-docs/running_psalm/issues/IfThisIsMismatch.md75p}e7a4docs/running_psalm/issues/NullableReturnStatement.md5p}e -docs/running_psalm/issues/UnusedMethodCall.md5p}e0T2docs/running_psalm/issues/MissingThrowsDocblock.md5p}e *"/docs/running_psalm/issues/InheritorViolation.md5p}eYI4docs/running_psalm/issues/UnhandledMatchCondition.mdW5p}eW93docs/running_psalm/issues/UndefinedPropertyFetch.md5p}e~h9docs/running_psalm/issues/PossiblyUndefinedArrayOffset.mdM5p}eM֚]l3docs/running_psalm/issues/PossiblyFalseReference.md5p}eW\]41docs/running_psalm/issues/PossiblyFalseOperand.md5p}e״1docs/running_psalm/issues/PossiblyNullArgument.md5p}e;Y46docs/running_psalm/issues/InvalidFalsableReturnType.md5p}e0docs/running_psalm/issues/MissingDocblockType.md5p}eOf*docs/running_psalm/issues/InternalClass.md5p}eዂ1docs/running_psalm/issues/InvalidTemplateParam.mdt5p}et|K)3docs/running_psalm/issues/OverriddenMethodAccess.md5p}en4docs/running_psalm/issues/MixedPropertyAssignment.md5p}eK`8docs/running_psalm/issues/ImpureByReferenceAssignment.md5p}eӬF5docs/running_psalm/issues/OverriddenPropertyAccess.md 5p}e 7:docs/running_psalm/issues/ImplementedReturnTypeMismatch.md5p}eO,docs/running_psalm/issues/UndefinedMethod.mdm5p}em^6docs/running_psalm/issues/InvalidPropertyAssignment.md5p}eD/docs/running_psalm/issues/InvalidArrayAccess.md5p}e[H,docs/running_psalm/issues/DeprecatedTrait.md05p}e0Xz.docs/running_psalm/issues/UnusedReturnValue.md45p}e43Yۤ&docs/running_psalm/issues/CheckType.md{5p}e{WRo0docs/running_psalm/issues/PossiblyInvalidCast.md5p}eS4docs/running_psalm/issues/PossiblyNullArrayOffset.md5p}e13docs/running_psalm/issues/MoreSpecificReturnType.mdM5p}eM+&.docs/running_psalm/issues/ParamNameMismatch.md 5p}e 3+docs/running_psalm/issues/UndefinedTrait.mdq5p}eqݹͤ1docs/running_psalm/issues/PropertyTypeCoercion.md5p}e (docs/running_psalm/issues/ConfigIssue.md5p}e,docs/running_psalm/issues/MixedMethodCall.mdf5p}efx!5docs/running_psalm/issues/InvalidDocblockParamName.md 5p}e $\3docs/running_psalm/issues/PossiblyUnusedProperty.md5p}ey,docs/running_psalm/issues/InvalidDocblock.mdt5p}et%Fz7docs/running_psalm/issues/ConstantDeclarationInTrait.md5p}e9[+docs/running_psalm/issues/UnusedVariable.md:5p}e:b+docs/running_psalm/issues/ParentNotFound.md5p}e/docs/running_psalm/issues/DeprecatedConstant.md5p}ednj4docs/running_psalm/issues/PossiblyNullArrayAccess.md5p}e=/docs/running_psalm/issues/RedundantCondition.md5p}eaI1docs/running_psalm/issues/PossiblyInvalidClone.md5p}ed.docs/running_psalm/issues/NonStaticSelfCall.md5p}ē4docs/running_psalm/issues/MixedInferredReturnType.md5p}e>.docs/running_psalm/issues/UndefinedFunction.mdf5p}efT4docs/running_psalm/issues/InvalidStaticInvocation.md5p}eAĤ6docs/running_psalm/issues/DocblockTypeContradiction.mdy5p}ey\1docs/running_psalm/issues/InaccessibleProperty.md5p}e&ؤ-docs/running_psalm/issues/MissingParamType.md5p}e_@d+docs/running_psalm/issues/DuplicateParam.md5p}e (docs/running_psalm/issues/TaintedHtml.md5p}evB)0docs/running_psalm/issues/MissingPropertyType.md5p}e`$docs/running_psalm/issues/NoValue.mdB5p}eBI3docs/running_psalm/issues/InvalidReturnStatement.md5p}e)docs/running_psalm/issues/InvalidScope.mda5p}eal#6docs/running_psalm/issues/UnsupportedReferenceUsage.md5p}eXd\>,docs/running_psalm/issues/TooFewArguments.md5p}e&Vڤ+docs/running_psalm/issues/UnusedProperty.md5p}esP^3docs/running_psalm/issues/InvalidPassByReference.md5p}ep8docs/running_psalm/issues/PossiblyInvalidFunctionCall.md5p}eJj5docs/running_psalm/issues/ImpurePropertyAssignment.md5p}ea3"9docs/running_psalm/issues/ConstructorSignatureMismatch.md5p}e (docs/running_psalm/issues/MissingFile.md5p}eH3G*docs/running_psalm/issues/NullReference.mdm5p}emߍ&-docs/running_psalm/issues/EmptyArrayAccess.mdx5p}ex-B9docs/running_psalm/issues/ImplementedParamTypeMismatch.md 5p}e 6J3docs/running_psalm/issues/UndefinedDocblockClass.md5p}e^7docs/running_psalm/issues/UndefinedThisPropertyFetch.md5p}eGvk5docs/running_psalm/issues/UnnecessaryVarAnnotation.md5p}euf.docs/running_psalm/issues/UndefinedConstant.mdn5p}eny}<docs/running_psalm/issues/PossiblyUndefinedGlobalVariable.md5p}etf@*docs/running_psalm/issues/InvalidGlobal.mdZ5p}eZ(docs/running_psalm/issues/TaintedEval.mdA5p}eA<.docs/running_psalm/issues/InvalidReturnType.md5p}eȸ66docs/running_psalm/issues/RedundantIdentityWithTrue.md95p}e9q`/docs/running_psalm/issues/MixedPropertyFetch.md5p}eFF/docs/running_psalm/issues/UnusedClosureParam.md5p}ez<6docs/running_psalm/issues/InaccessibleClassConstant.md5p}e <docs/running_psalm/issues/UndefinedThisPropertyAssignment.md5p}eG_}+docs/running_psalm/issues/TaintedInclude.md5p}e#2docs/running_psalm/issues/PossiblyFalseIterator.md5p}eI=docs/running_psalm/issues/InvalidTraversableImplementation.md>5p}e>iM3docs/running_psalm/issues/TypeDoesNotContainNull.md5p}e:^+docs/running_psalm/issues/UndefinedClass.mdh5p}eh ,docs/running_psalm/issues/StringIncrement.md5p}e$5docs/running_psalm/issues/InvalidClassConstantType.mdI5p}eI/L2docs/running_psalm/issues/NoInterfaceProperties.md-5p}e-/8docs/running_psalm/issues/PropertyNotSetInConstructor.md5p}ewä(docs/running_psalm/issues/UnusedParam.mdp5p}ep(docs/running_psalm/issues/TaintedSSRF.mdO5p}eO80docs/running_psalm/issues/UnsafeInstantiation.md=5p}e=*docs/running_psalm/issues/TaintedCustom.mdh5p}eh%=docs/running_psalm/issues/NonInvariantDocblockPropertyType.md5p}e =docs/running_psalm/issues/UndefinedMagicPropertyAssignment.md35p}e373,docs/running_psalm/issues/InvalidArgument.mdz5p}ezɤ2docs/running_psalm/issues/DirectConstructorCall.md5p}enW.docs/running_psalm/issues/DuplicateFunction.md5p}e"docs/running_psalm/issues/Trace.md5p}eDդ*docs/running_psalm/issues/InvalidParent.md5p}eӤ/docs/running_psalm/issues/DeprecatedProperty.md5p}eG`4docs/running_psalm/issues/OverriddenFinalConstant.md5p}eΌv/docs/running_psalm/issues/ImpureFunctionCall.md@5p}e@U)docs/running_psalm/issues/UnusedMethod.md5p}e:V0docs/running_psalm/issues/ContinueOutsideLoop.md5p}eO9Y֤.docs/running_psalm/issues/NullPropertyFetch.md{5p}e{=]X4docs/running_psalm/issues/MissingClosureParamType.md5p}e"&f6docs/running_psalm/issues/MixedPropertyTypeCoercion.md25p}e29A,docs/running_psalm/issues/TaintedCallable.md45p}e49Qפ0docs/running_psalm/issues/InvalidParamDefault.md5p}e}(docs/running_psalm/issues/InvalidCast.md5p}eM-docs/running_psalm/issues/TooManyArguments.md5p}e41docs/running_psalm/issues/ImpureStaticProperty.md75p}e7U7)docs/running_psalm/issues/TaintedInput.mdB5p}eB>docs/running_psalm/issues/LessSpecificImplementedReturnType.md5p}e8(R2docs/running_psalm/issues/PossiblyNullReference.md5p}e9n(6Adocs/running_psalm/issues/PossiblyFalsePropertyAssignmentValue.md35p}e3f큤1docs/running_psalm/issues/PossiblyUnusedMethod.mdb5p}eb`3docs/running_psalm/issues/InvalidLiteralArgument.md5p}ebgK9docs/running_psalm/issues/AmbiguousConstantInheritance.md(5p}e( ;f 1docs/running_psalm/issues/UnresolvableConstant.md5p}e0ۤ-docs/running_psalm/issues/MixedArrayOffset.md5p}eT.docs/running_psalm/issues/MutableDependency.mdP5p}ePv3docs/running_psalm/issues/UnrecognizedExpression.md5p}ecB7docs/running_psalm/issues/UnsafeGenericInstantiation.md5 5p}e5 ZNj>7docs/running_psalm/issues/PossibleRawObjectIteration.md5p}ee3docs/running_psalm/issues/InterfaceInstantiation.md5p}eZ.docs/running_psalm/issues/DuplicateArrayKey.md5p}e_-docs/running_psalm/issues/ImpureMethodCall.mdz5p}ezw1w-docs/running_psalm/issues/NullFunctionCall.mds5p}esY;docs/running_psalm/issues/InvalidPropertyAssignmentValue.md5p}eX3,docs/running_psalm/issues/ComplexFunction.mdi5p}ei{Τ+docs/running_psalm/issues/InternalMethod.md5p}e'docs/running_psalm/issues/MixedClone.mdx5p}ex;ɮ8docs/running_psalm/issues/LessSpecificReturnStatement.md5p}e18docs/running_psalm/issues/UnimplementedAbstractMethod.md5p}e+a-docs/running_psalm/issues/MixedArrayAccess.md5p}eRࣤ9docs/running_psalm/issues/PossiblyInvalidPropertyFetch.mdY5p}eYϾӤ,docs/running_psalm/issues/InvalidToString.md5p}e۱,docs/running_psalm/issues/InvalidIterator.md5p}eG%3docs/running_psalm/issues/TypeDoesNotContainType.md5p}em4docs/running_psalm/issues/FalsableReturnStatement.md5p}eY/2docs/running_psalm/issues/TaintedTextWithQuotes.md5p}ekb4docs/running_psalm/issues/PossiblyUndefinedMethod.md5p}e;docs/running_psalm/issues/InvalidConstantAssignmentValue.md5p}e;docs/running_psalm/issues/PossiblyNullPropertyAssignment.md5p}eA9docs/running_psalm/issues/UnimplementedInterfaceMethod.md5p}e_פ2docs/running_psalm/issues/UnrecognizedStatement.md5p}eC)docs/running_psalm/issues/TaintedShell.md5p}eQ:docs/running_psalm/issues/MismatchingDocblockReturnType.md5p}eB s)docs/running_psalm/issues/InvalidCatch.md5p}e,?docs/running_psalm/issues/PossiblyUndefinedStringArrayOffset.md)5p}e)V04docs/running_psalm/issues/PossiblyInvalidIterator.md5p}eD1,docs/running_psalm/issues/UnevaluatedCode.md5p}eߒ0docs/running_psalm/issues/ImpurePropertyFetch.md5p}eRV.docs/running_psalm/issues/DuplicateConstant.md5p}eB1docs/running_psalm/issues/MixedReturnStatement.md5p}eDm@docs/running_psalm/issues/PossiblyNullPropertyAssignmentValue.md$5p}e$-}1docs/running_psalm/issues/PossiblyNullIterator.md5p}eia)/docs/running_psalm/issues/PrivateFinalMethod.md5p}ex.docs/running_psalm/issues/MixedFunctionCall.md5p}e$3docs/running_psalm/issues/MixedArrayTypeCoercion.md25p}e2{'docs/running_psalm/issues/TaintedSql.mdy5p}ey7docs/running_psalm/issues/PossiblyInvalidDocblockTag.md5p}eE)5docs/running_psalm/issues/NonInvariantPropertyType.mdN5p}eN46docs/running_psalm/issues/PossiblyNullPropertyFetch.md5p}euŤ3docs/running_psalm/issues/LessSpecificReturnType.md5p}eL6e*docs/running_psalm/issues/TaintedHeader.md5p}e &-docs/running_psalm/issues/InvalidAttribute.md+5p}e+)֫ɤ'docs/running_psalm/issues/ParseError.md5p}euAf2docs/running_psalm/issues/PossiblyFalseArgument.mdT5p}eTM:docs/running_psalm/issues/ExtensionRequirementViolation.mdi5p}eiKZ+docs/running_psalm/issues/InvalidOperand.md5p}ejVz/docs/running_psalm/issues/InvalidExtendClass.mdp5p}ep="7docs/running_psalm/issues/PossiblyInvalidArrayOffset.md5p}e(;%7docs/running_psalm/issues/MissingImmutableAnnotation.md5p}e>մz(docs/running_psalm/issues/PluginIssue.md*5p}e*xͤ8docs/running_psalm/issues/PossiblyNullArrayAssignment.md5p}eǤ.docs/running_psalm/issues/DuplicateEnumCase.md5p}eÿq/docs/running_psalm/issues/DeprecatedFunction.md25p}e2/%B1docs/running_psalm/issues/ArgumentTypeCoercion.md]5p}e]?&^1docs/running_psalm/issues/InvalidEnumCaseValue.mdY5p}eYxw>docs/running_psalm/issues/ReferenceReusedFromConfusingScope.md5p}e+h-docs/running_psalm/issues/NoEnumProperties.md5p}e0lѤ+docs/running_psalm/issues/UndefinedTrace.md5p}e%N3docs/running_psalm/issues/DuplicateEnumCaseValue.mdR5p}eRhqA7docs/running_psalm/issues/UncaughtThrowInGlobalScope.md5p}e^.docs/running_psalm/issues/UnusedConstructor.md5p}e3n: 5docs/running_psalm/issues/PossiblyNullFunctionCall.md5p}eR>'<docs/running_psalm/issues/PossiblyUndefinedIntArrayOffset.md5p}ey7;docs/running_psalm/issues/RedundantCastGivenDocblockType.md5p}e$6v3docs/running_psalm/issues/InvalidArrayAssignment.md}5p}e}QAdocs/running_psalm/issues/RedundantPropertyInitializationCheck.md35p}e3rA.docs/running_psalm/issues/InvalidMethodCall.mdt5p}etlq /docs/running_psalm/issues/InaccessibleMethod.md5p}ewb,docs/running_psalm/issues/NullArrayAccess.mdx5p}ex6Mx1.docs/running_psalm/issues/MissingDependency.md5p}eC8docs/running_psalm/issues/MixedStringOffsetAssignment.md5p}e)docs/running_psalm/issues/NullIterator.md^5p}e^Vj*docs/running_psalm/issues/ForbiddenCode.md5p}e(g4docs/running_psalm/issues/MixedReturnTypeCoercion.md5p}eLj1docs/running_psalm/issues/InvalidNamedArgument.md5p}eM1docs/running_psalm/issues/ImplicitToStringCast.mdt5p}et!&docs/running_psalm/issues/RiskyCast.md35p}e3;Cdocs/running_psalm/issues/RedundantFunctionCallGivenDocblockType.mde5p}ee(90docs/running_psalm/issues/InvalidFunctionCall.mdx5p}exѶs2docs/running_psalm/issues/AbstractInstantiation.md5p}e!Ȥ<docs/running_psalm/issues/MismatchingDocblockPropertyType.md5p}eN-docs/running_psalm/issues/AssignmentToVoid.md5p}ePɤ-docs/running_psalm/issues/InternalProperty.md5p}ey6Q)docs/running_psalm/issues/FalseOperand.md5p}ey5docs/running_psalm/issues/UndefinedInterfaceMethod.md5p}e6ܬ1docs/running_psalm/issues/UndefinedMagicMethod.md5p}e1docs/running_psalm/issues/ParadoxicalCondition.md5p}eX/docs/running_psalm/issues/RawObjectIteration.mdm5p}em𒮤=docs/running_psalm/issues/MoreSpecificImplementedParamType.md5p}eYt4docs/running_psalm/issues/PossiblyInvalidArgument.md5p}e>docs/running_psalm/issues/PossiblyInvalidPropertyAssignment.md`5p}e`bݤ-docs/running_psalm/issues/DeprecatedMethod.mdc5p}ec^ӂ0docs/running_psalm/issues/PossiblyNullOperand.md5p}e פ(docs/running_psalm/issues/TaintedLdap.md5p}eֵ(docs/running_psalm/issues/TaintedFile.md5p}eȤ/docs/running_psalm/issues/TaintedUnserialize.md5p}ec;docs/running_psalm/issues/ConflictingReferenceConstraint.md5p}e -0docs/running_psalm/issues/TaintedSystemSecret.md5p}eG@Cdocs/running_psalm/issues/PossiblyInvalidPropertyAssignmentValue.md55p}e5Z2docs/running_psalm/issues/RedundantFunctionCall.md5p}e/docs/running_psalm/issues/InvalidStringClass.md5p}e*q/docs/running_psalm/issues/AbstractMethodCall.md5p}eW8Τ)docs/running_psalm/issues/MixedOperand.mdW5p}eWT 3docs/running_psalm/issues/NullPropertyAssignment.mdy5p}ey pcomposer.phar /vendor/ # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file # composer.lock { "name": "psalm/phar", "description": "Composer-based Psalm Phar", "license": ["MIT"], "require": { "php": "^7.1 || ^8.0" }, "conflict": { "vimeo/psalm" : "*" }, "bin": ["psalm.phar"], "config": { "platform-check": false } } This allows you to install [Psalm](https://github.com/vimeo/psalm) without worrying about composer conflicts. > */ private array $additionalFileTypeScanners = []; /** * @var array> */ private array $additionalFileTypeAnalyzers = []; /** * @var list */ private array $additionalFileExtensions = []; /** * @internal */ public function __construct(\Psalm\Config $config) { $this->config = $config; } /** * @param string $fileExtension e.g. `'html'` * @param class-string $className */ public function addFileTypeScanner(string $fileExtension, string $className) : void { if (!class_exists($className) || !is_a($className, FileScanner::class, \true)) { throw new LogicException(sprintf('Class %s must be of type %s', $className, FileScanner::class), 1622727271); } if (!empty($this->config->getFiletypeScanners()[$fileExtension]) || !empty($this->additionalFileTypeScanners[$fileExtension])) { throw new LogicException(sprintf('Cannot redeclare scanner for file-type %s', $fileExtension), 1622727272); } $this->additionalFileTypeScanners[$fileExtension] = $className; $this->addFileExtension($fileExtension); } /** * @return array> */ public function getAdditionalFileTypeScanners() : array { return $this->additionalFileTypeScanners; } /** * @param string $fileExtension e.g. `'html'` * @param class-string $className */ public function addFileTypeAnalyzer(string $fileExtension, string $className) : void { if (!class_exists($className) || !is_a($className, FileAnalyzer::class, \true)) { throw new LogicException(sprintf('Class %s must be of type %s', $className, FileAnalyzer::class), 1622727281); } if (!empty($this->config->getFiletypeAnalyzers()[$fileExtension]) || !empty($this->additionalFileTypeAnalyzers[$fileExtension])) { throw new LogicException(sprintf('Cannot redeclare analyzer for file-type %s', $fileExtension), 1622727282); } $this->additionalFileTypeAnalyzers[$fileExtension] = $className; $this->addFileExtension($fileExtension); } /** * @return array> */ public function getAdditionalFileTypeAnalyzers() : array { return $this->additionalFileTypeAnalyzers; } /** * @return list e.g. `['html', 'perl']` */ public function getAdditionalFileExtensions() : array { return $this->additionalFileExtensions; } /** * @param string $fileExtension e.g. `'html'` */ private function addFileExtension(string $fileExtension) : void { /** @psalm-suppress RedundantCondition */ if (!in_array($fileExtension, $this->additionalFileExtensions, \true) && !in_array($fileExtension, $this->config->getFileExtensions(), \true)) { $this->additionalFileExtensions[] = $fileExtension; } } } > */ public $use_referencing_locations = []; /** * @var FileStorageProvider */ public $file_storage_provider; /** * @var ClassLikeStorageProvider */ public $classlike_storage_provider; /** * @var bool */ public $collect_references = \false; /** * @var bool */ public $collect_locations = \false; /** * @var null|'always'|'auto' */ public $find_unused_code; /** * @var FileProvider */ public $file_provider; /** * @var FileReferenceProvider */ public $file_reference_provider; /** * @var StatementsProvider */ public $statements_provider; private Progress $progress; /** * @var array */ private static $stubbed_constants = []; /** * Whether to register autoloaded information * * @var bool */ public $register_autoload_files = \false; /** * Whether to log functions just at the file level or globally (for stubs) * * @var bool */ public $register_stub_files = \false; /** * @var bool */ public $find_unused_variables = \false; /** * @var Scanner */ public $scanner; /** * @var Analyzer */ public $analyzer; /** * @var Functions */ public $functions; /** * @var ClassLikes */ public $classlikes; /** * @var Methods */ public $methods; /** * @var Properties */ public $properties; /** * @var Populator */ public $populator; /** * @var ?TaintFlowGraph */ public $taint_flow_graph; /** * @var bool */ public $server_mode = \false; /** * @var bool */ public $store_node_types = \false; /** * Whether or not to infer types from usage. Computationally expensive, so turned off by default * * @var bool */ public $infer_types_from_usage = \false; /** * @var bool */ public $alter_code = \false; /** * @var bool */ public $diff_methods = \false; /** * @var array */ public $methods_to_move = []; /** * @var array */ public $methods_to_rename = []; /** * @var array */ public $properties_to_move = []; /** * @var array */ public $properties_to_rename = []; /** * @var array */ public $class_constants_to_move = []; /** * @var array */ public $class_constants_to_rename = []; /** * @var array */ public $classes_to_move = []; /** * @var array */ public $call_transforms = []; /** * @var array */ public $property_transforms = []; /** * @var array */ public $class_constant_transforms = []; /** * @var array */ public $class_transforms = []; /** * @var bool */ public $allow_backwards_incompatible_changes = \true; /** @var int */ public $analysis_php_version_id = PHP_VERSION_ID; /** @var 'cli'|'config'|'composer'|'tests'|'runtime' */ public $php_version_source = 'runtime'; /** * @var bool */ public $track_unused_suppressions = \false; /** @internal */ public function __construct(\Psalm\Config $config, Providers $providers, ?Progress $progress = null) { if ($progress === null) { $progress = new VoidProgress(); } $this->config = $config; $this->file_storage_provider = $providers->file_storage_provider; $this->classlike_storage_provider = $providers->classlike_storage_provider; $this->progress = $progress; $this->file_provider = $providers->file_provider; $this->file_reference_provider = $providers->file_reference_provider; $this->statements_provider = $providers->statements_provider; self::$stubbed_constants = []; $reflection = new Reflection($providers->classlike_storage_provider, $this); $this->scanner = new Scanner($this, $config, $providers->file_storage_provider, $providers->file_provider, $reflection, $providers->file_reference_provider, $progress); $this->loadAnalyzer(); $this->functions = new Functions($providers->file_storage_provider, $reflection); $this->classlikes = new ClassLikes($this->config, $providers->classlike_storage_provider, $providers->file_reference_provider, $providers->statements_provider, $this->scanner); $this->properties = new Properties($providers->classlike_storage_provider, $providers->file_reference_provider, $this->classlikes); $this->methods = new Methods($providers->classlike_storage_provider, $providers->file_reference_provider, $this->classlikes); $this->populator = new Populator($providers->classlike_storage_provider, $providers->file_storage_provider, $this->classlikes, $providers->file_reference_provider, $progress); $this->loadAnalyzer(); } private function loadAnalyzer() : void { $this->analyzer = new Analyzer($this->config, $this->file_provider, $this->file_storage_provider, $this->progress); } /** * @param array $candidate_files */ public function reloadFiles(ProjectAnalyzer $project_analyzer, array $candidate_files, bool $force = \false) : void { $this->loadAnalyzer(); if ($force) { FileReferenceProvider::clearCache(); } $this->file_reference_provider->loadReferenceCache(\false); FunctionLikeAnalyzer::clearCache(); if ($force || !$this->statements_provider->parser_cache_provider) { $diff_files = $candidate_files; } else { $diff_files = []; $parser_cache_provider = $this->statements_provider->parser_cache_provider; foreach ($candidate_files as $candidate_file_path) { if ($parser_cache_provider->loadExistingFileContentsFromCache($candidate_file_path) !== $this->file_provider->getContents($candidate_file_path)) { $diff_files[] = $candidate_file_path; } } } $referenced_files = $project_analyzer->getReferencedFilesFromDiff($diff_files, \false); foreach ($diff_files as $diff_file_path) { $this->invalidateInformationForFile($diff_file_path); } foreach ($referenced_files as $referenced_file_path) { if (in_array($referenced_file_path, $diff_files, \true)) { continue; } $file_storage = $this->file_storage_provider->get($referenced_file_path); foreach ($file_storage->classlikes_in_file as $fq_classlike_name) { $this->classlike_storage_provider->remove($fq_classlike_name); $this->classlikes->removeClassLike($fq_classlike_name); } $this->file_storage_provider->remove($referenced_file_path); $this->scanner->removeFile($referenced_file_path); } $referenced_files = array_combine($referenced_files, $referenced_files); $this->scanner->addFilesToDeepScan($referenced_files); $this->addFilesToAnalyze(array_combine($candidate_files, $candidate_files)); $this->scanner->scanFiles($this->classlikes); $this->file_reference_provider->updateReferenceCache($this, $referenced_files); $this->populator->populateCodebase(); } public function enterServerMode() : void { $this->server_mode = \true; $this->store_node_types = \true; } public function collectLocations() : void { $this->collect_locations = \true; $this->classlikes->collect_locations = \true; $this->methods->collect_locations = \true; $this->properties->collect_locations = \true; } /** * @param 'always'|'auto' $find_unused_code */ public function reportUnusedCode(string $find_unused_code = 'auto') : void { $this->collect_references = \true; $this->classlikes->collect_references = \true; $this->find_unused_code = $find_unused_code; $this->find_unused_variables = \true; } public function reportUnusedVariables() : void { $this->collect_references = \true; $this->find_unused_variables = \true; } /** * @param array $files_to_analyze */ public function addFilesToAnalyze(array $files_to_analyze) : void { $this->scanner->addFilesToDeepScan($files_to_analyze); $this->analyzer->addFilesToAnalyze($files_to_analyze); } /** * Scans all files their related files */ public function scanFiles(int $threads = 1) : void { $has_changes = $this->scanner->scanFiles($this->classlikes, $threads); if ($has_changes) { $this->populator->populateCodebase(); } } public function getFileContents(string $file_path) : string { return $this->file_provider->getContents($file_path); } /** * @return list */ public function getStatementsForFile(string $file_path) : array { return $this->statements_provider->getStatementsForFile($file_path, $this->analysis_php_version_id, $this->progress); } public function createClassLikeStorage(string $fq_classlike_name) : ClassLikeStorage { return $this->classlike_storage_provider->create($fq_classlike_name); } public function cacheClassLikeStorage(ClassLikeStorage $classlike_storage, string $file_path) : void { if (!$this->classlike_storage_provider->cache) { return; } $file_contents = $this->file_provider->getContents($file_path); $this->classlike_storage_provider->cache->writeToCache($classlike_storage, $file_path, $file_contents); } public function exhumeClassLikeStorage(string $fq_classlike_name, string $file_path) : void { $file_contents = $this->file_provider->getContents($file_path); $storage = $this->classlike_storage_provider->exhume($fq_classlike_name, $file_path, $file_contents); if ($storage->is_trait) { $this->classlikes->addFullyQualifiedTraitName($storage->name, $file_path); } elseif ($storage->is_interface) { $this->classlikes->addFullyQualifiedInterfaceName($storage->name, $file_path); } elseif ($storage->is_enum) { $this->classlikes->addFullyQualifiedEnumName($storage->name, $file_path); } else { $this->classlikes->addFullyQualifiedClassName($storage->name, $file_path); } } public static function getPsalmTypeFromReflection(?ReflectionType $type) : Union { return Reflection::getPsalmTypeFromReflectionType($type); } public function createFileStorageForPath(string $file_path) : FileStorage { return $this->file_storage_provider->create($file_path); } /** * @return array */ public function findReferencesToSymbol(string $symbol) : array { if (!$this->collect_locations) { throw new UnexpectedValueException('Should not be checking references'); } if (strpos($symbol, '::$') !== \false) { return $this->findReferencesToProperty($symbol); } if (strpos($symbol, '::') !== \false) { return $this->findReferencesToMethod($symbol); } return $this->findReferencesToClassLike($symbol); } /** * @return array */ public function findReferencesToMethod(string $method_id) : array { return $this->file_reference_provider->getClassMethodLocations(strtolower($method_id)); } /** * @return array */ public function findReferencesToProperty(string $property_id) : array { /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ [$fq_class_name, $property_name] = explode('::', $property_id); return $this->file_reference_provider->getClassPropertyLocations(strtolower($fq_class_name) . '::' . $property_name); } /** * @return CodeLocation[] * @psalm-return array */ public function findReferencesToClassLike(string $fq_class_name) : array { $fq_class_name_lc = strtolower($fq_class_name); $locations = $this->file_reference_provider->getClassLocations($fq_class_name_lc); if (isset($this->use_referencing_locations[$fq_class_name_lc])) { $locations = [...$locations, ...$this->use_referencing_locations[$fq_class_name_lc]]; } return $locations; } public function getClosureStorage(string $file_path, string $closure_id) : FunctionStorage { $file_storage = $this->file_storage_provider->get($file_path); // closures can be returned here if (isset($file_storage->functions[$closure_id])) { return $file_storage->functions[$closure_id]; } throw new UnexpectedValueException('Expecting ' . $closure_id . ' to have storage in ' . $file_path); } public function addGlobalConstantType(string $const_id, Union $type) : void { self::$stubbed_constants[$const_id] = $type; } public function getStubbedConstantType(string $const_id) : ?Union { return self::$stubbed_constants[$const_id] ?? null; } /** * @return array */ public function getAllStubbedConstants() : array { return self::$stubbed_constants; } public function fileExists(string $file_path) : bool { return $this->file_provider->fileExists($file_path); } /** * Check whether a class/interface exists */ public function classOrInterfaceExists(string $fq_class_name, ?\Psalm\CodeLocation $code_location = null, ?string $calling_fq_class_name = null, ?string $calling_method_id = null) : bool { return $this->classlikes->classOrInterfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id); } /** * Check whether a class/interface exists * * @psalm-assert-if-true class-string|interface-string|enum-string $fq_class_name */ public function classOrInterfaceOrEnumExists(string $fq_class_name, ?\Psalm\CodeLocation $code_location = null, ?string $calling_fq_class_name = null, ?string $calling_method_id = null) : bool { return $this->classlikes->classOrInterfaceOrEnumExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id); } /** @psalm-mutation-free */ public function classExtendsOrImplements(string $fq_class_name, string $possible_parent) : bool { return $this->classlikes->classExtends($fq_class_name, $possible_parent) || $this->classlikes->classImplements($fq_class_name, $possible_parent); } /** * Determine whether or not a given class exists */ public function classExists(string $fq_class_name, ?\Psalm\CodeLocation $code_location = null, ?string $calling_fq_class_name = null, ?string $calling_method_id = null) : bool { return $this->classlikes->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id); } /** * Determine whether or not a class extends a parent * * @throws UnpopulatedClasslikeException when called on unpopulated class * @throws InvalidArgumentException when class does not exist */ public function classExtends(string $fq_class_name, string $possible_parent) : bool { return $this->classlikes->classExtends($fq_class_name, $possible_parent, \true); } /** * Check whether a class implements an interface */ public function classImplements(string $fq_class_name, string $interface) : bool { return $this->classlikes->classImplements($fq_class_name, $interface); } public function interfaceExists(string $fq_interface_name, ?\Psalm\CodeLocation $code_location = null, ?string $calling_fq_class_name = null, ?string $calling_method_id = null) : bool { return $this->classlikes->interfaceExists($fq_interface_name, $code_location, $calling_fq_class_name, $calling_method_id); } public function interfaceExtends(string $interface_name, string $possible_parent) : bool { return $this->classlikes->interfaceExtends($interface_name, $possible_parent); } /** * @return array all interfaces extended by $interface_name */ public function getParentInterfaces(string $fq_interface_name) : array { return $this->classlikes->getParentInterfaces($this->classlikes->getUnAliasedName($fq_interface_name)); } /** * Determine whether or not a class has the correct casing */ public function classHasCorrectCasing(string $fq_class_name) : bool { return $this->classlikes->classHasCorrectCasing($fq_class_name); } public function interfaceHasCorrectCasing(string $fq_interface_name) : bool { return $this->classlikes->interfaceHasCorrectCasing($fq_interface_name); } public function traitHasCorrectCasing(string $fq_trait_name) : bool { return $this->classlikes->traitHasCorrectCasing($fq_trait_name); } /** * Given a function id, return the function like storage for * a method, closure, or function. * * @param non-empty-string $function_id * @return FunctionStorage|MethodStorage */ public function getFunctionLikeStorage(StatementsAnalyzer $statements_analyzer, string $function_id) : FunctionLikeStorage { $doesMethodExist = MethodIdentifier::isValidMethodIdReference($function_id) && $this->methodExists($function_id); if ($doesMethodExist) { $method_id = MethodIdentifier::wrap($function_id); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if (!$declaring_method_id) { throw new UnexpectedValueException('Declaring method for ' . $method_id . ' cannot be found'); } return $this->methods->getStorage($declaring_method_id); } return $this->functions->getStorage($statements_analyzer, strtolower($function_id)); } /** * Whether or not a given method exists * * @param string|MethodIdentifier $method_id * @param string|MethodIdentifier|null $calling_method_id */ public function methodExists($method_id, ?\Psalm\CodeLocation $code_location = null, $calling_method_id = null, ?string $file_path = null, bool $is_used = \true) : bool { return $this->methods->methodExists(MethodIdentifier::wrap($method_id), is_string($calling_method_id) ? strtolower($calling_method_id) : strtolower((string) $calling_method_id), $code_location, null, $file_path, \true, $is_used); } /** * @param string|MethodIdentifier $method_id * @return array */ public function getMethodParams($method_id) : array { return $this->methods->getMethodParams(MethodIdentifier::wrap($method_id)); } /** * @param string|MethodIdentifier $method_id */ public function isVariadic($method_id) : bool { return $this->methods->isVariadic(MethodIdentifier::wrap($method_id)); } /** * @param string|MethodIdentifier $method_id * @param list $call_args */ public function getMethodReturnType($method_id, ?string &$self_class, array $call_args = []) : ?Union { return $this->methods->getMethodReturnType(MethodIdentifier::wrap($method_id), $self_class, null, $call_args); } /** * @param string|MethodIdentifier $method_id */ public function getMethodReturnsByRef($method_id) : bool { return $this->methods->getMethodReturnsByRef(MethodIdentifier::wrap($method_id)); } /** * @param string|MethodIdentifier $method_id */ public function getMethodReturnTypeLocation($method_id, \Psalm\CodeLocation &$defined_location = null) : ?\Psalm\CodeLocation { return $this->methods->getMethodReturnTypeLocation(MethodIdentifier::wrap($method_id), $defined_location); } /** * @param string|MethodIdentifier $method_id */ public function getDeclaringMethodId($method_id) : ?string { $new_method_id = $this->methods->getDeclaringMethodId(MethodIdentifier::wrap($method_id)); return $new_method_id ? (string) $new_method_id : null; } /** * Get the class this method appears in (vs is declared in, which could give a trait) * * @param string|MethodIdentifier $method_id */ public function getAppearingMethodId($method_id) : ?string { $new_method_id = $this->methods->getAppearingMethodId(MethodIdentifier::wrap($method_id)); return $new_method_id ? (string) $new_method_id : null; } /** * @param string|MethodIdentifier $method_id * @return array */ public function getOverriddenMethodIds($method_id) : array { return $this->methods->getOverriddenMethodIds(MethodIdentifier::wrap($method_id)); } /** * @param string|MethodIdentifier $method_id */ public function getCasedMethodId($method_id) : string { return $this->methods->getCasedMethodId(MethodIdentifier::wrap($method_id)); } public function invalidateInformationForFile(string $file_path) : void { $this->scanner->removeFile($file_path); try { $file_storage = $this->file_storage_provider->get($file_path); } catch (InvalidArgumentException $e) { return; } foreach ($file_storage->classlikes_in_file as $fq_classlike_name) { $this->classlike_storage_provider->remove($fq_classlike_name); $this->classlikes->removeClassLike($fq_classlike_name); } $this->file_storage_provider->remove($file_path); } public function getFunctionStorageForSymbol(string $file_path, string $symbol) : ?FunctionLikeStorage { if (strpos($symbol, '::')) { $symbol = substr($symbol, 0, -2); /** @psalm-suppress ArgumentTypeCoercion */ $method_id = new MethodIdentifier(...explode('::', $symbol)); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if (!$declaring_method_id) { return null; } return $this->methods->getStorage($declaring_method_id); } $function_id = strtolower(substr($symbol, 0, -2)); $file_storage = $this->file_storage_provider->get($file_path); if (isset($file_storage->functions[$function_id])) { return $file_storage->functions[$function_id]; } if (!$function_id) { return null; } return $this->functions->getStorage(null, $function_id); } /** * Get Markup content from Reference */ public function getMarkupContentForSymbolByReference(Reference $reference) : ?PHPMarkdownContent { //Direct Assignment if (is_numeric($reference->symbol[0])) { return new PHPMarkdownContent(preg_replace('/^[^:]*:/', '', $reference->symbol)); } //Class if (strpos($reference->symbol, '::')) { //Class Method if (strpos($reference->symbol, '()')) { $symbol = substr($reference->symbol, 0, -2); /** @psalm-suppress ArgumentTypeCoercion */ $method_id = new MethodIdentifier(...explode('::', $symbol)); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if (!$declaring_method_id) { return null; } $storage = $this->methods->getStorage($declaring_method_id); return new PHPMarkdownContent($storage->getHoverMarkdown(), "{$storage->defining_fqcln}::{$storage->cased_name}", $storage->description); } /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ [, $symbol_name] = explode('::', $reference->symbol); //Class Property if (strpos($reference->symbol, '$') !== \false) { $property_id = preg_replace('/^\\\\/', '', $reference->symbol); /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ [$fq_class_name, $property_name] = explode('::$', $property_id); $class_storage = $this->classlikes->getStorageFor($fq_class_name); //Get Real Properties if (isset($class_storage->declaring_property_ids[$property_name])) { $declaring_property_class = $class_storage->declaring_property_ids[$property_name]; $declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class); if (isset($declaring_class_storage->properties[$property_name])) { $storage = $declaring_class_storage->properties[$property_name]; return new PHPMarkdownContent("{$storage->getInfo()} {$symbol_name}", $reference->symbol, $storage->description); } } //Get Docblock properties if (isset($class_storage->pseudo_property_set_types['$' . $property_name])) { return new PHPMarkdownContent('public ' . (string) $class_storage->pseudo_property_set_types['$' . $property_name] . ' $' . $property_name, $reference->symbol); } //Get Docblock properties if (isset($class_storage->pseudo_property_get_types['$' . $property_name])) { return new PHPMarkdownContent('public ' . (string) $class_storage->pseudo_property_get_types['$' . $property_name] . ' $' . $property_name, $reference->symbol); } return null; } /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ [$fq_classlike_name, $const_name] = explode('::', $reference->symbol); $class_constants = $this->classlikes->getConstantsForClass($fq_classlike_name, ReflectionProperty::IS_PRIVATE); if (!isset($class_constants[$const_name])) { return null; } //Class Constant return new PHPMarkdownContent($class_constants[$const_name]->getHoverMarkdown($const_name), $fq_classlike_name . '::' . $const_name, $class_constants[$const_name]->description); } //Procedural Function if (strpos($reference->symbol, '()')) { $function_id = strtolower(substr($reference->symbol, 0, -2)); $file_storage = $this->file_storage_provider->get($reference->file_path); if (isset($file_storage->functions[$function_id])) { $function_storage = $file_storage->functions[$function_id]; return new PHPMarkdownContent($function_storage->getHoverMarkdown(), $function_id, $function_storage->description); } if (!$function_id) { return null; } $function = $this->functions->getStorage(null, $function_id); return new PHPMarkdownContent($function->getHoverMarkdown(), $function_id, $function->description); } //Procedural Variable if (strpos($reference->symbol, '$') === 0) { $type = VariableFetchAnalyzer::getGlobalType($reference->symbol, $this->analysis_php_version_id); if (!$type->isMixed()) { return new PHPMarkdownContent((string) $type, $reference->symbol); } } try { $storage = $this->classlike_storage_provider->get($reference->symbol); return new PHPMarkdownContent(($storage->abstract ? 'abstract ' : '') . 'class ' . $storage->name, $storage->name, $storage->description); } catch (InvalidArgumentException $e) { //continue on as normal } if (strpos($reference->symbol, '\\')) { $const_name_parts = explode('\\', $reference->symbol); $const_name = array_pop($const_name_parts); $namespace_name = implode('\\', $const_name_parts); $namespace_constants = NamespaceAnalyzer::getConstantsForNamespace($namespace_name, ReflectionProperty::IS_PUBLIC); //Namespace Constant if (isset($namespace_constants[$const_name])) { $type = $namespace_constants[$const_name]; return new PHPMarkdownContent($reference->symbol . ' ' . $type, $reference->symbol); } } else { $file_storage = $this->file_storage_provider->get($reference->file_path); // ? if (isset($file_storage->constants[$reference->symbol])) { return new PHPMarkdownContent('const ' . $reference->symbol . ' ' . $file_storage->constants[$reference->symbol], $reference->symbol); } $type = ConstFetchAnalyzer::getGlobalConstType($this, $reference->symbol, $reference->symbol); //Global Constant if ($type) { return new PHPMarkdownContent('const ' . $reference->symbol . ' ' . $type, $reference->symbol); } } return new PHPMarkdownContent($reference->symbol); } /** * @psalm-suppress PossiblyUnusedMethod * @deprecated will be removed in Psalm 6. use {@see Codebase::getSymbolLocationByReference()} instead */ public function getSymbolInformation(string $file_path, string $symbol) : ?array { if (is_numeric($symbol[0])) { return ['type' => preg_replace('/^[^:]*:/', '', $symbol)]; } try { if (strpos($symbol, '::')) { if (strpos($symbol, '()')) { $symbol = substr($symbol, 0, -2); /** @psalm-suppress ArgumentTypeCoercion */ $method_id = new MethodIdentifier(...explode('::', $symbol)); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if (!$declaring_method_id) { return null; } $storage = $this->methods->getStorage($declaring_method_id); return ['type' => 'getCompletionSignature(), 'description' => $storage->description]; } [, $symbol_name] = explode('::', $symbol); if (strpos($symbol, '$') !== \false) { $storage = $this->properties->getStorage($symbol); return ['type' => 'getInfo() . ' ' . $symbol_name, 'description' => $storage->description]; } [$fq_classlike_name, $const_name] = explode('::', $symbol); $class_constants = $this->classlikes->getConstantsForClass($fq_classlike_name, ReflectionProperty::IS_PRIVATE); if (!isset($class_constants[$const_name])) { return null; } return ['type' => ' $class_constants[$const_name]->description]; } if (strpos($symbol, '()')) { $function_id = strtolower(substr($symbol, 0, -2)); $file_storage = $this->file_storage_provider->get($file_path); if (isset($file_storage->functions[$function_id])) { $function_storage = $file_storage->functions[$function_id]; return ['type' => 'getCompletionSignature(), 'description' => $function_storage->description]; } if (!$function_id) { return null; } $function = $this->functions->getStorage(null, $function_id); return ['type' => 'getCompletionSignature(), 'description' => $function->description]; } if (strpos($symbol, '$') === 0) { $type = VariableFetchAnalyzer::getGlobalType($symbol, $this->analysis_php_version_id); if (!$type->isMixed()) { return ['type' => 'classlike_storage_provider->get($symbol); return ['type' => 'abstract ? 'abstract ' : '') . 'class ' . $storage->name, 'description' => $storage->description]; } catch (InvalidArgumentException $e) { } if (strpos($symbol, '\\')) { $const_name_parts = explode('\\', $symbol); $const_name = array_pop($const_name_parts); $namespace_name = implode('\\', $const_name_parts); $namespace_constants = NamespaceAnalyzer::getConstantsForNamespace($namespace_name, ReflectionProperty::IS_PUBLIC); if (isset($namespace_constants[$const_name])) { $type = $namespace_constants[$const_name]; return ['type' => 'file_storage_provider->get($file_path); if (isset($file_storage->constants[$symbol])) { return ['type' => 'constants[$symbol]]; } $constant = ConstFetchAnalyzer::getGlobalConstType($this, $symbol, $symbol); if ($constant) { return ['type' => 'getMessage()); return null; } } /** * @psalm-suppress PossiblyUnusedMethod * @deprecated will be removed in Psalm 6. use {@see Codebase::getSymbolLocationByReference()} instead */ public function getSymbolLocation(string $file_path, string $symbol) : ?\Psalm\CodeLocation { if (is_numeric($symbol[0])) { $symbol = preg_replace('/:.*/', '', $symbol); $symbol_parts = explode('-', $symbol); $file_contents = $this->getFileContents($file_path); return new Raw($file_contents, $file_path, $this->config->shortenFileName($file_path), (int) $symbol_parts[0], (int) $symbol_parts[1]); } try { if (strpos($symbol, '::')) { if (strpos($symbol, '()')) { $symbol = substr($symbol, 0, -2); /** @psalm-suppress ArgumentTypeCoercion */ $method_id = new MethodIdentifier(...explode('::', $symbol)); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if (!$declaring_method_id) { return null; } $storage = $this->methods->getStorage($declaring_method_id); return $storage->location; } if (strpos($symbol, '$') !== \false) { $storage = $this->properties->getStorage($symbol); return $storage->location; } [$fq_classlike_name, $const_name] = explode('::', $symbol); $class_constants = $this->classlikes->getConstantsForClass($fq_classlike_name, ReflectionProperty::IS_PRIVATE); if (!isset($class_constants[$const_name])) { return null; } return $class_constants[$const_name]->location; } if (strpos($symbol, '()')) { $file_storage = $this->file_storage_provider->get($file_path); $function_id = strtolower(substr($symbol, 0, -2)); if (isset($file_storage->functions[$function_id])) { return $file_storage->functions[$function_id]->location; } if (!$function_id) { return null; } return $this->functions->getStorage(null, $function_id)->location; } return $this->classlike_storage_provider->get($symbol)->location; } catch (UnexpectedValueException $e) { error_log($e->getMessage()); return null; } catch (InvalidArgumentException $e) { return null; } } public function getSymbolLocationByReference(Reference $reference) : ?\Psalm\CodeLocation { if (is_numeric($reference->symbol[0])) { $symbol = preg_replace('/:.*/', '', $reference->symbol); $symbol_parts = explode('-', $symbol); if (!isset($symbol_parts[0]) || !isset($symbol_parts[1])) { return null; } $file_contents = $this->getFileContents($reference->file_path); return new Raw($file_contents, $reference->file_path, $this->config->shortenFileName($reference->file_path), (int) $symbol_parts[0], (int) $symbol_parts[1]); } try { if (strpos($reference->symbol, '::')) { if (strpos($reference->symbol, '()')) { $symbol = substr($reference->symbol, 0, -2); /** @psalm-suppress ArgumentTypeCoercion */ $method_id = new MethodIdentifier(...explode('::', $symbol)); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if (!$declaring_method_id) { return null; } $storage = $this->methods->getStorage($declaring_method_id); return $storage->location; } if (strpos($reference->symbol, '$') !== \false) { $storage = $this->properties->getStorage($reference->symbol); return $storage->location; } /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ [$fq_classlike_name, $const_name] = explode('::', $reference->symbol); $class_constants = $this->classlikes->getConstantsForClass($fq_classlike_name, ReflectionProperty::IS_PRIVATE); if (!isset($class_constants[$const_name])) { return null; } return $class_constants[$const_name]->location; } if (strpos($reference->symbol, '()')) { $file_storage = $this->file_storage_provider->get($reference->file_path); $function_id = strtolower(substr($reference->symbol, 0, -2)); if (isset($file_storage->functions[$function_id])) { return $file_storage->functions[$function_id]->location; } if (!$function_id) { return null; } return $this->functions->getStorage(null, $function_id)->location; } return $this->classlike_storage_provider->get($reference->symbol)->location; } catch (UnexpectedValueException $e) { error_log($e->getMessage()); return null; } catch (InvalidArgumentException $e) { return null; } } /** * @psalm-suppress PossiblyUnusedMethod * @return array{0: string, 1: Range}|null */ public function getReferenceAtPosition(string $file_path, Position $position) : ?array { $ref = $this->getReferenceAtPositionAsReference($file_path, $position); if ($ref === null) { return null; } return [$ref->symbol, $ref->range]; } /** * Get Reference from Position */ public function getReferenceAtPositionAsReference(string $file_path, Position $position) : ?Reference { $is_open = $this->file_provider->isOpen($file_path); if (!$is_open) { throw new UnanalyzedFileException($file_path . ' is not open'); } $file_contents = $this->getFileContents($file_path); $offset = $position->toOffset($file_contents); $reference_maps = $this->analyzer->getMapsForFile($file_path); $reference_start_pos = null; $reference_end_pos = null; $symbol = null; foreach ($reference_maps as $reference_map) { ksort($reference_map); foreach ($reference_map as $start_pos => [$end_pos, $possible_reference]) { if ($offset < $start_pos) { break; } if ($offset > $end_pos) { continue; } $reference_start_pos = $start_pos; $reference_end_pos = $end_pos; $symbol = $possible_reference; } if ($symbol !== null && $reference_start_pos !== null && $reference_end_pos !== null) { break; } } if ($symbol === null || $reference_start_pos === null || $reference_end_pos === null) { return null; } $range = new Range(self::getPositionFromOffset($reference_start_pos, $file_contents), self::getPositionFromOffset($reference_end_pos, $file_contents)); return new Reference($file_path, $symbol, $range); } /** * @return array{0: non-empty-string, 1: int, 2: Range}|null */ public function getFunctionArgumentAtPosition(string $file_path, Position $position) : ?array { $is_open = $this->file_provider->isOpen($file_path); if (!$is_open) { throw new UnanalyzedFileException($file_path . ' is not open'); } $file_contents = $this->getFileContents($file_path); $offset = $position->toOffset($file_contents); [, , $argument_map] = $this->analyzer->getMapsForFile($file_path); $reference = null; $argument_number = null; if (!$argument_map) { return null; } $start_pos = null; $end_pos = null; ksort($argument_map); foreach ($argument_map as $start_pos => [$end_pos, $possible_reference, $possible_argument_number]) { if ($offset < $start_pos) { break; } if ($offset > $end_pos) { continue; } $reference = $possible_reference; $argument_number = $possible_argument_number; } if ($reference === null || $start_pos === null || $end_pos === null || $argument_number === null) { return null; } $range = new Range(self::getPositionFromOffset($start_pos, $file_contents), self::getPositionFromOffset($end_pos, $file_contents)); return [$reference, $argument_number, $range]; } /** * @param non-empty-string $function_symbol */ public function getSignatureInformation(string $function_symbol, string $file_path = null) : ?SignatureInformation { $signature_label = ''; $signature_documentation = null; if (strpos($function_symbol, '::') !== \false) { /** @psalm-suppress ArgumentTypeCoercion */ $method_id = new MethodIdentifier(...explode('::', $function_symbol)); $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); if ($declaring_method_id === null) { return null; } $method_storage = $this->methods->getStorage($declaring_method_id); $params = $method_storage->params; $signature_label = $method_storage->cased_name; $signature_documentation = $method_storage->description; } else { try { if ($file_path) { $function_storage = $this->functions->getStorage(null, strtolower($function_symbol), dirname($file_path), $file_path); } else { $function_storage = $this->functions->getStorage(null, strtolower($function_symbol)); } $params = $function_storage->params; $signature_label = $function_storage->cased_name; $signature_documentation = $function_storage->description; } catch (Exception $exception) { if (InternalCallMapHandler::inCallMap($function_symbol)) { $callables = InternalCallMapHandler::getCallablesFromCallMap($function_symbol); if (!$callables || !$callables[0]->params) { return null; } $params = $callables[0]->params; } else { return null; } } } $signature_label .= '('; $parameters = []; foreach ($params as $i => $param) { $parameter_label = ($param->type ?: 'mixed') . ' $' . $param->name; $parameters[] = new ParameterInformation([strlen($signature_label), strlen($signature_label) + strlen($parameter_label)], $param->description ?? null); $signature_label .= $parameter_label; if ($i < count($params) - 1) { $signature_label .= ', '; } } $signature_label .= ')'; return new SignatureInformation($signature_label, $parameters, $signature_documentation); } /** * @return array{0: string, 1: '->'|'::'|'['|'symbol', 2: int}|null */ public function getCompletionDataAtPosition(string $file_path, Position $position) : ?array { $is_open = $this->file_provider->isOpen($file_path); if (!$is_open) { throw new UnanalyzedFileException($file_path . ' is not open'); } $file_contents = $this->getFileContents($file_path); $offset = $position->toOffset($file_contents); $literal_part = $this->getBeginedLiteralPart($file_path, $position); $begin_literal_offset = $offset - strlen($literal_part); [$reference_map, $type_map] = $this->analyzer->getMapsForFile($file_path); if (!$reference_map && !$type_map) { return null; } krsort($type_map); foreach ($type_map as $start_pos => [$end_pos_excluding_whitespace, $possible_type]) { if ($offset < $start_pos) { continue; } /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ $num_whitespace_bytes = preg_match('/\\G\\s+/', $file_contents, $matches, 0, $end_pos_excluding_whitespace) ? strlen($matches[0]) : 0; $end_pos = $end_pos_excluding_whitespace + $num_whitespace_bytes; if ($offset - $end_pos === 1) { $candidate_gap = substr($file_contents, $end_pos, 1); if ($candidate_gap === '[') { $gap = $candidate_gap; $recent_type = $possible_type; if ($recent_type === 'mixed') { return null; } return [$recent_type, $gap, $offset]; } } if ($begin_literal_offset - $end_pos === 2) { $candidate_gap = substr($file_contents, $end_pos, 2); if ($candidate_gap === '->' || $candidate_gap === '::') { $gap = $candidate_gap; $recent_type = $possible_type; if ($recent_type === 'mixed') { return null; } return [$recent_type, $gap, $offset]; } } } foreach ($reference_map as $start_pos => [$end_pos, $possible_reference]) { if ($offset < $start_pos) { continue; } // If the reference precedes a "::" then treat it as a class reference. if ($offset - $end_pos === 2 && substr($file_contents, $end_pos, 2) === '::') { return [$possible_reference, '::', $offset]; } if ($offset <= $end_pos && substr($file_contents, $begin_literal_offset - 2, 2) === '::') { $class_name = explode('::', $possible_reference)[0]; return [$class_name, '::', $offset]; } // Only continue for references that are partial / don't exist. if ($possible_reference[0] !== '*') { continue; } if ($offset - $end_pos === 0) { $recent_type = $possible_reference; return [$recent_type, 'symbol', $offset]; } } return null; } public function getBeginedLiteralPart(string $file_path, Position $position) : string { $is_open = $this->file_provider->isOpen($file_path); if (!$is_open) { throw new UnanalyzedFileException($file_path . ' is not open'); } $file_contents = $this->getFileContents($file_path); $offset = $position->toOffset($file_contents); preg_match('/\\$?\\w+$/', substr($file_contents, 0, $offset), $matches); return $matches[0] ?? ''; } public function getTypeContextAtPosition(string $file_path, Position $position) : ?Union { $file_contents = $this->getFileContents($file_path); $offset = $position->toOffset($file_contents); [$reference_map, $type_map, $argument_map] = $this->analyzer->getMapsForFile($file_path); if (!$reference_map && !$type_map && !$argument_map) { return null; } foreach ($argument_map as $start_pos => [$end_pos, $function, $argument_num]) { if ($offset < $start_pos || $offset > $end_pos) { continue; } // First parameter to a function-like $function_storage = $this->getFunctionStorageForSymbol($file_path, $function . '()'); if (!$function_storage || !$function_storage->params || !isset($function_storage->params[$argument_num])) { return null; } return $function_storage->params[$argument_num]->type; } return null; } /** * @param list $allow_visibilities * @param list $ignore_fq_class_names * @return list */ public function getCompletionItemsForClassishThing(string $type_string, string $gap, bool $snippets_supported = \false, array $allow_visibilities = null, array $ignore_fq_class_names = []) : array { if ($allow_visibilities === null) { $allow_visibilities = [ClassLikeAnalyzer::VISIBILITY_PUBLIC, ClassLikeAnalyzer::VISIBILITY_PROTECTED, ClassLikeAnalyzer::VISIBILITY_PRIVATE]; } $allow_visibilities[] = null; $completion_items = []; $type = \Psalm\Type::parseString($type_string); foreach ($type->getAtomicTypes() as $atomic_type) { if ($atomic_type instanceof TNamedObject) { try { $class_storage = $this->classlike_storage_provider->get($atomic_type->value); $method_storages = []; foreach ($class_storage->declaring_method_ids as $declaring_method_id) { try { $method_storages[] = $this->methods->getStorage($declaring_method_id); } catch (UnexpectedValueException $e) { error_log($e->getMessage()); } } if ($gap === '->') { $method_storages += $class_storage->pseudo_methods; } if ($gap === '::') { $method_storages += $class_storage->pseudo_static_methods; } foreach ($method_storages as $method_storage) { if (!in_array($method_storage->visibility, $allow_visibilities)) { continue; } if ($method_storage->is_static || $gap === '->') { $completion_item = new CompletionItem($method_storage->cased_name, CompletionItemKind::METHOD, $method_storage->getCompletionSignature(), $method_storage->description, (string) $method_storage->visibility, $method_storage->cased_name, $method_storage->cased_name, null, null, new Command('Trigger parameter hints', 'editor.action.triggerParameterHints'), null, 2); if ($snippets_supported && count($method_storage->params) > 0) { $completion_item->insertText .= '($0)'; $completion_item->insertTextFormat = InsertTextFormat::SNIPPET; } else { $completion_item->insertText .= '()'; } $completion_items[] = $completion_item; } } if ($gap === '->') { $pseudo_property_types = []; foreach ($class_storage->pseudo_property_get_types as $property_name => $type) { $pseudo_property_types[$property_name] = new CompletionItem( str_replace('$', '', $property_name), CompletionItemKind::PROPERTY, $type->__toString(), null, '1', //sort text str_replace('$', '', $property_name), str_replace('$', '', $property_name) ); } foreach ($class_storage->pseudo_property_set_types as $property_name => $type) { $pseudo_property_types[$property_name] = new CompletionItem(str_replace('$', '', $property_name), CompletionItemKind::PROPERTY, $type->__toString(), null, '1', str_replace('$', '', $property_name), str_replace('$', '', $property_name)); } $completion_items = [...$completion_items, ...array_values($pseudo_property_types)]; } foreach ($class_storage->declaring_property_ids as $property_name => $declaring_class) { try { $property_storage = $this->properties->getStorage($declaring_class . '::$' . $property_name); } catch (UnexpectedValueException $e) { error_log($e->getMessage()); continue; } if (!in_array($property_storage->visibility, $allow_visibilities)) { continue; } if ($property_storage->is_static === ($gap === '::')) { $completion_items[] = new CompletionItem($property_name, CompletionItemKind::PROPERTY, $property_storage->getInfo(), $property_storage->description, (string) $property_storage->visibility, $property_name, ($gap === '::' ? '$' : '') . $property_name); } } foreach ($class_storage->constants as $const_name => $const) { $completion_items[] = new CompletionItem($const_name, CompletionItemKind::VARIABLE, 'const ' . $const_name, $const->description, null, $const_name, $const_name); } if ($gap === '->') { foreach ($class_storage->namedMixins as $mixin) { if (in_array($mixin->value, $ignore_fq_class_names)) { continue; } $mixin_completion_items = $this->getCompletionItemsForClassishThing($mixin->value, $gap, $snippets_supported, [ClassLikeAnalyzer::VISIBILITY_PUBLIC], [$type_string, ...$ignore_fq_class_names]); $completion_items = [...$completion_items, ...$mixin_completion_items]; } } } catch (Exception $e) { error_log($e->getMessage()); continue; } } } return $completion_items; } /** * @param list $items * @return list */ public function filterCompletionItemsByBeginLiteralPart(array $items, string $literal_part) : array { if (!$literal_part) { return $items; } $res = []; foreach ($items as $item) { if ($item->insertText && strpos($item->insertText, $literal_part) === 0) { $res[] = $item; } } return $res; } /** * @return list */ public function getCompletionItemsForPartialSymbol(string $type_string, int $offset, string $file_path) : array { $fq_suggestion = \false; if (($type_string[1] ?? '') === '\\') { $fq_suggestion = \true; } $matching_classlike_names = $this->classlikes->getMatchingClassLikeNames($type_string); $completion_items = []; $file_storage = $this->file_storage_provider->get($file_path); $aliases = null; foreach ($file_storage->classlikes_in_file as $fq_class_name => $_) { try { $class_storage = $this->classlike_storage_provider->get($fq_class_name); } catch (Exception $e) { continue; } if (!$class_storage->stmt_location) { continue; } if ($offset > $class_storage->stmt_location->raw_file_start && $offset < $class_storage->stmt_location->raw_file_end) { $aliases = $class_storage->aliases; break; } } if (!$aliases) { foreach ($file_storage->namespace_aliases as $namespace_start => $namespace_aliases) { if ($namespace_start < $offset) { $aliases = $namespace_aliases; break; } } if (!$aliases) { $aliases = $file_storage->aliases; } } foreach ($matching_classlike_names as $fq_class_name) { $extra_edits = []; $insertion_text = \Psalm\Type::getStringFromFQCLN($fq_class_name, $aliases && $aliases->namespace ? $aliases->namespace : null, $aliases->uses_flipped ?? [], null); if ($aliases && !$fq_suggestion && $aliases->namespace && $insertion_text === '\\' . $fq_class_name && $aliases->namespace_first_stmt_start) { $file_contents = $this->getFileContents($file_path); $class_name = preg_replace('/^.*\\\\/', '', $fq_class_name, 1); if ($aliases->uses_end) { $position = self::getPositionFromOffset($aliases->uses_end, $file_contents); $extra_edits[] = new TextEdit(new Range($position, $position), "\n" . 'use ' . $fq_class_name . ';'); } else { $position = self::getPositionFromOffset($aliases->namespace_first_stmt_start, $file_contents); $extra_edits[] = new TextEdit(new Range($position, $position), 'use ' . $fq_class_name . ';' . "\n" . "\n"); } $insertion_text = $class_name; } try { $class_storage = $this->classlike_storage_provider->get($fq_class_name); $description = $class_storage->description; } catch (Exception $e) { $description = null; } $completion_items[] = new CompletionItem($fq_class_name, CompletionItemKind::CLASS_, null, $description, null, $fq_class_name, $insertion_text, null, $extra_edits); } $functions = $this->functions->getMatchingFunctionNames($type_string, $offset, $file_path, $this); $namespace_map = []; if ($aliases) { $namespace_map += $aliases->uses_flipped; if ($aliases->namespace) { $namespace_map[$aliases->namespace] = ''; } } // Sort the map by longest first, so we replace most specific // used namespaces first. ksort($namespace_map); $namespace_map = array_reverse($namespace_map); foreach ($functions as $function_lowercase => $function) { // Transform FQFN relative to all uses namespaces $function_name = $function->cased_name; if (!$function_name) { continue; } $in_namespace_map = \false; foreach ($namespace_map as $namespace_name => $namespace_alias) { if (strpos($function_lowercase, $namespace_name . '\\') === 0) { $function_name = $namespace_alias . '\\' . substr($function_name, strlen($namespace_name) + 1); $in_namespace_map = \true; } } // If the function is not use'd, and it's not a global function // prepend it with a backslash. if (!$in_namespace_map && strpos($function_name, '\\') !== \false) { $function_name = '\\' . $function_name; } $completion_items[] = new CompletionItem($function_name, CompletionItemKind::FUNCTION, $function->getCompletionSignature(), $function->description, null, $function_name, $function_name . (count($function->params) !== 0 ? '($0)' : '()'), null, null, new Command('Trigger parameter hints', 'editor.action.triggerParameterHints'), null, 2); } return $completion_items; } /** * @return list */ public function getCompletionItemsForType(Union $type) : array { $completion_items = []; foreach ($type->getAtomicTypes() as $atomic_type) { if ($atomic_type instanceof TBool) { $bools = (string) $atomic_type === 'bool' ? ['true', 'false'] : [(string) $atomic_type]; foreach ($bools as $property_name) { $completion_items[] = new CompletionItem($property_name, CompletionItemKind::VALUE, 'bool', null, null, null, $property_name); } } elseif ($atomic_type instanceof TLiteralString) { $completion_items[] = new CompletionItem($atomic_type->value, CompletionItemKind::VALUE, $atomic_type->getId(), null, null, null, "'{$atomic_type->value}'"); } elseif ($atomic_type instanceof TLiteralInt) { $completion_items[] = new CompletionItem((string) $atomic_type->value, CompletionItemKind::VALUE, $atomic_type->getId(), null, null, null, (string) $atomic_type->value); } elseif ($atomic_type instanceof TClassConstant) { $const = $atomic_type->fq_classlike_name . '::' . $atomic_type->const_name; $completion_items[] = new CompletionItem($const, CompletionItemKind::VALUE, $atomic_type->getId(), null, null, null, $const); } } return $completion_items; } /** * @return list */ public function getCompletionItemsForArrayKeys(string $type_string) : array { $completion_items = []; $type = \Psalm\Type::parseString($type_string); foreach ($type->getAtomicTypes() as $atomic_type) { if ($atomic_type instanceof TKeyedArray) { foreach ($atomic_type->properties as $property_name => $property) { $completion_items[] = new CompletionItem((string) $property_name, CompletionItemKind::PROPERTY, (string) $property, null, null, null, "'{$property_name}'"); } } } return $completion_items; } private static function getPositionFromOffset(int $offset, string $file_contents) : Position { $file_contents = substr($file_contents, 0, $offset); $offsetLength = $offset - strlen($file_contents); //PHP 8.0: Argument #3 ($offset) must be contained in argument #1 ($haystack) if (($textlen = strlen($file_contents)) < $offsetLength) { $offsetLength = $textlen; } $before_newline_count = strrpos($file_contents, "\n", $offsetLength); return new Position(substr_count($file_contents, "\n"), $offset - (int) $before_newline_count - 1); } public function addTemporaryFileChanges(string $file_path, string $new_content, ?int $version = null) : void { $this->file_provider->addTemporaryFileChanges($file_path, $new_content, $version); } public function removeTemporaryFileChanges(string $file_path) : void { $this->file_provider->removeTemporaryFileChanges($file_path); } /** * Checks if type is a subtype of other * * Given two types, checks if `$input_type` is a subtype of `$container_type`. * If you consider `Union` as a set of types, this will tell you * if `$input_type` is fully contained in `$container_type`, * * $input_type ⊆ $container_type * * Useful for emitting issues like InvalidArgument, where argument at the call site * should be a subset of the function parameter type. */ public function isTypeContainedByType(Union $input_type, Union $container_type) : bool { return UnionTypeComparator::isContainedBy($this, $input_type, $container_type); } /** * Checks if type has any part that is a subtype of other * * Given two types, checks if *any part* of `$input_type` is a subtype of `$container_type`. * If you consider `Union` as a set of types, this will tell you if intersection * of `$input_type` with `$container_type` is not empty. * * $input_type ∩ $container_type ≠ ∅ , e.g. they are not disjoint. * * Useful for emitting issues like PossiblyInvalidArgument, where argument at the call * site should be a subtype of the function parameter type, but it's has some types that are * not a subtype of the required type. */ public function canTypeBeContainedByType(Union $input_type, Union $container_type) : bool { return UnionTypeComparator::canBeContainedBy($this, $input_type, $container_type); } /** * Extracts key and value types from a traversable object (or iterable) * * Given an iterable type (*but not TArray*) returns a tuple of it's key/value types. * First element of the tuple holds key type, second has the value type. * * Example: * ```php * $codebase->getKeyValueParamsForTraversableObject(Type::parseString('iterable')) * // returns [Union(TInt), Union(TString)] * ``` * * @return array{Union, Union} */ public function getKeyValueParamsForTraversableObject(Atomic $type) : array { $key_type = null; $value_type = null; ForeachAnalyzer::getKeyValueParamsForTraversableObject($type, $this, $key_type, $value_type); return [$key_type ?? \Psalm\Type::getMixed(), $value_type ?? \Psalm\Type::getMixed()]; } /** * @param array $phantom_classes * @psalm-suppress PossiblyUnusedMethod part of the public API */ public function queueClassLikeForScanning(string $fq_classlike_name, bool $analyze_too = \false, bool $store_failure = \true, array $phantom_classes = []) : void { $this->scanner->queueClassLikeForScanning($fq_classlike_name, $analyze_too, $store_failure, $phantom_classes); } /** * @param array $taints * @psalm-suppress PossiblyUnusedMethod */ public function addTaintSource(Union $expr_type, string $taint_id, array $taints = TaintKindGroup::ALL_INPUT, ?\Psalm\CodeLocation $code_location = null) : Union { if (!$this->taint_flow_graph) { return $expr_type; } $source = new TaintSource($taint_id, $taint_id, $code_location, null, $taints); $this->taint_flow_graph->addSource($source); return $expr_type->addParentNodes([$source->id => $source]); } /** * @param array $taints * @psalm-suppress PossiblyUnusedMethod */ public function addTaintSink(string $taint_id, array $taints = TaintKindGroup::ALL_INPUT, ?\Psalm\CodeLocation $code_location = null) : void { if (!$this->taint_flow_graph) { return; } $sink = new TaintSink($taint_id, $taint_id, $code_location, null, $taints); $this->taint_flow_graph->addSink($sink); } public function getMinorAnalysisPhpVersion() : int { return self::transformPhpVersionId($this->analysis_php_version_id % 10000, 100); } public function getMajorAnalysisPhpVersion() : int { return self::transformPhpVersionId($this->analysis_php_version_id, 10000); } public static function transformPhpVersionId(int $php_version_id, int $div) : int { return intdiv($php_version_id, $div); } } }>> $existingIssues * @psalm-pure */ public static function countTotalIssues(array $existingIssues) : int { $totalIssues = 0; foreach ($existingIssues as $existingIssue) { $totalIssues += array_reduce( $existingIssue, /** * @param array{o:int, s:array} $existingIssue */ static fn(int $carry, array $existingIssue): int => $carry + $existingIssue['o'], 0 ); } return $totalIssues; } /** * @param array> $issues */ public static function create(FileProvider $fileProvider, string $baselineFile, array $issues, bool $include_php_versions) : void { $groupedIssues = self::countIssueTypesByFile($issues); self::writeToFile($fileProvider, $baselineFile, $groupedIssues, $include_php_versions); } /** * @return array}>> * @throws ConfigException */ public static function read(FileProvider $fileProvider, string $baselineFile) : array { if (!$fileProvider->fileExists($baselineFile)) { throw new ConfigException("{$baselineFile} does not exist or is not readable"); } $xmlSource = $fileProvider->getContents($baselineFile); if ($xmlSource === '') { throw new ConfigException('Baseline file is empty'); } $baselineDoc = new DOMDocument(); $baselineDoc->loadXML($xmlSource, LIBXML_NOBLANKS); $filesElement = $baselineDoc->getElementsByTagName('files'); if ($filesElement->length === 0) { throw new ConfigException('Baseline file does not contain '); } $files = []; /** @var DOMElement $filesElement */ $filesElement = $filesElement[0]; foreach ($filesElement->getElementsByTagName('file') as $file) { $fileName = $file->getAttribute('src'); $fileName = str_replace('\\', '/', $fileName); $files[$fileName] = []; foreach ($file->childNodes as $issue) { if (!$issue instanceof DOMElement) { continue; } $issueType = $issue->tagName; $files[$fileName][$issueType] = ['o' => 0, 's' => []]; $codeSamples = $issue->getElementsByTagName('code'); foreach ($codeSamples as $codeSample) { $files[$fileName][$issueType]['o'] += 1; $files[$fileName][$issueType]['s'][] = str_replace("\r\n", "\n", trim($codeSample->textContent)); } // TODO: Remove in Psalm 6 $occurrencesAttr = $issue->getAttribute('occurrences'); if ($occurrencesAttr !== '') { $files[$fileName][$issueType]['o'] = (int) $occurrencesAttr; } } } return $files; } /** * @param array> $issues * @return array}>> * @throws ConfigException */ public static function update(FileProvider $fileProvider, string $baselineFile, array $issues, bool $include_php_versions) : array { $existingIssues = self::read($fileProvider, $baselineFile); $newIssues = self::countIssueTypesByFile($issues); foreach ($existingIssues as $file => &$existingIssuesCount) { if (!isset($newIssues[$file])) { unset($existingIssues[$file]); continue; } foreach ($existingIssuesCount as $issueType => $existingIssueType) { if (!isset($newIssues[$file][$issueType])) { unset($existingIssuesCount[$issueType]); continue; } $existingIssuesCount[$issueType]['o'] = min($existingIssueType['o'], $newIssues[$file][$issueType]['o']); $existingIssuesCount[$issueType]['s'] = array_intersect($existingIssueType['s'], $newIssues[$file][$issueType]['s']); } } $groupedIssues = array_filter($existingIssues); self::writeToFile($fileProvider, $baselineFile, $groupedIssues, $include_php_versions); return $groupedIssues; } /** * @param array> $issues * @return array}>> */ private static function countIssueTypesByFile(array $issues) : array { if ($issues === []) { return []; } $groupedIssues = array_reduce( array_merge(...array_values($issues)), /** * @param array}>> $carry * @return array}>> */ static function (array $carry, IssueData $issue) : array { if ($issue->severity !== \Psalm\Config::REPORT_ERROR) { return $carry; } $fileName = $issue->file_name; $fileName = str_replace('\\', '/', $fileName); $issueType = $issue->type; if (!isset($carry[$fileName])) { $carry[$fileName] = []; } if (!isset($carry[$fileName][$issueType])) { $carry[$fileName][$issueType] = ['o' => 0, 's' => []]; } ++$carry[$fileName][$issueType]['o']; $carry[$fileName][$issueType]['s'][] = $issue->selected_text; return $carry; }, [] ); // Sort files first ksort($groupedIssues); foreach ($groupedIssues as &$issues) { ksort($issues); } unset($issues); return $groupedIssues; } /** * @param array}>> $groupedIssues */ private static function writeToFile(FileProvider $fileProvider, string $baselineFile, array $groupedIssues, bool $include_php_versions) : void { $baselineDoc = new DOMDocument('1.0', 'UTF-8'); $filesNode = $baselineDoc->createElement('files'); $filesNode->setAttribute('psalm-version', \PSALM_VERSION); if ($include_php_versions) { $extensions = [...get_loaded_extensions(), ...get_loaded_extensions(\true)]; usort($extensions, 'strnatcasecmp'); $filesNode->setAttribute('php-version', implode(';' . "\n\t", [...['php:' . PHP_VERSION], ...array_map(static fn(string $extension): string => $extension . ':' . phpversion($extension), $extensions)])); } foreach ($groupedIssues as $file => $issueTypes) { $fileNode = $baselineDoc->createElement('file'); $fileNode->setAttribute('src', $file); foreach ($issueTypes as $issueType => $existingIssueType) { $issueNode = $baselineDoc->createElement($issueType); sort($existingIssueType['s']); foreach ($existingIssueType['s'] as $selection) { $codeNode = $baselineDoc->createElement('code'); $textContent = trim($selection); if ($textContent !== htmlspecialchars($textContent)) { $codeNode->appendChild($baselineDoc->createCDATASection($textContent)); } else { $codeNode->textContent = trim($textContent); } $issueNode->appendChild($codeNode); } $fileNode->appendChild($issueNode); } $filesNode->appendChild($fileNode); } $baselineDoc->appendChild($filesNode); $baselineDoc->formatOutput = \true; $xml = preg_replace_callback( '/)\\n/', /** * @param string[] $matches */ static fn(array $matches): string => sprintf("saveXML() ); if ($xml === null) { throw new RuntimeException('Failed to reformat opening attributes!'); } $fileProvider->setContents($baselineFile, $xml); } } class_name = $class_name; $this->const_name = $const_name; } } file_start = (int) $stmt->getAttribute('startFilePos'); /** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */ $this->file_end = (int) $stmt->getAttribute('endFilePos'); $this->raw_file_start = $this->file_start; $this->raw_file_end = $this->file_end; $this->file_path = $file_source->getFilePath(); $this->file_name = $file_source->getFileName(); $this->single_line = $single_line; $this->regex_type = $regex_type; $this->previous_location = $previous_location; $this->text = $selected_text; /** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */ $doc_comment = $stmt->getDocComment(); $this->docblock_start = $doc_comment ? $doc_comment->getStartFilePos() : null; $this->docblock_start_line_number = $doc_comment ? $doc_comment->getStartLine() : null; $this->preview_start = $this->docblock_start ?: $this->file_start; /** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */ $this->raw_line_number = $stmt->getLine(); $this->docblock_line_number = $comment_line; } /** * @psalm-suppress PossiblyUnusedMethod Part of public API * @return static */ public function setCommentLine(?int $line) : self { if ($line === $this->docblock_line_number) { return $this; } $cloned = clone $this; $cloned->docblock_line_number = $line; return $cloned; } /** * @psalm-external-mutation-free * @psalm-suppress InaccessibleProperty Mainly used for caching */ private function calculateRealLocation() : void { if ($this->have_recalculated) { return; } $this->have_recalculated = \true; $this->selection_start = $this->file_start; $this->selection_end = $this->file_end + 1; $project_analyzer = ProjectAnalyzer::getInstance(); $codebase = $project_analyzer->getCodebase(); /** @psalm-suppress ImpureMethodCall */ $file_contents = $codebase->getFileContents($this->file_path); $file_length = strlen($file_contents); $search_limit = $this->single_line ? $this->selection_start : $this->selection_end; if ($search_limit <= $file_length) { $preview_end = strpos($file_contents, "\n", $search_limit); } else { $preview_end = \false; } // if the string didn't contain a newline if ($preview_end === \false) { $preview_end = $this->selection_end; } $this->preview_end = $preview_end; if ($this->docblock_line_number && $this->docblock_start_line_number && $this->preview_start < $this->selection_start) { $preview_lines = explode("\n", mb_strcut($file_contents, $this->preview_start, $this->selection_start - $this->preview_start - 1)); $preview_offset = 0; $comment_line_offset = $this->docblock_line_number - $this->docblock_start_line_number; for ($i = 0; $i < $comment_line_offset; ++$i) { $preview_offset += strlen($preview_lines[$i]) + 1; } if (!isset($preview_lines[$i])) { throw new Exception('Should have offset'); } $key_line = $preview_lines[$i]; $indentation = (int) strpos($key_line, '@'); $key_line = trim(preg_replace('@\\**/\\s*@', '', mb_strcut($key_line, $indentation))); $this->selection_start = $preview_offset + $indentation + $this->preview_start; $this->selection_end = $this->selection_start + strlen($key_line); } if ($this->regex_type !== null) { switch ($this->regex_type) { case self::VAR_TYPE: $regex = '/@(?:psalm-)?var[ \\t]+' . CommentAnalyzer::TYPE_REGEX . '/'; break; case self::FUNCTION_RETURN_TYPE: $regex = '/\\:\\s+(\\??\\s*[A-Za-z0-9_\\\\\\[\\]]+)/'; break; case self::FUNCTION_PARAM_TYPE: $regex = '/^(\\??\\s*[A-Za-z0-9_\\\\\\[\\]]+)\\s/'; break; case self::FUNCTION_PHPDOC_RETURN_TYPE: $regex = '/@(?:psalm-)?return[ \\t]+' . CommentAnalyzer::TYPE_REGEX . '/'; break; case self::FUNCTION_PHPDOC_METHOD: $regex = '/@(?:psalm-)?method[ \\t]+(.*)/'; break; case self::FUNCTION_PHPDOC_PARAM_TYPE: $regex = '/@(?:psalm-)?param[ \\t]+' . CommentAnalyzer::TYPE_REGEX . '/'; break; case self::FUNCTION_PARAM_VAR: $regex = '/(\\$[^ ]*)/'; break; case self::CATCH_VAR: $regex = '/(\\$[^ ^\\)]*)/'; break; default: throw new UnexpectedValueException('Unrecognised regex type ' . $this->regex_type); } $preview_snippet = mb_strcut($file_contents, $this->selection_start, $this->selection_end - $this->selection_start); if ($this->text) { $regex = '/(' . str_replace(',', ',[ ]*', preg_quote($this->text, '/')) . ')/'; } if (preg_match($regex, $preview_snippet, $matches, PREG_OFFSET_CAPTURE)) { if (!isset($matches[1]) || $matches[1][1] === -1) { throw new LogicException("Failed to match anything to 1st capturing group, " . "or regex doesn't contain 1st capturing group, regex type " . $this->regex_type); } $this->selection_start = $this->selection_start + $matches[1][1]; $this->selection_end = $this->selection_start + strlen($matches[1][0]); } } // reset preview start to beginning of line $this->preview_start = (int) strrpos($file_contents, "\n", min($this->preview_start, $this->selection_start) - strlen($file_contents)) + 1; $this->selection_start = max($this->preview_start, $this->selection_start); $this->selection_end = min($this->preview_end, $this->selection_end); if ($this->preview_end - $this->selection_end > 200) { $this->preview_end = (int) strrpos($file_contents, "\n", $this->selection_end + 200 - strlen($file_contents)); // if the line is over 200 characters long if ($this->preview_end < $this->selection_end) { $this->preview_end = $this->selection_end + 50; } } $this->snippet = mb_strcut($file_contents, $this->preview_start, $this->preview_end - $this->preview_start); // text is within snippet. It's 50% faster to cut it from the snippet than from the full text $selection_length = $this->selection_end - $this->selection_start; $this->text = mb_strcut($this->snippet, $this->selection_start - $this->preview_start, $selection_length); // reset preview start to beginning of line if ($file_contents !== '') { $this->column_from = $this->selection_start - (int) strrpos($file_contents, "\n", $this->selection_start - strlen($file_contents)); } else { $this->column_from = $this->selection_start; } $newlines = substr_count($this->text, "\n"); if ($newlines) { $last_newline_pos = strrpos($file_contents, "\n", $this->selection_end - strlen($file_contents) - 1); $this->column_to = $this->selection_end - (int) $last_newline_pos; } else { $this->column_to = $this->column_from + strlen($this->text); } $this->end_line_number = $this->getLineNumber() + $newlines; } public function getLineNumber() : int { return $this->docblock_line_number ?: $this->raw_line_number; } public function getEndLineNumber() : int { $this->calculateRealLocation(); return $this->end_line_number; } public function getSnippet() : string { $this->calculateRealLocation(); return $this->snippet; } public function getSelectedText() : string { $this->calculateRealLocation(); return (string) $this->text; } public function getColumn() : int { $this->calculateRealLocation(); return $this->column_from; } public function getEndColumn() : int { $this->calculateRealLocation(); return $this->column_to; } /** * @return array{0: int, 1: int} */ public function getSelectionBounds() : array { $this->calculateRealLocation(); return [$this->selection_start, $this->selection_end]; } /** * @return array{0: int, 1: int} */ public function getSnippetBounds() : array { $this->calculateRealLocation(); return [$this->preview_start, $this->preview_end]; } public function getHash() : string { return $this->file_name . ' ' . $this->raw_file_start . $this->raw_file_end; } public function getShortSummary() : string { return $this->file_name . ':' . $this->getLineNumber() . ':' . $this->getColumn(); } } start = $start; $this->end = $end; $this->insertion_text = $insertion_text; $this->preserve_indentation = $preserve_indentation; $this->remove_trailing_newline = $remove_trailing_newline; } public function getKey() : string { return $this->start === $this->end ? $this->start . ':' . sha1($this->insertion_text) : $this->start . ':' . $this->end; } public function transform(string $existing_contents) : string { if ($this->preserve_indentation) { $newline_pos = strrpos($existing_contents, "\n", $this->start - strlen($existing_contents)); $newline_pos = $newline_pos !== \false ? $newline_pos + 1 : 0; $indentation = substr($existing_contents, $newline_pos, $this->start - $newline_pos); if (trim($indentation) === '') { $this->insertion_text .= $indentation; } } if ($this->remove_trailing_newline && strlen($existing_contents) > $this->end && $existing_contents[$this->end] === "\n") { $newline_pos = strrpos($existing_contents, "\n", $this->start - strlen($existing_contents)); $newline_pos = $newline_pos !== \false ? $newline_pos + 1 : 0; $indentation = substr($existing_contents, $newline_pos, $this->start - $newline_pos); if (trim($indentation) === '') { $this->start -= strlen($indentation); $this->end++; } } return substr($existing_contents, 0, $this->start) . $this->insertion_text . substr($existing_contents, $this->end); } } > $template_type_map */ public static function parseString(string $type_string, ?int $analysis_php_version_id = null, array $template_type_map = []) : Union { return TypeParser::parseTokens(TypeTokenizer::tokenize($type_string), $analysis_php_version_id, $template_type_map); } public static function getFQCLNFromString(string $class, \Psalm\Aliases $aliases) : string { if ($class === '') { throw new InvalidArgumentException('$class cannot be empty'); } if ($class[0] === '\\') { return substr($class, 1); } $imported_namespaces = $aliases->uses; if (strpos($class, '\\') !== \false) { $class_parts = explode('\\', $class); $first_namespace = array_shift($class_parts); if (isset($imported_namespaces[strtolower($first_namespace)])) { return $imported_namespaces[strtolower($first_namespace)] . '\\' . implode('\\', $class_parts); } } elseif (isset($imported_namespaces[strtolower($class)])) { return $imported_namespaces[strtolower($class)]; } $namespace = $aliases->namespace; return ($namespace ? $namespace . '\\' : '') . $class; } /** * @param array $aliased_classes * @psalm-pure */ public static function getStringFromFQCLN(string $value, ?string $namespace, array $aliased_classes, ?string $this_class, bool $allow_self = \false, bool $is_static = \false) : string { if ($allow_self && $value === $this_class) { if ($is_static) { return 'static'; } return 'self'; } if (isset($aliased_classes[strtolower($value)])) { return $aliased_classes[strtolower($value)]; } if ($namespace && stripos($value, $namespace . '\\') === 0) { $candidate = preg_replace('/^' . preg_quote($namespace . '\\') . '/i', '', $value); $candidate_parts = explode('\\', $candidate); if (!isset($aliased_classes[strtolower($candidate_parts[0])])) { return $candidate; } } elseif (!$namespace && strpos($value, '\\') === \false) { return $value; } if (strpos($value, '\\')) { $parts = explode('\\', $value); $suffix = array_pop($parts); while ($parts) { $left = implode('\\', $parts); if (isset($aliased_classes[strtolower($left)])) { return $aliased_classes[strtolower($left)] . '\\' . $suffix; } $suffix = array_pop($parts) . '\\' . $suffix; } } return '\\' . $value; } /** * @psalm-pure */ public static function getInt(bool $from_calculation = \false, ?int $value = null) : Union { if ($value !== null) { return new Union([new TLiteralInt($value)], ['from_calculation' => $from_calculation]); } return new Union([new TInt()], ['from_calculation' => $from_calculation]); } /** * @psalm-pure */ public static function getIntRange(?int $min, ?int $max) : Union { return new Union([new TIntRange($min, $max)]); } /** * @psalm-pure */ public static function getLowercaseString() : Union { $type = new TLowercaseString(); return new Union([$type]); } /** * @psalm-pure */ public static function getNonEmptyLowercaseString() : Union { $type = new TNonEmptyLowercaseString(); return new Union([$type]); } /** * @psalm-pure */ public static function getNonEmptyString() : Union { $type = new TNonEmptyString(); return new Union([$type]); } /** * @psalm-pure */ public static function getNonFalsyString() : Union { $type = new TNonFalsyString(); return new Union([$type]); } /** * @psalm-pure */ public static function getNumeric() : Union { $type = new TNumeric(); return new Union([$type]); } /** * @psalm-pure */ public static function getNumericString() : Union { $type = new TNumericString(); return new Union([$type]); } /** * @param int|string $value * @return TLiteralString|TLiteralInt */ public static function getLiteral($value) : Atomic { if (is_int($value)) { return new TLiteralInt($value); } return TLiteralString::make($value); } public static function getString(?string $value = null) : Union { return new Union([$value === null ? new TString() : self::getAtomicStringFromLiteral($value)]); } /** @return TLiteralString|TNonEmptyString */ public static function getAtomicStringFromLiteral(string $value, bool $from_docblock = \false) : TString { $config = \Psalm\Config::getInstance(); $event = new StringInterpreterEvent($value, ProjectAnalyzer::getInstance()->getCodebase()); $type = $config->eventDispatcher->dispatchStringInterpreter($event); if (!$type) { if (strlen($value) < $config->max_string_length) { $type = new TLiteralString($value, $from_docblock); } else { $type = new TNonEmptyString($from_docblock); } } return $type; } /** * @psalm-pure */ public static function getSingleLetter() : Union { $type = new TSingleLetter(); return new Union([$type]); } /** * @psalm-pure */ public static function getClassString(string $extends = 'object') : Union { return new Union([new TClassString($extends, $extends === 'object' ? null : new TNamedObject($extends))]); } /** * @psalm-pure */ public static function getLiteralClassString(string $class_type, bool $definite_class = \false) : Union { $type = new TLiteralClassString($class_type, $definite_class); return new Union([$type]); } /** * @psalm-pure */ public static function getNull(bool $from_docblock = \false) : Union { $type = new TNull($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getMixed(bool $from_loop_isset = \false, bool $from_docblock = \false) : Union { $type = new TMixed($from_loop_isset, $from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getScalar(bool $from_docblock = \false) : Union { $type = new TScalar($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getNever(bool $from_docblock = \false) : Union { $type = new TNever($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getBool(bool $from_docblock = \false) : Union { $type = new TBool($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getFloat(?float $value = null, bool $from_docblock = \false) : Union { if ($value !== null) { $type = new TLiteralFloat($value, $from_docblock); } else { $type = new TFloat($from_docblock); } return new Union([$type]); } /** * @psalm-pure */ public static function getObject() : Union { $type = new TObject(); return new Union([$type]); } /** * @psalm-pure */ public static function getClosure() : Union { $type = new TClosure('Closure'); return new Union([$type]); } /** * @psalm-pure */ public static function getArrayKey(bool $from_docblock = \false) : Union { $type = new TArrayKey($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getArray() : Union { $type = new TArray([new Union([new TArrayKey()]), new Union([new TMixed()])]); return new Union([$type]); } /** * @psalm-pure */ public static function getEmptyArray() : Union { return new Union([self::getEmptyArrayAtomic()]); } /** * @psalm-pure */ public static function getEmptyArrayAtomic() : TArray { return new TArray([new Union([new TNever()]), new Union([new TNever()])]); } /** * @psalm-pure */ public static function getList(?Union $of = null, bool $from_docblock = \false) : Union { return new Union([self::getListAtomic($of ?? self::getMixed($from_docblock), $from_docblock)]); } /** * @psalm-pure */ public static function getNonEmptyList(?Union $of = null, bool $from_docblock = \false) : Union { return new Union([self::getNonEmptyListAtomic($of ?? self::getMixed($from_docblock), $from_docblock)]); } /** * @psalm-pure */ public static function getListAtomic(Union $of, bool $from_docblock = \false) : TKeyedArray { return new TKeyedArray([$of->setPossiblyUndefined(\true)], null, [self::getListKey(), $of], \true, $from_docblock); } /** * @psalm-pure */ public static function getNonEmptyListAtomic(Union $of, bool $from_docblock = \false) : TKeyedArray { return new TKeyedArray([$of->setPossiblyUndefined(\false)], null, [self::getListKey(), $of], \true, $from_docblock); } private static ?Union $listKey = null; private static ?Union $listKeyFromDocblock = null; /** * @psalm-pure * @psalm-suppress ImpureStaticProperty Used for caching */ public static function getListKey(bool $from_docblock = \false) : Union { if ($from_docblock) { return self::$listKeyFromDocblock ??= new Union([new TIntRange(0, null, \true)]); } return self::$listKey ??= self::getIntRange(0, null); } /** * @psalm-pure */ public static function getVoid(bool $from_docblock = \false) : Union { $type = new TVoid($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getFalse(bool $from_docblock = \false) : Union { $type = new TFalse($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getTrue(bool $from_docblock = \false) : Union { $type = new TTrue($from_docblock); return new Union([$type]); } /** * @psalm-pure */ public static function getResource(bool $from_docblock = \false) : Union { return new Union([new TResource($from_docblock)]); } /** * @psalm-external-mutation-free * @param non-empty-list $union_types */ public static function combineUnionTypeArray(array $union_types, ?\Psalm\Codebase $codebase) : Union { $first_type = array_pop($union_types); foreach ($union_types as $type) { $first_type = self::combineUnionTypes($first_type, $type, $codebase); } return $first_type; } /** * Combines two union types into one * * @param int $literal_limit any greater number of literal types than this * will be merged to a scalar * @psalm-external-mutation-free * @psalm-suppress ImpurePropertyAssignment We're not mutating external instances * @psalm-suppress InaccessibleProperty We're not mutating external instances */ public static function combineUnionTypes(?Union $type_1, ?Union $type_2, ?\Psalm\Codebase $codebase = null, bool $overwrite_empty_array = \false, bool $allow_mixed_union = \true, int $literal_limit = 500, ?bool $possibly_undefined = null) : Union { if ($type_2 === null && $type_1 === null) { throw new UnexpectedValueException('At least one type must be provided to combine'); } if ($type_1 === null) { if ($possibly_undefined !== null) { return $type_2->setPossiblyUndefined($possibly_undefined); } return $type_2; } if ($type_2 === null) { if ($possibly_undefined !== null) { return $type_1->setPossiblyUndefined($possibly_undefined); } return $type_1; } if ($type_1 === $type_2) { if ($possibly_undefined !== null) { return $type_1->setPossiblyUndefined($possibly_undefined); } return $type_1; } if ($type_1->isVanillaMixed() && $type_2->isVanillaMixed()) { $combined_type = self::getMixed(); } else { $both_failed_reconciliation = \false; if ($type_1->failed_reconciliation) { if ($type_2->failed_reconciliation) { $both_failed_reconciliation = \true; } else { return $type_2->setProperties(['parent_nodes' => array_merge($type_2->parent_nodes, $type_1->parent_nodes), 'possibly_undefined' => $possibly_undefined ?? $type_2->possibly_undefined]); } } elseif ($type_2->failed_reconciliation) { return $type_1->setProperties(['parent_nodes' => array_merge($type_1->parent_nodes, $type_2->parent_nodes), 'possibly_undefined' => $possibly_undefined ?? $type_1->possibly_undefined]); } $combined_type = TypeCombiner::combine(array_merge(array_values($type_1->getAtomicTypes()), array_values($type_2->getAtomicTypes())), $codebase, $overwrite_empty_array, $allow_mixed_union, $literal_limit); if (!$type_1->initialized || !$type_2->initialized) { $combined_type->initialized = \false; } if ($type_1->from_docblock || $type_2->from_docblock) { $combined_type->from_docblock = \true; } if ($type_1->from_calculation || $type_2->from_calculation) { $combined_type->from_calculation = \true; } if ($type_1->ignore_nullable_issues || $type_2->ignore_nullable_issues) { $combined_type->ignore_nullable_issues = \true; } if ($type_1->ignore_falsable_issues || $type_2->ignore_falsable_issues) { $combined_type->ignore_falsable_issues = \true; } if ($type_1->explicit_never || $type_2->explicit_never) { $combined_type->explicit_never = \true; } if ($type_1->had_template && $type_2->had_template) { $combined_type->had_template = \true; } if ($type_1->reference_free && $type_2->reference_free) { $combined_type->reference_free = \true; } if ($both_failed_reconciliation) { $combined_type->failed_reconciliation = \true; } } if ($possibly_undefined !== null) { $combined_type->possibly_undefined = $possibly_undefined; } elseif ($type_1->possibly_undefined || $type_2->possibly_undefined) { $combined_type->possibly_undefined = \true; } if ($type_1->possibly_undefined_from_try || $type_2->possibly_undefined_from_try) { $combined_type->possibly_undefined_from_try = \true; } if ($type_1->parent_nodes || $type_2->parent_nodes) { $combined_type->parent_nodes = $type_1->parent_nodes + $type_2->parent_nodes; } if ($type_1->by_ref || $type_2->by_ref) { $combined_type->by_ref = \true; } return $combined_type; } /** * Combines two union types into one via an intersection */ public static function intersectUnionTypes(?Union $type_1, ?Union $type_2, \Psalm\Codebase $codebase, bool $allow_interface_equality = \false, bool $allow_float_int_equality = \true) : ?Union { if ($type_2 === null && $type_1 === null) { throw new UnexpectedValueException('At least one type must be provided to combine'); } if ($type_1 === null) { return $type_2; } if ($type_2 === null) { return $type_1; } if ($type_1 === $type_2) { return $type_1; } $intersection_performed = \false; $type_1_mixed = $type_1->isMixed(); $type_2_mixed = $type_2->isMixed(); $possibly_undefined = $type_1->possibly_undefined && $type_2->possibly_undefined; if ($type_1_mixed && $type_2_mixed) { $combined_type = new Union([new TMixed()], ['possibly_undefined' => $possibly_undefined]); } else { $both_failed_reconciliation = \false; if ($type_1->failed_reconciliation) { if ($type_2->failed_reconciliation) { $both_failed_reconciliation = \true; } else { return $type_2; } } elseif ($type_2->failed_reconciliation) { return $type_1; } if ($type_1_mixed) { $combined_type = $type_2->getBuilder(); $intersection_performed = \true; } elseif ($type_2_mixed) { $combined_type = $type_1->getBuilder(); $intersection_performed = \true; } else { $combined_type = null; foreach ($type_1->getAtomicTypes() as $type_1_atomic) { foreach ($type_2->getAtomicTypes() as $type_2_atomic) { $intersection_atomic = self::intersectAtomicTypes($type_1_atomic, $type_2_atomic, $codebase, $intersection_performed, $allow_interface_equality, $allow_float_int_equality); if (null !== $intersection_atomic) { if (null === $combined_type) { $combined_type = new MutableUnion([$intersection_atomic]); } else { $combined_type->addType($intersection_atomic); } } } } } //if a type is contained by the other, the intersection is the narrowest type if (!$intersection_performed) { $type_1_in_2 = UnionTypeComparator::isContainedBy($codebase, $type_1, $type_2); $type_2_in_1 = UnionTypeComparator::isContainedBy($codebase, $type_2, $type_1); if ($type_1_in_2) { $intersection_performed = \true; $combined_type = $type_1->getBuilder(); } elseif ($type_2_in_1) { $intersection_performed = \true; $combined_type = $type_2->getBuilder(); } } if ($combined_type !== null) { if (!$type_1->initialized && !$type_2->initialized) { $combined_type->initialized = \false; } if ($type_1->possibly_undefined_from_try && $type_2->possibly_undefined_from_try) { $combined_type->possibly_undefined_from_try = \true; } if ($type_1->from_docblock && $type_2->from_docblock) { $combined_type->from_docblock = \true; } if ($type_1->from_calculation && $type_2->from_calculation) { $combined_type->from_calculation = \true; } if ($type_1->ignore_nullable_issues && $type_2->ignore_nullable_issues) { $combined_type->ignore_nullable_issues = \true; } if ($type_1->ignore_falsable_issues && $type_2->ignore_falsable_issues) { $combined_type->ignore_falsable_issues = \true; } if ($both_failed_reconciliation) { $combined_type->failed_reconciliation = \true; } $combined_type->possibly_undefined = $possibly_undefined; $combined_type = $combined_type->freeze(); } } if (!$intersection_performed && $type_1->getId() !== $type_2->getId()) { return null; } return $combined_type; } private static function intersectAtomicTypes(Atomic $type_1_atomic, Atomic $type_2_atomic, \Psalm\Codebase $codebase, bool &$intersection_performed, bool $allow_interface_equality = \false, bool $allow_float_int_equality = \true) : ?Atomic { $intersection_atomic = null; $wider_type = null; if ($type_1_atomic instanceof TNamedObject && $type_2_atomic instanceof TNamedObject) { if ($type_1_atomic->value === $type_2_atomic->value && get_class($type_1_atomic) === TNamedObject::class && get_class($type_2_atomic) !== TNamedObject::class) { $intersection_atomic = $type_2_atomic; $wider_type = $type_1_atomic; $intersection_performed = \true; } elseif ($type_1_atomic->value === $type_2_atomic->value && get_class($type_2_atomic) === TNamedObject::class && get_class($type_1_atomic) !== TNamedObject::class) { $intersection_atomic = $type_1_atomic; $wider_type = $type_2_atomic; $intersection_performed = \true; } } if ($type_1_atomic instanceof TInt && $type_2_atomic instanceof TInt) { $int_intersection = TIntRange::intersectIntRanges(TIntRange::convertToIntRange($type_1_atomic), TIntRange::convertToIntRange($type_2_atomic)); if ($int_intersection && ($int_intersection->min_bound !== null || $int_intersection->max_bound !== null)) { $intersection_performed = \true; if ($int_intersection->min_bound !== null && $int_intersection->min_bound === $int_intersection->max_bound) { return new TLiteralInt($int_intersection->min_bound); } return $int_intersection; } } if (null === $intersection_atomic) { if (AtomicTypeComparator::isContainedBy($codebase, $type_2_atomic, $type_1_atomic, $allow_interface_equality, $allow_float_int_equality)) { $intersection_atomic = $type_2_atomic; $wider_type = $type_1_atomic; $intersection_performed = \true; } elseif (AtomicTypeComparator::isContainedBy($codebase, $type_1_atomic, $type_2_atomic, $allow_interface_equality, $allow_float_int_equality)) { $intersection_atomic = $type_1_atomic; $wider_type = $type_2_atomic; $intersection_performed = \true; } if ($intersection_atomic && !self::hasIntersection($type_1_atomic) && !self::hasIntersection($type_2_atomic)) { return $intersection_atomic; } } if (self::mayHaveIntersection($type_1_atomic, $codebase) && self::mayHaveIntersection($type_2_atomic, $codebase)) { /** @psalm-suppress TypeDoesNotContainType */ if ($type_1_atomic instanceof TNamedObject && $type_2_atomic instanceof TNamedObject) { try { $first = $codebase->classlike_storage_provider->get($type_1_atomic->value); $second = $codebase->classlike_storage_provider->get($type_2_atomic->value); $first_is_class = !$first->is_interface && !$first->is_trait; $second_is_class = !$second->is_interface && !$second->is_trait; if ($first_is_class && $second_is_class) { return $intersection_atomic; } } catch (InvalidArgumentException $e) { // Ignore non-existing classes during initial scan } } if ($intersection_atomic === null && $wider_type === null) { $intersection_atomic = $type_1_atomic; $wider_type = $type_2_atomic; } if ($intersection_atomic === null || $wider_type === null) { throw new LogicException('$intersection_atomic and $wider_type should be both set or null.' . ' Check the preceding code for errors.' . ' Did you forget to assign one of the variables?'); } if (!self::mayHaveIntersection($intersection_atomic, $codebase) || !self::mayHaveIntersection($wider_type, $codebase)) { throw new LogicException('$intersection_atomic and $wider_type should be both support intersection.' . ' Check the preceding code for errors.'); } $intersection_performed = \true; $wider_type_clone = $wider_type->setIntersectionTypes([]); $final_intersection = array_merge([$wider_type_clone->getKey() => $wider_type_clone], $intersection_atomic->getIntersectionTypes()); $wider_type_intersection_types = $wider_type->getIntersectionTypes(); foreach ($wider_type_intersection_types as $wider_type_intersection_type) { $final_intersection[$wider_type_intersection_type->getKey()] = $wider_type_intersection_type; } return $intersection_atomic->setIntersectionTypes($final_intersection); } return $intersection_atomic; } /** * @psalm-assert-if-true TIterable|TNamedObject|TTemplateParam|TObjectWithProperties $type */ private static function mayHaveIntersection(Atomic $type, \Psalm\Codebase $codebase) : bool { if ($type instanceof TIterable || $type instanceof TTemplateParam || $type instanceof TObjectWithProperties) { return \true; } if (!$type instanceof TNamedObject) { return \false; } try { $storage = $codebase->classlike_storage_provider->get($type->value); } catch (InvalidArgumentException $e) { // Ignore non-existing classes during initial scan return \true; } return !$storage->final; } private static function hasIntersection(Atomic $type) : bool { return self::isIntersectionType($type) && $type->extra_types; } /** * @psalm-assert-if-true TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $type */ public static function isIntersectionType(Atomic $type) : bool { return $type instanceof TNamedObject || $type instanceof TTemplateParam || $type instanceof TIterable || $type instanceof TObjectWithProperties || $type instanceof TCallableObject; } } > */ protected static $issues_data = []; /** * @var array */ protected static $console_issues = []; /** * @var array */ protected static $fixable_issue_counts = []; /** * @var int */ protected static $error_count = 0; /** * @var array */ protected static $emitted = []; /** @var int */ protected static $recording_level = 0; /** @var array> */ protected static $recorded_issues = []; /** * @var array> */ protected static $unused_suppressions = []; /** * @var array> */ protected static $used_suppressions = []; /** @var array */ private static array $server = []; /** * This will add an issue to be emitted if it's not suppressed and return if it has been added * * @param string[] $suppressed_issues */ public static function accepts(CodeIssue $e, array $suppressed_issues = [], bool $is_fixable = \false) : bool { $config = \Psalm\Config::getInstance(); $project_analyzer = ProjectAnalyzer::getInstance(); $codebase = $project_analyzer->getCodebase(); $event = new BeforeAddIssueEvent($e, $is_fixable, $codebase); if ($config->eventDispatcher->dispatchBeforeAddIssue($event) === \false) { return \false; } if (self::isSuppressed($e, $suppressed_issues)) { return \false; } return self::add($e, $is_fixable); } /** * This will add an issue to be emitted if it's not suppressed * * @param string[] $suppressed_issues */ public static function maybeAdd(CodeIssue $e, array $suppressed_issues = [], bool $is_fixable = \false) : void { self::accepts($e, $suppressed_issues, $is_fixable); } /** * This is part of the findUnusedPsalmSuppress feature */ public static function addUnusedSuppression(string $file_path, int $offset, string $issue_type) : void { if (strpos($issue_type, 'Tainted') === 0) { return; } if (isset(self::$used_suppressions[$file_path][$offset])) { return; } if (!isset(self::$unused_suppressions[$file_path])) { self::$unused_suppressions[$file_path] = []; } self::$unused_suppressions[$file_path][$offset] = $offset + strlen($issue_type) - 1; } /** * This will return false if an issue is ready to be added for emission. Reasons for not returning false include: * - The issue is suppressed in config * - We're in a recording state * - The issue is included in the list of issues to be suppressed in param * * @param string[] $suppressed_issues */ public static function isSuppressed(CodeIssue $e, array $suppressed_issues = []) : bool { $config = \Psalm\Config::getInstance(); $fqcn_parts = explode('\\', get_class($e)); $issue_type = array_pop($fqcn_parts); $file_path = $e->getFilePath(); if (!$e instanceof ConfigIssue && !$config->reportIssueInFile($issue_type, $file_path)) { return \true; } $suppressed_issue_position = array_search($issue_type, $suppressed_issues, \true); if ($suppressed_issue_position !== \false) { if (is_int($suppressed_issue_position)) { self::$used_suppressions[$file_path][$suppressed_issue_position] = \true; } return \true; } $parent_issue_type = \Psalm\Config::getParentIssueType($issue_type); if ($parent_issue_type) { $suppressed_issue_position = array_search($parent_issue_type, $suppressed_issues, \true); if ($suppressed_issue_position !== \false) { if (is_int($suppressed_issue_position)) { self::$used_suppressions[$file_path][$suppressed_issue_position] = \true; } return \true; } } $suppress_all_position = $config->disable_suppress_all ? \false : array_search('all', $suppressed_issues, \true); if ($suppress_all_position !== \false) { if (is_int($suppress_all_position)) { self::$used_suppressions[$file_path][$suppress_all_position] = \true; } return \true; } $reporting_level = $config->getReportingLevelForIssue($e); if ($reporting_level === \Psalm\Config::REPORT_SUPPRESS) { return \true; } if ($e->code_location->getLineNumber() === -1) { return \true; } if (self::$recording_level > 0) { self::$recorded_issues[self::$recording_level][] = $e; return \true; } return \false; } /** * Add an issue to be emitted. This method should normally not be used! Use IssueBuffer::maybeAdd instead. * * @psalm-internal Psalm\IssueBuffer * @psalm-internal Psalm\Type\Reconciler::getValueForKey * @throws CodeException */ public static function add(CodeIssue $e, bool $is_fixable = \false) : bool { $config = \Psalm\Config::getInstance(); $project_analyzer = ProjectAnalyzer::getInstance(); $codebase = $project_analyzer->getCodebase(); $fqcn_parts = explode('\\', get_class($e)); $issue_type = array_pop($fqcn_parts); if (!$project_analyzer->show_issues) { return \false; } $is_tainted = strpos($issue_type, 'Tainted') === 0; if ($codebase->taint_flow_graph && !$is_tainted) { return \false; } $reporting_level = $config->getReportingLevelForIssue($e); if ($reporting_level === \Psalm\Config::REPORT_SUPPRESS) { return \false; } if ($config->debug_emitted_issues) { ob_start(); debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $trace = ob_get_clean(); fwrite(STDERR, "\nEmitting {$e->getShortLocation()} {$issue_type} {$e->message}\n{$trace}\n"); } // Make issue type for trace variable specific ("Trace" => "Trace~$var"). $trace_var = $issue_type === 'Trace' && preg_match('/^(\\$.+?):/', $e->message, $m) === 1 && isset($m[1]) ? '~' . $m[1] : ''; $emitted_key = $issue_type . $trace_var . '-' . $e->getShortLocation() . ':' . $e->code_location->getColumn() . ' ' . ($e->dupe_key ?? $e->message); if ($reporting_level === \Psalm\Config::REPORT_INFO) { if ($is_tainted || !self::alreadyEmitted($emitted_key)) { self::$issues_data[$e->getFilePath()][] = $e->toIssueData(IssueData::SEVERITY_INFO); if ($is_fixable) { self::addFixableIssue($issue_type); } } return \false; } if ($config->throw_exception) { FileAnalyzer::clearCache(); $message = $e instanceof TaintedInput ? $e->getJourneyMessage() : ($e instanceof MixedIssue ? $e->getMixedOriginMessage() : $e->message); throw new CodeException($issue_type . ' - ' . $e->getShortLocationWithPrevious() . ':' . $e->code_location->getColumn() . ' - ' . $message); } if ($is_tainted || !self::alreadyEmitted($emitted_key)) { ++self::$error_count; self::$issues_data[$e->getFilePath()][] = $e->toIssueData(IssueData::SEVERITY_ERROR); if ($is_fixable) { self::addFixableIssue($issue_type); } } return \true; } private static function removeRecordedIssue(string $issue_type, int $file_offset) : void { $recorded_issues = self::$recorded_issues[self::$recording_level]; $filtered_issues = []; foreach ($recorded_issues as $issue) { [$from] = $issue->code_location->getSelectionBounds(); if ($issue::getIssueType() !== $issue_type || $from !== $file_offset) { $filtered_issues[] = $issue; } } self::$recorded_issues[self::$recording_level] = $filtered_issues; } /** * This will try to remove an issue that has been added for emission */ public static function remove(string $file_path, string $issue_type, int $file_offset) : void { if (self::$recording_level > 0) { self::removeRecordedIssue($issue_type, $file_offset); } if (!isset(self::$issues_data[$file_path])) { return; } $filtered_issues = []; foreach (self::$issues_data[$file_path] as $issue) { if ($issue->type !== $issue_type || $issue->from !== $file_offset) { $filtered_issues[] = $issue; } } if (empty($filtered_issues)) { unset(self::$issues_data[$file_path]); } else { self::$issues_data[$file_path] = $filtered_issues; } } public static function addFixableIssue(string $issue_type) : void { if (isset(self::$fixable_issue_counts[$issue_type])) { self::$fixable_issue_counts[$issue_type]++; } else { self::$fixable_issue_counts[$issue_type] = 1; } } /** * @return array> */ public static function getIssuesData() : array { return self::$issues_data; } /** * @return list */ public static function getIssuesDataForFile(string $file_path) : array { return self::$issues_data[$file_path] ?? []; } /** * @return array */ public static function getFixableIssues() : array { return self::$fixable_issue_counts; } /** * @param array $fixable_issue_counts */ public static function addFixableIssues(array $fixable_issue_counts) : void { foreach ($fixable_issue_counts as $issue_type => $count) { if (isset(self::$fixable_issue_counts[$issue_type])) { self::$fixable_issue_counts[$issue_type] += $count; } else { self::$fixable_issue_counts[$issue_type] = $count; } } } /** * @return array> */ public static function getUnusedSuppressions() : array { return self::$unused_suppressions; } /** * @return array> */ public static function getUsedSuppressions() : array { return self::$used_suppressions; } /** * @param array> $unused_suppressions */ public static function addUnusedSuppressions(array $unused_suppressions) : void { self::$unused_suppressions += $unused_suppressions; } /** * @param array> $used_suppressions */ public static function addUsedSuppressions(array $used_suppressions) : void { foreach ($used_suppressions as $file => $offsets) { if (!isset(self::$used_suppressions[$file])) { self::$used_suppressions[$file] = $offsets; } else { self::$used_suppressions[$file] += $offsets; } } } public static function processUnusedSuppressions(FileProvider $file_provider) : void { $config = \Psalm\Config::getInstance(); foreach (self::$unused_suppressions as $file_path => $offsets) { if (!$offsets) { continue; } if (!$config->isInProjectDirs($file_path)) { continue; } $file_contents = $file_provider->getContents($file_path); foreach ($offsets as $start => $end) { if (isset(self::$used_suppressions[$file_path][$start])) { continue; } self::add(new UnusedPsalmSuppress('This suppression is never used', new Raw($file_contents, $file_path, $config->shortenFileName($file_path), $start, $end))); } } } public static function getErrorCount() : int { return self::$error_count; } /** * @param array> $issues_data */ public static function addIssues(array $issues_data) : void { foreach ($issues_data as $file_path => $file_issues) { foreach ($file_issues as $issue) { $emitted_key = $issue->type . '-' . $issue->file_name . ':' . $issue->line_from . ':' . $issue->column_from . ' ' . ($issue->dupe_key ?? $issue->message); if (!self::alreadyEmitted($emitted_key)) { self::$issues_data[$file_path][] = $issue; } } } } /** * @param array}>> $issue_baseline */ public static function finish(ProjectAnalyzer $project_analyzer, bool $is_full, float $start_time, bool $add_stats = \false, array $issue_baseline = []) : void { if (!$project_analyzer->stdout_report_options) { throw new UnexpectedValueException('Cannot finish without stdout report options'); } $codebase = $project_analyzer->getCodebase(); foreach ($codebase->config->config_issues as $issue) { self::maybeAdd($issue); } $error_count = 0; $info_count = 0; $issues_data = []; if (self::$issues_data) { if (in_array($project_analyzer->stdout_report_options->format, [\Psalm\Report::TYPE_CONSOLE, \Psalm\Report::TYPE_PHP_STORM])) { echo "\n"; } ksort(self::$issues_data); foreach (self::$issues_data as $file_path => $file_issues) { usort($file_issues, static fn(IssueData $d1, IssueData $d2): int => [$d1->file_path, $d1->line_from, $d1->column_from] <=> [$d2->file_path, $d2->line_from, $d2->column_from]); self::$issues_data[$file_path] = $file_issues; } // make a copy so what gets saved in cache is unaffected by baseline $issues_data = self::$issues_data; } if (!empty($issue_baseline)) { // Set severity for issues in baseline to INFO foreach ($issues_data as $file_path => $file_issues) { foreach ($file_issues as $key => $issue_data) { $file = $issue_data->file_name; $file = str_replace('\\', '/', $file); $type = $issue_data->type; if (isset($issue_baseline[$file][$type]) && $issue_baseline[$file][$type]['o'] > 0) { if ($issue_baseline[$file][$type]['o'] === count($issue_baseline[$file][$type]['s'])) { $position = array_search(str_replace("\r\n", "\n", trim($issue_data->selected_text)), $issue_baseline[$file][$type]['s'], \true); if ($position !== \false) { $issue_data->severity = IssueData::SEVERITY_INFO; array_splice($issue_baseline[$file][$type]['s'], $position, 1); $issue_baseline[$file][$type]['o']--; } } else { $issue_baseline[$file][$type]['s'] = []; $issue_data->severity = IssueData::SEVERITY_INFO; $issue_baseline[$file][$type]['o']--; } } $issues_data[$file_path][$key] = $issue_data; } } if ($codebase->config->find_unused_baseline_entry) { foreach ($issue_baseline as $file_path => $issues) { foreach ($issues as $issue_name => $issue) { if ($issue['o'] !== 0) { $issues_data[$file_path][] = new IssueData(IssueData::SEVERITY_ERROR, 0, 0, UnusedBaselineEntry::getIssueType(), sprintf('Baseline for issue "%s" has %d extra %s.', $issue_name, $issue['o'], $issue['o'] === 1 ? 'entry' : 'entries'), $file_path, '', '', '', 0, 0, 0, 0, 0, 0, UnusedBaselineEntry::SHORTCODE, UnusedBaselineEntry::ERROR_LEVEL); } } } } } echo self::getOutput($issues_data, $project_analyzer->stdout_report_options, $codebase->analyzer->getTotalTypeCoverage($codebase)); foreach ($issues_data as $file_issues) { foreach ($file_issues as $issue_data) { if ($issue_data->severity === \Psalm\Config::REPORT_ERROR) { ++$error_count; } else { ++$info_count; } } } if ($codebase->config->eventDispatcher->after_analysis) { $source_control_info = null; $build_info = (new BuildInfoCollector(self::$server))->collect(); try { $source_control_info = (new GitInfoCollector())->collect(); } catch (RuntimeException $e) { // do nothing } /** @psalm-suppress ArgumentTypeCoercion due to Psalm bug */ $event = new AfterAnalysisEvent($codebase, $issues_data, $build_info, $source_control_info); $codebase->config->eventDispatcher->dispatchAfterAnalysis($event); } foreach ($project_analyzer->generated_report_options as $report_options) { if (!$report_options->output_path) { throw new UnexpectedValueException('Output path should not be null here'); } $folder = dirname($report_options->output_path); if (!is_dir($folder) && !mkdir($folder, 0777, \true) && !is_dir($folder)) { throw new RuntimeException(sprintf('Directory "%s" was not created', $folder)); } file_put_contents($report_options->output_path, self::getOutput($issues_data, $report_options, $codebase->analyzer->getTotalTypeCoverage($codebase))); } if (in_array($project_analyzer->stdout_report_options->format, [\Psalm\Report::TYPE_CONSOLE, \Psalm\Report::TYPE_PHP_STORM, \Psalm\Report::TYPE_GITHUB_ACTIONS])) { echo str_repeat('-', 30) . "\n"; if ($error_count) { echo ($project_analyzer->stdout_report_options->use_color ? "\x1b[0;31m" . $error_count . " errors\x1b[0m" : $error_count . ' errors') . ' found' . "\n"; } else { self::printSuccessMessage($project_analyzer); } $show_info = $project_analyzer->stdout_report_options->show_info; $show_suggestions = $project_analyzer->stdout_report_options->show_suggestions; if ($info_count && ($show_info || $show_suggestions)) { echo str_repeat('-', 30) . "\n"; echo $info_count . ' other issues found.' . "\n"; if (!$show_info) { echo 'You can display them with ' . ($project_analyzer->stdout_report_options->use_color ? "\x1b[30;48;5;195m--show-info=true\x1b[0m" : '--show-info=true') . "\n"; } } if (self::$fixable_issue_counts && $show_suggestions && !$codebase->taint_flow_graph) { echo str_repeat('-', 30) . "\n"; $total_count = array_sum(self::$fixable_issue_counts); $command = '--alter --issues=' . implode(',', array_keys(self::$fixable_issue_counts)); $command .= ' --dry-run'; echo 'Psalm can automatically fix ' . $total_count . ($show_info ? ' issues' : ' of these issues') . ".\n" . 'Run Psalm again with ' . "\n" . ($project_analyzer->stdout_report_options->use_color ? "\x1b[30;48;5;195m" . $command . "\x1b[0m" : $command) . "\n" . 'to see what it can fix.' . "\n"; } echo str_repeat('-', 30) . "\n" . "\n"; if ($start_time) { echo 'Checks took ' . number_format(microtime(\true) - $start_time, 2) . ' seconds'; echo ' and used ' . number_format(memory_get_peak_usage() / (1024 * 1024), 3) . 'MB of memory' . "\n"; $analysis_summary = $codebase->analyzer->getTypeInferenceSummary($codebase); echo $analysis_summary . "\n"; if ($add_stats) { echo '-----------------' . "\n"; echo $codebase->analyzer->getNonMixedStats(); echo "\n"; } if ($project_analyzer->debug_performance) { echo '-----------------' . "\n"; echo 'Slow-to-analyze functions' . "\n"; echo '-----------------' . "\n\n"; $function_timings = $codebase->analyzer->getFunctionTimings(); arsort($function_timings); $i = 0; foreach ($function_timings as $function_id => $time) { if (++$i > 10) { break; } echo $function_id . ': ' . round(1000 * $time, 2) . 'ms per node' . "\n"; } echo "\n"; } } } if ($is_full && $start_time) { $project_analyzer->finish($start_time, PSALM_VERSION); } if ($error_count && !($codebase->taint_flow_graph && $project_analyzer->generated_report_options && isset($_SERVER['GITHUB_WORKFLOW']))) { exit(2); } } public static function printSuccessMessage(ProjectAnalyzer $project_analyzer) : void { if (!$project_analyzer->stdout_report_options) { throw new UnexpectedValueException('Cannot print success message without stdout report options'); } // this message will be printed $message = "No errors found!"; // color block will contain this amount of characters $blockSize = 30; // message with prepended and appended whitespace to be same as $blockSize $messageWithPadding = str_repeat(' ', 7) . $message . str_repeat(' ', 7); // top side of the color block $paddingTop = str_repeat(' ', $blockSize); // bottom side of the color block $paddingBottom = str_repeat(' ', $blockSize); // background color, 42 = green $background = "42"; // foreground/text color, 30 = black $foreground = "30"; // text style, 1 = bold $style = "2"; if ($project_analyzer->stdout_report_options->use_color) { echo "\x1b[{$background};{$style}m{$paddingTop}\x1b[0m" . "\n"; echo "\x1b[{$background};{$foreground};{$style}m{$messageWithPadding}\x1b[0m" . "\n"; echo "\x1b[{$background};{$style}m{$paddingBottom}\x1b[0m" . "\n"; } else { echo "\n"; echo "{$messageWithPadding}\n"; echo "\n"; } } /** * @param array> $issues_data * @param array{int, int} $mixed_counts */ public static function getOutput(array $issues_data, ReportOptions $report_options, array $mixed_counts = [0, 0]) : string { $total_expression_count = $mixed_counts[0] + $mixed_counts[1]; $mixed_expression_count = $mixed_counts[0]; $normalized_data = $issues_data === [] ? [] : array_merge(...array_values($issues_data)); $format = $report_options->format; switch ($format) { case \Psalm\Report::TYPE_COMPACT: $output = new CompactReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_EMACS: $output = new EmacsReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_TEXT: $output = new TextReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_JSON: $output = new JsonReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_BY_ISSUE_LEVEL: $output = new ByIssueLevelAndTypeReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_JSON_SUMMARY: $output = new JsonSummaryReport($normalized_data, self::$fixable_issue_counts, $report_options, $mixed_expression_count, $total_expression_count); break; case \Psalm\Report::TYPE_SONARQUBE: $output = new SonarqubeReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_PYLINT: $output = new PylintReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_CHECKSTYLE: $output = new CheckstyleReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_XML: $output = new XmlReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_JUNIT: $output = new JunitReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_CONSOLE: $output = new ConsoleReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_GITHUB_ACTIONS: $output = new GithubActionsReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_PHP_STORM: $output = new PhpStormReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_SARIF: $output = new SarifReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_CODECLIMATE: $output = new CodeClimateReport($normalized_data, self::$fixable_issue_counts, $report_options); break; case \Psalm\Report::TYPE_COUNT: $output = new CountReport($normalized_data, self::$fixable_issue_counts, $report_options); break; default: throw new RuntimeException('Unexpected report format: ' . $report_options->format); } return $output->create(); } protected static function alreadyEmitted(string $message) : bool { $sham = sha1($message); if (isset(self::$emitted[$sham])) { return \true; } self::$emitted[$sham] = \true; return \false; } public static function clearCache() : void { self::$issues_data = []; self::$emitted = []; self::$error_count = 0; self::$recording_level = 0; self::$recorded_issues = []; self::$console_issues = []; self::$unused_suppressions = []; self::$used_suppressions = []; } /** * @return array> */ public static function clear() : array { $current_data = self::$issues_data; self::$issues_data = []; self::$emitted = []; return $current_data; } /** * Return whether or not we're in a recording state regarding startRecording/stopRecording status */ public static function isRecording() : bool { return self::$recording_level > 0; } /** * Increase the recording level in order to start recording issues instead of adding them while in a loop */ public static function startRecording() : void { ++self::$recording_level; self::$recorded_issues[self::$recording_level] = []; } /** * Decrease the recording level after leaving a loop * * @see startRecording */ public static function stopRecording() : void { if (self::$recording_level === 0) { throw new UnexpectedValueException('Cannot stop recording - already at base level'); } --self::$recording_level; } /** * This will return the recorded issues for the current recording level * * @return array */ public static function clearRecordingLevel() : array { if (self::$recording_level === 0) { throw new UnexpectedValueException('Not currently recording'); } $recorded_issues = self::$recorded_issues[self::$recording_level]; self::$recorded_issues[self::$recording_level] = []; return $recorded_issues; } /** * This will try to add issues that has been retrieved through clearRecordingLevel or record them at a lower level */ public static function bubbleUp(CodeIssue $e) : void { if (self::$recording_level === 0) { self::add($e); return; } self::$recorded_issues[self::$recording_level][] = $e; } /** * @internal * @param array $server */ public static final function captureServer(array $server) : void { self::$server = $server; } } print_errors = $print_errors; $this->print_infos = $print_infos; } public function startScanningFiles() : void { $this->write('Scanning files...' . "\n"); } public function startAnalyzingFiles() : void { $this->write('Analyzing files...' . "\n\n"); } public function startAlteringFiles() : void { $this->write('Altering files...' . "\n"); } public function alterFileDone(string $file_name) : void { $this->write('Altered ' . $file_name . "\n"); } public function start(int $number_of_tasks) : void { $this->number_of_tasks = $number_of_tasks; $this->progress = 0; } public function taskDone(int $level) : void { if ($level === 0 || $level === 1 && !$this->print_infos || !$this->print_errors) { $this->write(self::doesTerminalSupportUtf8() ? '░' : '_'); } elseif ($level === 1) { $this->write('I'); } else { $this->write('E'); } ++$this->progress; if ($this->progress % self::NUMBER_OF_COLUMNS !== 0) { return; } $this->printOverview(); $this->write(PHP_EOL); } public function finish() : void { $this->write(PHP_EOL); } protected function getOverview() : string { if ($this->number_of_tasks === null) { throw new LogicException('Progress::start() should be called before Progress::startDone()'); } $leadingSpaces = 1 + strlen((string) $this->number_of_tasks) - strlen((string) $this->progress); // Don't show 100% unless this is the last line of the progress bar. $percentage = floor($this->progress / $this->number_of_tasks * 100); return sprintf('%s%s / %s (%s%%)', str_repeat(' ', $leadingSpaces), $this->progress, $this->number_of_tasks, $percentage); } private function printOverview() : void { $this->write($this->getOverview()); } } write($message); } public function startScanningFiles() : void { $this->write('Scanning files...' . "\n"); } public function startAnalyzingFiles() : void { $this->write('Analyzing files...' . "\n"); } public function startAlteringFiles() : void { $this->write('Updating files...' . "\n"); } public function alterFileDone(string $file_name) : void { $this->write('Altered ' . $file_name . "\n"); } } number_of_tasks > self::TOO_MANY_FILES) { ++$this->progress; // Source for rate limiting: // https://github.com/phan/phan/blob/9a788581ee1a4e1c35bebf89c435fd8a238c1d17/src/Phan/CLI.php $time = microtime(\true); // If not enough time has elapsed, then don't update the progress bar. // Making the update frequency based on time (instead of the number of files) // prevents the terminal from rapidly flickering while processing small/empty files, // and reduces the time spent writing to stderr. if ($time - $this->previous_update_time < self::PROGRESS_BAR_SAMPLE_INTERVAL) { // Make sure to output the section for 100% completion regardless of limits, to avoid confusion. if ($this->progress !== $this->number_of_tasks) { return; } } $this->previous_update_time = $time; $inner_progress = self::renderInnerProgressBar(self::NUMBER_OF_COLUMNS, $this->progress / $this->number_of_tasks); $this->write($inner_progress . ' ' . $this->getOverview() . "\r"); } else { parent::taskDone($level); } } /** * Fully stolen from * https://github.com/phan/phan/blob/d61a624b1384ea220f39927d53fd656a65a75fac/src/Phan/CLI.php * Renders a unicode progress bar that goes from light (left) to dark (right) * The length in the console is the positive integer $length * * @see https://en.wikipedia.org/wiki/Block_Elements */ private static function renderInnerProgressBar(int $length, float $p) : string { $current_float = $p * $length; $current = (int) $current_float; $rest = max($length - $current, 0); if (!self::doesTerminalSupportUtf8()) { // Show a progress bar of "XXXX>------" in Windows when utf-8 is unsupported. $progress_bar = str_repeat('X', $current); $delta = $current_float - $current; if ($delta > 0.5) { $progress_bar .= '>' . str_repeat('-', $rest - 1); } else { $progress_bar .= str_repeat('-', $rest); } return $progress_bar; } // The left-most characters are "Light shade" $progress_bar = str_repeat("█", $current); $delta = $current_float - $current; if ($delta > 3.0 / 4) { $progress_bar .= "▊" . str_repeat("░", $rest - 1); } elseif ($delta > 2.0 / 4) { $progress_bar .= "▌" . str_repeat("░", $rest - 1); } elseif ($delta > 1.0 / 4) { $progress_bar .= "▎" . str_repeat("░", $rest - 1); } else { $progress_bar .= str_repeat("░", $rest); } return $progress_bar; } public function finish() : void { if ($this->number_of_tasks > self::TOO_MANY_FILES) { $this->write(str_repeat(' ', self::NUMBER_OF_COLUMNS + strlen($this->getOverview()) + 1) . "\r"); } else { parent::finish(); } } } write('Warning: ' . $message . PHP_EOL); } protected static function doesTerminalSupportUtf8() : bool { if (stripos(PHP_OS, 'WIN') === 0) { if (!function_exists('sapi_windows_cp_is_utf8') || !sapi_windows_cp_is_utf8()) { return \false; } } return \true; } } */ final class RemoteInfo { /** * Remote name. * * @var null|string */ protected $name; /** * Remote URL. * * @var null|string */ protected $url; public function toArray() : array { return ['name' => $this->name, 'url' => $this->url]; } // accessor /** * Set remote name. * * @param string $name remote name * @return $this */ public function setName(string $name) : \Psalm\SourceControl\Git\RemoteInfo { $this->name = $name; return $this; } /** * Return remote name. */ public function getName() : ?string { return $this->name; } /** * Set remote URL. * * @param string $url remote URL * @return $this */ public function setUrl(string $url) : \Psalm\SourceControl\Git\RemoteInfo { $this->url = $url; return $this; } /** * Return remote URL. */ public function getUrl() : ?string { return $this->url; } } */ final class GitInfo extends SourceControlInfo { /** * Branch name. * * @var string */ protected $branch; /** * Head. * * @var CommitInfo */ protected $head; /** * Remote. * * @var RemoteInfo[] */ protected $remotes; /** * Constructor. * * @param string $branch branch name * @param CommitInfo $head HEAD commit * @param RemoteInfo[] $remotes remote repositories */ public function __construct(string $branch, \Psalm\SourceControl\Git\CommitInfo $head, array $remotes) { $this->branch = $branch; $this->head = $head; $this->remotes = $remotes; } public function toArray() : array { $remotes = []; foreach ($this->remotes as $remote) { $remotes[] = $remote->toArray(); } return ['branch' => $this->branch, 'head' => $this->head->toArray(), 'remotes' => $remotes]; } // accessor /** * Return branch name. */ public function getBranch() : string { return $this->branch; } /** * Return HEAD commit. */ public function getHead() : \Psalm\SourceControl\Git\CommitInfo { return $this->head; } /** * Return remote repositories. * * @return RemoteInfo[] */ public function getRemotes() : array { return $this->remotes; } } */ final class CommitInfo { /** * Commit ID. * * @var null|string */ protected $id; /** * Author name. * * @var null|string */ protected $author_name; /** * Author email. * * @var null|string */ protected $author_email; /** * Committer name. * * @var null|string */ protected $committer_name; /** * Committer email. * * @var null|string */ protected $committer_email; /** * Commit message. * * @var null|string */ protected $message; /** * Commit message. * * @var null|int */ protected $date; public function toArray() : array { return ['id' => $this->id, 'author_name' => $this->author_name, 'author_email' => $this->author_email, 'committer_name' => $this->committer_name, 'committer_email' => $this->committer_email, 'message' => $this->message, 'date' => $this->date]; } // accessor /** * Set commit ID. */ public function setId(string $id) : self { $this->id = $id; return $this; } /** * Return commit ID. */ public function getId() : ?string { return $this->id; } /** * Set author name. */ public function setAuthorName(string $author_name) : self { $this->author_name = $author_name; return $this; } /** * Return author name. */ public function getAuthorName() : ?string { return $this->author_name; } /** * Set author email. */ public function setAuthorEmail(string $author_email) : self { $this->author_email = $author_email; return $this; } /** * Return author email. */ public function getAuthorEmail() : ?string { return $this->author_email; } /** * Set committer name. */ public function setCommitterName(string $committer_name) : self { $this->committer_name = $committer_name; return $this; } /** * Return committer name. */ public function getCommitterName() : ?string { return $this->committer_name; } /** * Set committer email. */ public function setCommitterEmail(string $committer_email) : self { $this->committer_email = $committer_email; return $this; } /** * Return committer email. */ public function getCommitterEmail() : ?string { return $this->committer_email; } /** * Set commit message. */ public function setMessage(string $message) : self { $this->message = $message; return $this; } /** * Return commit message. */ public function getMessage() : ?string { return $this->message; } /** * Set commit date */ public function setDate(int $date) : self { $this->date = $date; return $this; } /** * Return commit date. */ public function getDate() : ?int { return $this->date; } } getText(), $docblock->getStartFilePos()); foreach ($parsed_docblock->tags as $special_key => $_) { if (strpos($special_key, 'psalm-') === 0) { $special_key = substr($special_key, 6); if (!in_array($special_key, self::PSALM_ANNOTATIONS, \true)) { throw new DocblockParseException('Unrecognised annotation @psalm-' . $special_key); } } } return $parsed_docblock; } /** * @psalm-pure * @return array */ public static function parseSuppressList(string $suppress_entry) : array { preg_match('/ (?(DEFINE) # either a single issue or comma separated list of issues (? (?&issue) \\s* , \\s* (?&issue_list) | (?&issue) ) # definition of a single issue (? [A-Za-z0-9_-]+ ) ) ^ (?P (?&issue_list) ) (?P .* ) $ /xm', $suppress_entry, $matches); if (!isset($matches['issues'])) { return []; } $issue_offset = 0; $ret = []; foreach (explode(',', $matches['issues']) as $suppressed_issue) { $issue_offset += strspn($suppressed_issue, "\t\n\f\r "); $ret[$issue_offset] = trim($suppressed_issue); $issue_offset += strlen($suppressed_issue) + 1; } return $ret; } } */ public $uses; /** * @var array */ public $uses_flipped; /** * @var array */ public $functions; /** * @var array */ public $functions_flipped; /** * @var array */ public $constants; /** * @var array */ public $constants_flipped; /** @var string|null */ public $namespace; /** @var ?int */ public $namespace_first_stmt_start; /** @var ?int */ public $uses_start; /** @var ?int */ public $uses_end; /** * @param array $uses * @param array $functions * @param array $constants * @param array $uses_flipped * @param array $functions_flipped * @param array $constants_flipped * @internal * @psalm-mutation-free */ public function __construct(?string $namespace = null, array $uses = [], array $functions = [], array $constants = [], array $uses_flipped = [], array $functions_flipped = [], array $constants_flipped = []) { $this->namespace = $namespace; $this->uses = $uses; $this->functions = $functions; $this->constants = $constants; $this->uses_flipped = $uses_flipped; $this->functions_flipped = $functions_flipped; $this->constants_flipped = $constants_flipped; } } */ public static $ERROR_LEVELS = [self::REPORT_INFO, self::REPORT_ERROR, self::REPORT_SUPPRESS]; /** * @var array */ private const MIXED_ISSUES = ['MixedArgument', 'MixedArrayAccess', 'MixedArrayAssignment', 'MixedArrayOffset', 'MixedArrayTypeCoercion', 'MixedAssignment', 'MixedFunctionCall', 'MixedInferredReturnType', 'MixedMethodCall', 'MixedOperand', 'MixedPropertyFetch', 'MixedPropertyAssignment', 'MixedReturnStatement', 'MixedStringOffsetAssignment', 'MixedArgumentTypeCoercion', 'MixedPropertyTypeCoercion', 'MixedReturnTypeCoercion']; /** * These are special object classes that allow any and all properties to be get/set on them * * @var array */ protected $universal_object_crates; /** * @var static|null */ private static $instance; /** * Whether or not to use types as defined in docblocks * * @var bool */ public $use_docblock_types = \true; /** * Whether or not to use types as defined in property docblocks. * This is distinct from the above because you may want to use * property docblocks, but not function docblocks. * * @var bool */ public $use_docblock_property_types = \false; /** * Whether using property annotations in docblocks should implicitly seal properties * * @var bool */ public $docblock_property_types_seal_properties = \true; /** * Whether or not to throw an exception on first error * * @var bool */ public $throw_exception = \false; /** * The directory to store PHP Parser (and other) caches * * @internal * @var string|null */ public $cache_directory; private bool $cache_directory_initialized = \false; /** * The directory to store all Psalm project caches * * @var string|null */ public $global_cache_directory; /** * Path to the autoader * * @var string|null */ public $autoloader; /** * @var ProjectFileFilter|null */ protected $project_files; /** * @var ProjectFileFilter|null */ protected $extra_files; /** * The base directory of this config file * * @var string */ public $base_dir; /** * The PHP version to assume as declared in the config file */ private ?string $configured_php_version = null; /** * @var array */ private array $file_extensions = ['php']; /** * @var array> */ private array $filetype_scanners = []; /** * @var array> */ private array $filetype_analyzers = []; /** * @var array */ private array $filetype_scanner_paths = []; /** * @var array */ private array $filetype_analyzer_paths = []; /** * @var array */ private array $issue_handlers = []; /** * @var array */ private array $mock_classes = []; /** * @var array */ private array $preloaded_stub_files = []; /** * @var array */ private array $stub_files = []; /** * @var bool */ public $hide_external_errors = \false; /** * @var bool */ public $hide_all_errors_except_passed_files = \false; /** @var bool */ public $allow_includes = \true; /** @var 1|2|3|4|5|6|7|8 */ public $level = 1; /** * @var ?bool */ public $show_mixed_issues; /** @var bool */ public $strict_binary_operands = \false; /** * @var bool */ public $remember_property_assignments_after_call = \true; /** @var bool */ public $use_igbinary = \false; /** @var 'lz4'|'deflate'|'off' */ public $compressor = 'off'; /** * @var bool */ public $allow_string_standin_for_class = \false; /** * @var bool */ public $disable_suppress_all = \false; /** * @var bool */ public $use_phpdoc_method_without_magic_or_parent = \false; /** * @var bool */ public $use_phpdoc_property_without_magic_or_parent = \false; /** * @var bool */ public $skip_checks_on_unresolvable_includes = \false; /** * @var bool */ public $seal_all_methods = \false; /** * @var bool */ public $seal_all_properties = \false; /** * @var bool */ public $memoize_method_calls = \false; /** * @var bool */ public $hoist_constants = \false; /** * @var bool */ public $add_param_default_to_docblock_type = \false; /** * @var bool */ public $disable_var_parsing = \false; /** * @var bool */ public $check_for_throws_docblock = \false; /** * @var bool */ public $check_for_throws_in_global_scope = \false; /** * @var bool */ public $ignore_internal_falsable_issues = \true; /** * @var bool */ public $ignore_internal_nullable_issues = \true; /** * @var array */ public $ignored_exceptions = []; /** * @var array */ public $ignored_exceptions_in_global_scope = []; /** * @var array */ public $ignored_exceptions_and_descendants = []; /** * @var array */ public $ignored_exceptions_and_descendants_in_global_scope = []; /** * @var bool */ public $infer_property_types_from_constructor = \true; /** * @var bool */ public $ensure_array_string_offsets_exist = \false; /** * @var bool */ public $ensure_array_int_offsets_exist = \false; /** * @var array */ public $forbidden_functions = []; /** * TODO: Psalm 6: Update default to be true and remove warning. * * @var bool */ public $find_unused_code = \false; /** * @var bool */ public $find_unused_variables = \false; /** * @var bool */ public $find_unused_psalm_suppress = \false; /** * TODO: Psalm 6: Update default to be true and remove warning. */ public bool $find_unused_baseline_entry = \false; /** * @var bool */ public $run_taint_analysis = \false; /** @var bool */ public $use_phpstorm_meta_path = \true; /** * @var bool */ public $resolve_from_config_file = \true; /** * @var bool */ public $restrict_return_types = \false; /** * @var bool */ public $limit_method_complexity = \false; /** * @var int */ public $max_graph_size = 200; /** * @var int */ public $max_avg_path_length = 70; /** * @var int */ public $max_shaped_array_size = 100; /** * @var string[] */ public $plugin_paths = []; /** * @var array */ private array $plugin_classes = []; /** * @var bool */ public $allow_internal_named_arg_calls = \true; /** * @var bool */ public $allow_named_arg_calls = \true; /** @var array */ private array $predefined_constants = []; /** @var array */ private array $predefined_functions = []; private ?ClassLoader $composer_class_loader = null; /** * @var string */ public $hash = ''; /** @var string|null */ public $error_baseline; /** * @var bool */ public $include_php_versions_in_error_baseline = \false; /** * @var string * @deprecated Please use {@see self::$shepherd_endpoint} instead. Property will be removed in Psalm 6. */ public $shepherd_host = 'shepherd.dev'; /** * @var string * @internal */ public $shepherd_endpoint = 'https://shepherd.dev/hooks/psalm/'; /** * @var array */ public $globals = []; /** * @var int */ public $max_string_length = 1000; private ?IncludeCollector $include_collector = null; /** * @var TaintAnalysisFileFilter|null */ protected $taint_analysis_ignored_files; /** * @var bool whether to emit a backtrace of emitted issues to stderr */ public $debug_emitted_issues = \false; private bool $report_info = \true; /** * @var EventDispatcher */ public $eventDispatcher; /** @var list */ public $config_issues = []; /** * @var 'default'|'never'|'always' */ public $trigger_error_exits = 'default'; /** * @var string[] */ public $internal_stubs = []; /** @var ?int */ public $threads; /** * A list of php extensions supported by Psalm. * Where key - extension name (without ext- prefix), value - whether to load extension’s stub. * Values: * - true: ext enabled explicitly or bundled with PHP (should load stubs) * - false: ext disabled explicitly (should not load stubs) * - null: state is unknown (e.g. config not processed yet) or ext neither explicitly enabled or disabled. * * @psalm-readonly-allow-private-mutation * @var array */ public $php_extensions = ["apcu" => null, "decimal" => null, "dom" => null, "ds" => null, "ffi" => null, "geos" => null, "gmp" => null, "ibm_db2" => null, "mongodb" => null, "mysqli" => null, "pdo" => null, "random" => null, "rdkafka" => null, "redis" => null, "simplexml" => null, "soap" => null, "xdebug" => null]; /** * A list of php extensions described in CallMap Psalm files * as opposite to stub files loaded by condition (see stubs/extensions dir). * * @see https://www.php.net/manual/en/extensions.membership.php * @var list * @readonly */ public $php_extensions_supported_by_psalm_callmaps = ['apache', 'bcmath', 'bzip2', 'calendar', 'ctype', 'curl', 'dom', 'enchant', 'exif', 'filter', 'gd', 'gettext', 'gmp', 'hash', 'ibm_db2', 'iconv', 'imap', 'intl', 'json', 'ldap', 'libxml', 'mbstring', 'mysqli', 'mysqlnd', 'mhash', 'oci8', 'opcache', 'openssl', 'pcntl', 'PDO', 'pdo_mysql', 'pdo-sqlite', 'pdo-pgsql', 'pgsql', 'pspell', 'phar', 'phpdbg', 'posix', 'redis', 'readline', 'session', 'sockets', 'sqlite3', 'snmp', 'soap', 'sodium', 'shmop', 'sysvsem', 'tidy', 'tokenizer', 'uodbc', 'xml', 'xmlreader', 'xmlwriter', 'xsl', 'zip', 'zlib']; /** * A list of php extensions required by the project that aren't fully supported by Psalm. * * @var array */ public $php_extensions_not_supported = []; /** * @var array */ private array $plugins = []; /** @var list */ public array $config_warnings = []; /** @internal */ protected function __construct() { self::$instance = $this; $this->eventDispatcher = new EventDispatcher(); $this->universal_object_crates = [strtolower(stdClass::class)]; } /** * Gets a Config object from an XML file. * * Searches up a folder hierarchy for the most immediate config. * * @throws ConfigException if a config path is not found */ public static function getConfigForPath(string $path, string $current_dir) : \Psalm\Config { $config_path = self::locateConfigFile($path); if (!$config_path) { throw new ConfigNotFoundException('Config not found for path ' . $path); } return self::loadFromXMLFile($config_path, $current_dir); } /** * Searches up a folder hierarchy for the most immediate config. * * @throws ConfigException */ public static function locateConfigFile(string $path) : ?string { $dir_path = realpath($path); if ($dir_path === \false) { throw new ConfigNotFoundException('Config not found for path ' . $path); } if (!is_dir($dir_path)) { $dir_path = dirname($dir_path); } do { $maybe_path = $dir_path . DIRECTORY_SEPARATOR . self::DEFAULT_FILE_NAME; if (file_exists($maybe_path) || file_exists($maybe_path .= '.dist')) { return $maybe_path; } $dir_path = dirname($dir_path); } while (dirname($dir_path) !== $dir_path); return null; } /** * Creates a new config object from the file */ public static function loadFromXMLFile(string $file_path, string $current_dir) : \Psalm\Config { $file_contents = file_get_contents($file_path); $base_dir = dirname($file_path) . DIRECTORY_SEPARATOR; if ($file_contents === \false) { throw new InvalidArgumentException('Cannot open ' . $file_path); } if ($file_contents === '') { throw new InvalidArgumentException('Invalid empty file ' . $file_path); } try { $config = self::loadFromXML($base_dir, $file_contents, $current_dir, $file_path); $config->hash = sha1($file_contents . PSALM_VERSION); } catch (ConfigException $e) { throw new ConfigException('Problem parsing ' . $file_path . ":\n" . ' ' . $e->getMessage()); } return $config; } /** * Computes the hash to use for a cache folder from CLI flags and from the config file's xml contents */ public function computeHash() : string { return sha1($this->hash . ':' . $this->level); } /** * Creates a new config object from an XML string * * @param string|null $current_dir Current working directory, if different to $base_dir * @param non-empty-string $file_contents * @throws ConfigException */ public static function loadFromXML(string $base_dir, string $file_contents, ?string $current_dir = null, ?string $file_path = null) : \Psalm\Config { if ($current_dir === null) { $current_dir = $base_dir; } self::validateXmlConfig($base_dir, $file_contents); return self::fromXmlAndPaths($base_dir, $file_contents, $current_dir, $file_path); } /** * @param non-empty-string $file_contents */ private static function loadDomDocument(string $base_dir, string $file_contents) : DOMDocument { $dom_document = new DOMDocument(); // there's no obvious way to set xml:base for a document when loading it from string // so instead we're changing the current directory instead to be able to process XIncludes $oldpwd = getcwd(); chdir($base_dir); $dom_document->loadXML($file_contents, LIBXML_NONET); $dom_document->xinclude(LIBXML_NOWARNING | LIBXML_NONET); chdir($oldpwd); return $dom_document; } /** * @param non-empty-string $file_contents * @throws ConfigException */ private static function validateXmlConfig(string $base_dir, string $file_contents) : void { $schema_path = dirname(__DIR__, 2) . '/config.xsd'; if (!file_exists($schema_path)) { throw new ConfigException('Cannot locate config schema'); } // Enable user error handling $prev_xml_internal_errors = libxml_use_internal_errors(\true); libxml_clear_errors(); $dom_document = self::loadDomDocument($base_dir, $file_contents); $psalm_nodes = $dom_document->getElementsByTagName('psalm'); $psalm_node = $psalm_nodes->item(0); if (!$psalm_node) { throw new ConfigException('Missing psalm node'); } if (!$psalm_node->hasAttribute('xmlns')) { $psalm_node->setAttribute('xmlns', self::CONFIG_NAMESPACE); $old_dom_document = $dom_document; $old_file_contents = $old_dom_document->saveXML(); assert($old_file_contents !== \false && $old_file_contents !== ''); $dom_document = self::loadDomDocument($base_dir, $old_file_contents); } $dom_document->schemaValidate($schema_path); // If it returns false it will generate errors handled below $errors = libxml_get_errors(); libxml_clear_errors(); libxml_use_internal_errors($prev_xml_internal_errors); foreach ($errors as $error) { if ($error->level === LIBXML_ERR_FATAL || $error->level === LIBXML_ERR_ERROR) { throw new ConfigException('Error on line ' . $error->line . ":\n" . ' ' . $error->message); } } } /** * @param positive-int $line_number 1-based line number * @return int 0-based byte offset * @throws OutOfBoundsException */ private static function lineNumberToByteOffset(string $string, int $line_number) : int { if ($line_number === 1) { return 0; } $offset = 0; for ($i = 0; $i < $line_number - 1; $i++) { $newline_offset = strpos($string, "\n", $offset); if (\false === $newline_offset) { throw new OutOfBoundsException('Line ' . $line_number . ' is not found in a string with ' . ($i + 1) . ' lines'); } $offset = $newline_offset + 1; } if ($offset > strlen($string)) { throw new OutOfBoundsException('Line ' . $line_number . ' is not found'); } return $offset; } private static function processDeprecatedAttribute(DOMAttr $attribute, string $file_contents, self $config, string $config_path) : void { $line = $attribute->getLineNo(); assert($line > 0); // getLineNo() always returns non-zero for nodes loaded from file $offset = self::lineNumberToByteOffset($file_contents, $line); $attribute_start = strrpos($file_contents, $attribute->name, $offset - strlen($file_contents)) ?: 0; $attribute_end = $attribute_start + strlen($attribute->name) - 1; $config->config_issues[] = new ConfigIssue('Attribute "' . $attribute->name . '" is deprecated ' . 'and is going to be removed in the next major version', new Raw($file_contents, $config_path, basename($config_path), $attribute_start, $attribute_end)); } private static function processDeprecatedElement(DOMElement $deprecated_element_xml, string $file_contents, self $config, string $config_path) : void { $line = $deprecated_element_xml->getLineNo(); assert($line > 0); $offset = self::lineNumberToByteOffset($file_contents, $line); $element_start = strpos($file_contents, $deprecated_element_xml->localName, $offset) ?: 0; $element_end = $element_start + strlen($deprecated_element_xml->localName) - 1; $config->config_issues[] = new ConfigIssue('Element "' . $deprecated_element_xml->localName . '" is deprecated ' . 'and is going to be removed in the next major version', new Raw($file_contents, $config_path, basename($config_path), $element_start, $element_end)); } private static function processConfigDeprecations(self $config, DOMDocument $dom_document, string $file_contents, string $config_path) : void { $config->config_issues = []; // Attributes to be removed in Psalm 6 $deprecated_attributes = []; /** @var list */ $deprecated_elements = []; $psalm_element_item = $dom_document->getElementsByTagName('psalm')->item(0); assert($psalm_element_item !== null); $attributes = $psalm_element_item->attributes; foreach ($attributes as $attribute) { if (in_array($attribute->name, $deprecated_attributes, \true)) { self::processDeprecatedAttribute($attribute, $file_contents, $config, $config_path); } } foreach ($deprecated_elements as $deprecated_element) { $deprecated_elements_xml = $dom_document->getElementsByTagNameNS(self::CONFIG_NAMESPACE, $deprecated_element); if ($deprecated_elements_xml->length) { $deprecated_element_xml = $deprecated_elements_xml->item(0); self::processDeprecatedElement($deprecated_element_xml, $file_contents, $config, $config_path); } } } /** * @param non-empty-string $file_contents * @psalm-suppress MixedAssignment * @psalm-suppress MixedArgument * @psalm-suppress MixedPropertyFetch * @throws ConfigException */ private static function fromXmlAndPaths(string $base_dir, string $file_contents, string $current_dir, ?string $config_path) : self { $config = new static(); $dom_document = self::loadDomDocument($base_dir, $file_contents); if (null !== $config_path) { self::processConfigDeprecations($config, $dom_document, $file_contents, $config_path); } $config_xml = simplexml_import_dom($dom_document); $booleanAttributes = ['useDocblockTypes' => 'use_docblock_types', 'useDocblockPropertyTypes' => 'use_docblock_property_types', 'docblockPropertyTypesSealProperties' => 'docblock_property_types_seal_properties', 'throwExceptionOnError' => 'throw_exception', 'hideExternalErrors' => 'hide_external_errors', 'hideAllErrorsExceptPassedFiles' => 'hide_all_errors_except_passed_files', 'resolveFromConfigFile' => 'resolve_from_config_file', 'allowFileIncludes' => 'allow_includes', 'strictBinaryOperands' => 'strict_binary_operands', 'rememberPropertyAssignmentsAfterCall' => 'remember_property_assignments_after_call', 'disableVarParsing' => 'disable_var_parsing', 'allowStringToStandInForClass' => 'allow_string_standin_for_class', 'disableSuppressAll' => 'disable_suppress_all', 'usePhpDocMethodsWithoutMagicCall' => 'use_phpdoc_method_without_magic_or_parent', 'usePhpDocPropertiesWithoutMagicCall' => 'use_phpdoc_property_without_magic_or_parent', 'memoizeMethodCallResults' => 'memoize_method_calls', 'hoistConstants' => 'hoist_constants', 'addParamDefaultToDocblockType' => 'add_param_default_to_docblock_type', 'checkForThrowsDocblock' => 'check_for_throws_docblock', 'checkForThrowsInGlobalScope' => 'check_for_throws_in_global_scope', 'ignoreInternalFunctionFalseReturn' => 'ignore_internal_falsable_issues', 'ignoreInternalFunctionNullReturn' => 'ignore_internal_nullable_issues', 'includePhpVersionsInErrorBaseline' => 'include_php_versions_in_error_baseline', 'ensureArrayStringOffsetsExist' => 'ensure_array_string_offsets_exist', 'ensureArrayIntOffsetsExist' => 'ensure_array_int_offsets_exist', 'reportMixedIssues' => 'show_mixed_issues', 'skipChecksOnUnresolvableIncludes' => 'skip_checks_on_unresolvable_includes', 'sealAllMethods' => 'seal_all_methods', 'sealAllProperties' => 'seal_all_properties', 'runTaintAnalysis' => 'run_taint_analysis', 'usePhpStormMetaPath' => 'use_phpstorm_meta_path', 'allowInternalNamedArgumentsCalls' => 'allow_internal_named_arg_calls', 'allowNamedArgumentCalls' => 'allow_named_arg_calls', 'findUnusedPsalmSuppress' => 'find_unused_psalm_suppress', 'findUnusedBaselineEntry' => 'find_unused_baseline_entry', 'reportInfo' => 'report_info', 'restrictReturnTypes' => 'restrict_return_types', 'limitMethodComplexity' => 'limit_method_complexity']; foreach ($booleanAttributes as $xmlName => $internalName) { if (isset($config_xml[$xmlName])) { $attribute_text = (string) $config_xml[$xmlName]; $config->setBooleanAttribute($internalName, $attribute_text === 'true' || $attribute_text === '1'); } } if ($config->resolve_from_config_file) { $config->base_dir = $base_dir; } else { $config->base_dir = $current_dir; $base_dir = $current_dir; } $composer_json_path = Composer::getJsonFilePath($config->base_dir); $composer_json = null; if (file_exists($composer_json_path)) { $composer_json = json_decode(file_get_contents($composer_json_path), \true); if (!is_array($composer_json)) { throw new UnexpectedValueException('Invalid composer.json at ' . $composer_json_path); } } $required_extensions = []; foreach ($composer_json["require"] ?? [] as $required => $_) { if (strpos($required, "ext-") === 0) { $required_extensions[strtolower(substr($required, 4))] = \true; } } foreach ($required_extensions as $required_ext => $_) { if (array_key_exists($required_ext, $config->php_extensions)) { $config->php_extensions[$required_ext] = \true; } else { $config->php_extensions_not_supported[$required_ext] = \true; } } if (isset($config_xml->enableExtensions) && isset($config_xml->enableExtensions->extension)) { foreach ($config_xml->enableExtensions->extension as $extension) { assert(isset($extension["name"])); $extensionName = (string) $extension["name"]; assert(array_key_exists($extensionName, $config->php_extensions)); $config->php_extensions[$extensionName] = \true; } } if (isset($config_xml->disableExtensions) && isset($config_xml->disableExtensions->extension)) { foreach ($config_xml->disableExtensions->extension as $extension) { assert(isset($extension["name"])); $extensionName = (string) $extension["name"]; assert(array_key_exists($extensionName, $config->php_extensions)); $config->php_extensions[$extensionName] = \false; } } if (isset($config_xml['phpVersion'])) { $config->configured_php_version = (string) $config_xml['phpVersion']; } if (isset($config_xml['autoloader'])) { $autoloader = (string) $config_xml['autoloader']; $autoloader_path = $config->base_dir . DIRECTORY_SEPARATOR . $autoloader; if (!file_exists($autoloader_path)) { // in here for legacy reasons where people put absolute paths but psalm resolved it relative if ($autoloader[0] === '/') { $autoloader_path = $autoloader; } if (!file_exists($autoloader_path)) { throw new ConfigException('Cannot locate autoloader'); } } $config->autoloader = realpath($autoloader_path); } if (isset($config_xml['cacheDirectory'])) { $config->cache_directory = (string) $config_xml['cacheDirectory']; } elseif ($user_cache_dir = (new Xdg())->getHomeCacheDir()) { $config->cache_directory = $user_cache_dir . '/psalm'; } else { $config->cache_directory = sys_get_temp_dir() . '/psalm'; } $config->global_cache_directory = $config->cache_directory; $config->cache_directory .= DIRECTORY_SEPARATOR . sha1($base_dir); if (isset($config_xml['serializer'])) { $attribute_text = (string) $config_xml['serializer']; $config->use_igbinary = $attribute_text === 'igbinary'; if ($config->use_igbinary && (!function_exists('igbinary_serialize') || !function_exists('igbinary_unserialize'))) { $config->use_igbinary = \false; $config->config_warnings[] = '"serializer" set to "igbinary" but ext-igbinary seems to be missing on ' . 'the system. Using php\'s build-in serializer.'; } } elseif ($igbinary_version = phpversion('igbinary')) { $config->use_igbinary = version_compare($igbinary_version, '2.0.5') >= 0; } if (isset($config_xml['compressor'])) { $compressor = (string) $config_xml['compressor']; if ($compressor === 'lz4') { if (function_exists('_HumbugBox427cd2d3980b\\lz4_compress') && function_exists('_HumbugBox427cd2d3980b\\lz4_uncompress')) { $config->compressor = 'lz4'; } else { $config->config_warnings[] = '"compressor" set to "lz4" but ext-lz4 seems to be missing on the ' . 'system. Disabling cache compressor.'; } } elseif ($compressor === 'deflate') { if (function_exists('gzinflate') && function_exists('gzdeflate')) { $config->compressor = 'deflate'; } else { $config->config_warnings[] = '"compressor" set to "deflate" but zlib seems to be missing on the ' . 'system. Disabling cache compressor.'; } } } elseif (function_exists('gzinflate') && function_exists('gzdeflate')) { $config->compressor = 'deflate'; } if (!isset($config_xml['findUnusedBaselineEntry'])) { $config->config_warnings[] = '"findUnusedBaselineEntry" will default to "true" in Psalm 6.' . ' You should explicitly enable or disable this setting.'; } if (isset($config_xml['findUnusedCode'])) { $attribute_text = (string) $config_xml['findUnusedCode']; $config->find_unused_code = $attribute_text === 'true' || $attribute_text === '1'; $config->find_unused_variables = $config->find_unused_code; } else { $config->config_warnings[] = '"findUnusedCode" will default to "true" in Psalm 6.' . ' You should explicitly enable or disable this setting.'; } if (isset($config_xml['findUnusedVariablesAndParams'])) { $attribute_text = (string) $config_xml['findUnusedVariablesAndParams']; $config->find_unused_variables = $attribute_text === 'true' || $attribute_text === '1'; } if (isset($config_xml['errorLevel'])) { $attribute_text = (int) $config_xml['errorLevel']; if (!in_array($attribute_text, [1, 2, 3, 4, 5, 6, 7, 8], \true)) { throw new ConfigException('Invalid error level ' . $config_xml['errorLevel']); } $config->level = $attribute_text; } else { $config->level = 2; } // turn on unused variable detection in level 1 if (!isset($config_xml['findUnusedCode']) && !isset($config_xml['findUnusedVariablesAndParams']) && $config->level === 1 && $config->show_mixed_issues !== \false) { $config->find_unused_variables = \true; } if (isset($config_xml['errorBaseline'])) { $attribute_text = (string) $config_xml['errorBaseline']; $config->error_baseline = $attribute_text; } if (isset($config_xml['maxStringLength'])) { $attribute_text = (int) $config_xml['maxStringLength']; $config->max_string_length = $attribute_text; } if (isset($config_xml['maxShapedArraySize'])) { $attribute_text = (int) $config_xml['maxShapedArraySize']; $config->max_shaped_array_size = $attribute_text; } if (isset($config_xml['inferPropertyTypesFromConstructor'])) { $attribute_text = (string) $config_xml['inferPropertyTypesFromConstructor']; $config->infer_property_types_from_constructor = $attribute_text === 'true' || $attribute_text === '1'; } if (isset($config_xml['triggerErrorExits'])) { $attribute_text = (string) $config_xml['triggerErrorExits']; if ($attribute_text === 'always' || $attribute_text === 'never') { $config->trigger_error_exits = $attribute_text; } } if (isset($config_xml->projectFiles)) { $config->project_files = ProjectFileFilter::loadFromXMLElement($config_xml->projectFiles, $base_dir, \true); } // any paths passed via CLI should be added to the projectFiles // as they're getting analyzed like if they are part of the project // ProjectAnalyzer::getInstance()->check_paths_files is not populated at this point in time $paths_to_check = CliUtils::getPathsToCheck(null); if ($paths_to_check !== null) { $paths_to_add_to_project_files = array(); foreach ($paths_to_check as $path) { // if we have an .xml arg here, the files passed are invalid // valid cases (in which we don't want to add CLI passed files to projectFiles though) // are e.g. if running phpunit tests for psalm itself if (substr($path, -4) === '.xml') { $paths_to_add_to_project_files = array(); break; } // we need an absolute path for checks if (Path::isRelative($path)) { $prospective_path = $base_dir . DIRECTORY_SEPARATOR . $path; } else { $prospective_path = $path; } // will report an error when config is loaded anyway if (!file_exists($prospective_path)) { continue; } if ($config->isInProjectDirs($prospective_path)) { continue; } $paths_to_add_to_project_files[] = $prospective_path; } if ($paths_to_add_to_project_files !== array() && !isset($config_xml->projectFiles)) { if ($config_xml === null) { $config_xml = new SimpleXMLElement(''); } $config_xml->addChild('projectFiles'); } if ($paths_to_add_to_project_files !== array() && isset($config_xml->projectFiles)) { foreach ($paths_to_add_to_project_files as $path) { if (is_dir($path)) { $child = $config_xml->projectFiles->addChild('directory'); } else { $child = $config_xml->projectFiles->addChild('file'); } $child->addAttribute('name', $path); } $config->project_files = ProjectFileFilter::loadFromXMLElement($config_xml->projectFiles, $base_dir, \true); } } if (isset($config_xml->extraFiles)) { $config->extra_files = ProjectFileFilter::loadFromXMLElement($config_xml->extraFiles, $base_dir, \true); } if (isset($config_xml->taintAnalysis->ignoreFiles)) { $config->taint_analysis_ignored_files = TaintAnalysisFileFilter::loadFromXMLElement($config_xml->taintAnalysis->ignoreFiles, $base_dir, \false); } if (isset($config_xml->fileExtensions->extension)) { $config->file_extensions = []; $config->loadFileExtensions($config_xml->fileExtensions->extension); } if (isset($config_xml->mockClasses) && isset($config_xml->mockClasses->class)) { /** @var SimpleXMLElement $mock_class */ foreach ($config_xml->mockClasses->class as $mock_class) { $config->mock_classes[] = strtolower((string) $mock_class['name']); } } if (isset($config_xml->universalObjectCrates) && isset($config_xml->universalObjectCrates->class)) { /** @var SimpleXMLElement $universal_object_crate */ foreach ($config_xml->universalObjectCrates->class as $universal_object_crate) { /** @var string $classString */ $classString = $universal_object_crate['name']; $config->addUniversalObjectCrate($classString); } } if (isset($config_xml->ignoreExceptions)) { if (isset($config_xml->ignoreExceptions->class)) { foreach ($config_xml->ignoreExceptions->class as $exception_class) { $exception_name = (string) $exception_class['name']; $global_attribute_text = (string) $exception_class['onlyGlobalScope']; if ($global_attribute_text !== 'true' && $global_attribute_text !== '1') { $config->ignored_exceptions[$exception_name] = \true; } $config->ignored_exceptions_in_global_scope[$exception_name] = \true; } } if (isset($config_xml->ignoreExceptions->classAndDescendants)) { foreach ($config_xml->ignoreExceptions->classAndDescendants as $exception_class) { $exception_name = (string) $exception_class['name']; $global_attribute_text = (string) $exception_class['onlyGlobalScope']; if ($global_attribute_text !== 'true' && $global_attribute_text !== '1') { $config->ignored_exceptions_and_descendants[$exception_name] = \true; } $config->ignored_exceptions_and_descendants_in_global_scope[$exception_name] = \true; } } } if (isset($config_xml->forbiddenFunctions) && isset($config_xml->forbiddenFunctions->function)) { /** @var SimpleXMLElement $forbidden_function */ foreach ($config_xml->forbiddenFunctions->function as $forbidden_function) { $config->forbidden_functions[strtolower((string) $forbidden_function['name'])] = \true; } } if (isset($config_xml->stubs) && isset($config_xml->stubs->file)) { /** @var SimpleXMLElement $stub_file */ foreach ($config_xml->stubs->file as $stub_file) { $stub_file_name = (string) $stub_file['name']; if (!Path::isAbsolute($stub_file_name)) { $stub_file_name = $config->base_dir . DIRECTORY_SEPARATOR . $stub_file_name; } $file_path = realpath($stub_file_name); if (!$file_path) { throw new ConfigException('Cannot resolve stubfile path ' . rtrim($config->base_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $stub_file['name']); } if (isset($stub_file['preloadClasses'])) { $preload_classes = (string) $stub_file['preloadClasses']; if ($preload_classes === 'true' || $preload_classes === '1') { $config->addPreloadedStubFile($file_path); } else { $config->addStubFile($file_path); } } else { $config->addStubFile($file_path); } } } // this plugin loading system borrows heavily from etsy/phan if (isset($config_xml->plugins)) { if (isset($config_xml->plugins->plugin)) { foreach ($config_xml->plugins->plugin as $plugin) { $plugin_file_name = (string) $plugin['filename']; $path = Path::isAbsolute($plugin_file_name) ? $plugin_file_name : $config->base_dir . $plugin_file_name; $config->addPluginPath($path); } } if (isset($config_xml->plugins->pluginClass)) { foreach ($config_xml->plugins->pluginClass as $plugin) { $plugin_class_name = $plugin['class']; // any child elements are used as plugin configuration $plugin_config = null; if ($plugin->count()) { $plugin_config = $plugin->children(); } $config->addPluginClass((string) $plugin_class_name, $plugin_config); } } } if (isset($config_xml->issueHandlers)) { foreach ($config_xml->issueHandlers as $issue_handlers) { $issue_handler_children = $issue_handlers->children(); if ($issue_handler_children) { foreach ($issue_handler_children as $key => $issue_handler) { if ($key === 'PluginIssue') { $custom_class_name = (string) $issue_handler['name']; /** @var string $key */ $config->issue_handlers[$custom_class_name] = IssueHandler::loadFromXMLElement($issue_handler, $base_dir); } else { /** @var string $key */ $config->issue_handlers[$key] = IssueHandler::loadFromXMLElement($issue_handler, $base_dir); } } } } } if (isset($config_xml->globals) && isset($config_xml->globals->var)) { /** @var SimpleXMLElement $var */ foreach ($config_xml->globals->var as $var) { $config->globals['$' . (string) $var['name']] = (string) $var['type']; } } if (isset($config_xml['threads'])) { $config->threads = (int) $config_xml['threads']; } return $config; } public static function getInstance() : \Psalm\Config { if (self::$instance) { return self::$instance; } throw new UnexpectedValueException('No config initialized'); } public function setComposerClassLoader(?ClassLoader $loader = null) : void { $this->composer_class_loader = $loader; } public function setAdvancedErrorLevel(string $issue_key, array $config, ?string $default_error_level = null) : void { $this->issue_handlers[$issue_key] = new IssueHandler(); if ($default_error_level !== null) { $this->issue_handlers[$issue_key]->setErrorLevel($default_error_level); } $this->issue_handlers[$issue_key]->setCustomLevels($config, $this->base_dir); } public function safeSetAdvancedErrorLevel(string $issue_key, array $config, ?string $default_error_level = null) : void { if (!isset($this->issue_handlers[$issue_key])) { $this->setAdvancedErrorLevel($issue_key, $config, $default_error_level); } } public function setCustomErrorLevel(string $issue_key, string $error_level) : void { $this->issue_handlers[$issue_key] = new IssueHandler(); $this->issue_handlers[$issue_key]->setErrorLevel($error_level); } public function safeSetCustomErrorLevel(string $issue_key, string $error_level) : void { if (!isset($this->issue_handlers[$issue_key])) { $this->setCustomErrorLevel($issue_key, $error_level); } } /** * @throws ConfigException if a Config file could not be found */ private function loadFileExtensions(SimpleXMLElement $extensions) : void { foreach ($extensions as $extension) { $extension_name = preg_replace('/^\\.?/', '', (string) $extension['name'], 1); $this->file_extensions[] = $extension_name; if (isset($extension['scanner'])) { $path = $this->base_dir . (string) $extension['scanner']; if (!file_exists($path)) { throw new ConfigException('Error parsing config: cannot find file ' . $path); } $this->filetype_scanner_paths[$extension_name] = $path; } if (isset($extension['checker'])) { $path = $this->base_dir . (string) $extension['checker']; if (!file_exists($path)) { throw new ConfigException('Error parsing config: cannot find file ' . $path); } $this->filetype_analyzer_paths[$extension_name] = $path; } } } public function addPluginPath(string $path) : void { if (!file_exists($path)) { throw new InvalidArgumentException('Cannot find plugin file ' . $path); } $this->plugin_paths[] = $path; } public function addPluginClass(string $class_name, ?SimpleXMLElement $plugin_config = null) : void { $this->plugin_classes[] = ['class' => $class_name, 'config' => $plugin_config]; } /** @return array */ public function getPluginClasses() : array { return $this->plugin_classes; } public function processPluginFileExtensions(ProjectAnalyzer $projectAnalyzer) : void { $projectAnalyzer->progress->debug('Process plugin adjustments...' . PHP_EOL); $socket = new \Psalm\PluginFileExtensionsSocket($this); foreach ($this->plugin_classes as $pluginClassEntry) { $pluginClassName = $pluginClassEntry['class']; $pluginConfig = $pluginClassEntry['config']; $plugin = $this->loadPlugin($projectAnalyzer, $pluginClassName); if (!$plugin instanceof PluginFileExtensionsInterface) { continue; } try { $plugin->processFileExtensions($socket, $pluginConfig); } catch (Throwable $t) { throw new ConfigException('Failed to process plugin file extensions ' . $pluginClassName, 1635800581, $t); } $projectAnalyzer->progress->debug('Initialized plugin ' . $pluginClassName . ' successfully' . PHP_EOL); } // populate additional aspects after plugins have been initialized foreach ($socket->getAdditionalFileExtensions() as $fileExtension) { $this->file_extensions[] = $fileExtension; } foreach ($socket->getAdditionalFileTypeScanners() as $extension => $className) { $this->filetype_scanners[$extension] = $className; } foreach ($socket->getAdditionalFileTypeAnalyzers() as $extension => $className) { $this->filetype_analyzers[$extension] = $className; } } /** * Initialises all the plugins (done once the config is fully loaded) */ public function initializePlugins(ProjectAnalyzer $project_analyzer) : void { $codebase = $project_analyzer->getCodebase(); $project_analyzer->progress->debug('Initializing plugins...' . PHP_EOL); $socket = new \Psalm\PluginRegistrationSocket($this, $codebase); // initialize plugin classes earlier to let them hook into subsequent load process foreach ($this->plugin_classes as $plugin_class_entry) { $plugin_class_name = $plugin_class_entry['class']; $plugin_config = $plugin_class_entry['config']; $plugin = $this->loadPlugin($project_analyzer, $plugin_class_name); if (!$plugin instanceof PluginEntryPointInterface) { continue; } try { $plugin($socket, $plugin_config); } catch (Throwable $t) { throw new ConfigException('Failed to invoke plugin ' . $plugin_class_name, 1635800582, $t); } $project_analyzer->progress->debug('Initialized plugin ' . $plugin_class_name . ' successfully' . PHP_EOL); } foreach ($this->filetype_scanner_paths as $extension => $path) { $fq_class_name = $this->getPluginClassForPath($codebase, $path, FileScanner::class); self::requirePath($path); $this->filetype_scanners[$extension] = $fq_class_name; } foreach ($this->filetype_analyzer_paths as $extension => $path) { $fq_class_name = $this->getPluginClassForPath($codebase, $path, FileAnalyzer::class); self::requirePath($path); $this->filetype_analyzers[$extension] = $fq_class_name; } foreach ($this->plugin_paths as $path) { try { $plugin = new \Psalm\FileBasedPluginAdapter($path, $this, $codebase); $plugin($socket); } catch (Throwable $e) { throw new ConfigException('Failed to load plugin ' . $path, 0, $e); } } new HtmlFunctionTainter(); $socket->registerHooksFromClass(HtmlFunctionTainter::class); } private function loadPlugin(ProjectAnalyzer $projectAnalyzer, string $pluginClassName) : PluginInterface { if (isset($this->plugins[$pluginClassName])) { return $this->plugins[$pluginClassName]; } try { // Below will attempt to load plugins from the project directory first. // Failing that, it will use registered autoload chain, which will load // plugins from Psalm directory or phar file. If that fails as well, it // will fall back to project autoloader. It may seem that the last step // will always fail, but it's only true if project uses Composer autoloader if ($this->composer_class_loader && ($pluginclas_class_path = $this->composer_class_loader->findFile($pluginClassName))) { $projectAnalyzer->progress->debug('Loading plugin ' . $pluginClassName . ' via require' . PHP_EOL); self::requirePath($pluginclas_class_path); } else { if (!class_exists($pluginClassName)) { throw new UnexpectedValueException($pluginClassName . ' is not a known class'); } } if (!is_a($pluginClassName, PluginInterface::class, \true)) { throw new UnexpectedValueException($pluginClassName . ' is not a PluginInterface implementation'); } $this->plugins[$pluginClassName] = new $pluginClassName(); $projectAnalyzer->progress->debug('Loaded plugin ' . $pluginClassName . PHP_EOL); return $this->plugins[$pluginClassName]; } catch (Throwable $e) { throw new ConfigException('Failed to load plugin ' . $pluginClassName, 0, $e); } } private static function requirePath(string $path) : void { /** @psalm-suppress UnresolvableInclude */ require_once $path; } /** * @template T * @param T::class $must_extend * @return class-string */ private function getPluginClassForPath(\Psalm\Codebase $codebase, string $path, string $must_extend) : string { $file_storage = $codebase->createFileStorageForPath($path); $file_to_scan = new FileScanner($path, $this->shortenFileName($path), \true); $file_to_scan->scan($codebase, $file_storage); $declared_classes = ClassLikeAnalyzer::getClassesForFile($codebase, $path); if (!count($declared_classes)) { throw new InvalidArgumentException('Plugins must have at least one class in the file - ' . $path . ' has ' . count($declared_classes)); } $fq_class_name = reset($declared_classes); if (!$codebase->classlikes->classExtends($fq_class_name, $must_extend)) { throw new InvalidArgumentException('This plugin must extend ' . $must_extend . ' - ' . $path . ' does not'); } /** * @var class-string */ return $fq_class_name; } public function shortenFileName(string $to) : string { if (!is_file($to)) { return preg_replace('/^' . preg_quote($this->base_dir, '/') . '/', '', $to, 1); } $from = $this->base_dir; // some compatibility fixes for Windows paths $from = is_dir($from) ? rtrim($from, '\\/') . '/' : $from; $to = is_dir($to) ? rtrim($to, '\\/') . '/' : $to; $from = str_replace('\\', '/', $from); $to = str_replace('\\', '/', $to); $from = explode('/', $from); $to = explode('/', $to); $relPath = $to; foreach ($from as $depth => $dir) { // find first non-matching dir if ($dir === $to[$depth]) { // ignore this directory array_shift($relPath); } else { // get number of remaining dirs to $from $remaining = count($from) - $depth; if ($remaining > 1) { // add traversals up to first matching dir $padLength = (count($relPath) + $remaining - 1) * -1; $relPath = array_pad($relPath, $padLength, '..'); break; } } } return implode('/', $relPath); } public function reportIssueInFile(string $issue_type, string $file_path) : bool { if (($this->level < 3 && $this->show_mixed_issues === \false || $this->level > 2 && $this->show_mixed_issues !== \true) && in_array($issue_type, self::MIXED_ISSUES, \true)) { return \false; } if ($this->mustBeIgnored($file_path)) { return \false; } $dependent_files = [strtolower($file_path) => $file_path]; $project_analyzer = ProjectAnalyzer::getInstance(); // if the option is set and at least one file is passed via CLI if ($this->hide_all_errors_except_passed_files && $project_analyzer->check_paths_files && !in_array($file_path, $project_analyzer->check_paths_files, \true)) { return \false; } $codebase = $project_analyzer->getCodebase(); if (!$this->hide_external_errors) { try { $file_storage = $codebase->file_storage_provider->get($file_path); $dependent_files += $file_storage->required_by_file_paths; } catch (InvalidArgumentException $e) { // do nothing } } $any_file_path_matched = \false; foreach ($dependent_files as $dependent_file_path) { if ((!$project_analyzer->full_run && $codebase->analyzer->canReportIssues($dependent_file_path) || $project_analyzer->canReportIssues($dependent_file_path)) && ($file_path === $dependent_file_path || !$this->mustBeIgnored($dependent_file_path))) { $any_file_path_matched = \true; break; } } if (!$any_file_path_matched) { return \false; } if ($this->getReportingLevelForFile($issue_type, $file_path) === self::REPORT_SUPPRESS) { return \false; } return \true; } public function isInProjectDirs(string $file_path) : bool { return $this->project_files && $this->project_files->allows($file_path); } public function isInExtraDirs(string $file_path) : bool { return $this->extra_files && $this->extra_files->allows($file_path); } public function mustBeIgnored(string $file_path) : bool { return $this->project_files && $this->project_files->forbids($file_path); } public function trackTaintsInPath(string $file_path) : bool { return !$this->taint_analysis_ignored_files || $this->taint_analysis_ignored_files->allows($file_path); } public function getReportingLevelForIssue(CodeIssue $e) : string { $fqcn_parts = explode('\\', get_class($e)); $issue_type = array_pop($fqcn_parts); $reporting_level = null; if ($e instanceof ClassIssue) { $reporting_level = $this->getReportingLevelForClass($issue_type, $e->fq_classlike_name); } elseif ($e instanceof MethodIssue) { $reporting_level = $this->getReportingLevelForMethod($issue_type, $e->method_id); } elseif ($e instanceof FunctionIssue) { $reporting_level = $this->getReportingLevelForFunction($issue_type, $e->function_id); } elseif ($e instanceof PropertyIssue) { $reporting_level = $this->getReportingLevelForProperty($issue_type, $e->property_id); } elseif ($e instanceof ClassConstantIssue) { $reporting_level = $this->getReportingLevelForClassConstant($issue_type, $e->const_id); } elseif ($e instanceof ArgumentIssue && $e->function_id) { $reporting_level = $this->getReportingLevelForArgument($issue_type, $e->function_id); } elseif ($e instanceof VariableIssue) { $reporting_level = $this->getReportingLevelForVariable($issue_type, $e->var_name); } if ($reporting_level === null) { $reporting_level = $this->getReportingLevelForFile($issue_type, $e->getFilePath()); } if (!$this->report_info && $reporting_level === self::REPORT_INFO) { $reporting_level = self::REPORT_SUPPRESS; } $parent_issue_type = self::getParentIssueType($issue_type); if ($parent_issue_type && $reporting_level === self::REPORT_ERROR) { $parent_reporting_level = $this->getReportingLevelForFile($parent_issue_type, $e->getFilePath()); if ($parent_reporting_level !== $reporting_level) { return $parent_reporting_level; } } return $reporting_level; } /** * @psalm-pure */ public static function getParentIssueType(string $issue_type) : ?string { if ($issue_type === 'PossiblyUndefinedIntArrayOffset' || $issue_type === 'PossiblyUndefinedStringArrayOffset') { return 'PossiblyUndefinedArrayOffset'; } if ($issue_type === 'PossiblyNullReference') { return 'NullReference'; } if ($issue_type === 'PossiblyFalseReference') { return null; } if ($issue_type === 'PossiblyUndefinedArrayOffset') { return null; } if (strpos($issue_type, 'Possibly') === 0) { $stripped_issue_type = preg_replace('/^Possibly(False|Null)?/', '', $issue_type, 1); if (strpos($stripped_issue_type, 'Invalid') === \false && strpos($stripped_issue_type, 'Un') !== 0) { $stripped_issue_type = 'Invalid' . $stripped_issue_type; } return $stripped_issue_type; } if (strpos($issue_type, 'Tainted') === 0) { return 'TaintedInput'; } if (preg_match('/^(False|Null)[A-Z]/', $issue_type) && !strpos($issue_type, 'Reference')) { return preg_replace('/^(False|Null)/', 'Invalid', $issue_type, 1); } if ($issue_type === 'UndefinedInterfaceMethod') { return 'UndefinedMethod'; } if ($issue_type === 'UndefinedMagicPropertyFetch') { return 'UndefinedPropertyFetch'; } if ($issue_type === 'UndefinedMagicPropertyAssignment') { return 'UndefinedPropertyAssignment'; } if ($issue_type === 'UndefinedMagicMethod') { return 'UndefinedMethod'; } if ($issue_type === 'PossibleRawObjectIteration') { return 'RawObjectIteration'; } if ($issue_type === 'UninitializedProperty') { return 'PropertyNotSetInConstructor'; } if ($issue_type === 'InvalidDocblockParamName') { return 'InvalidDocblock'; } if ($issue_type === 'UnusedClosureParam') { return 'UnusedParam'; } if ($issue_type === 'UnusedConstructor') { return 'UnusedMethod'; } if ($issue_type === 'StringIncrement') { return 'InvalidOperand'; } if ($issue_type === 'InvalidLiteralArgument') { return 'InvalidArgument'; } if ($issue_type === 'RedundantConditionGivenDocblockType') { return 'RedundantCondition'; } if ($issue_type === 'RedundantFunctionCallGivenDocblockType') { return 'RedundantFunctionCall'; } if ($issue_type === 'RedundantCastGivenDocblockType') { return 'RedundantCast'; } if ($issue_type === 'TraitMethodSignatureMismatch') { return 'MethodSignatureMismatch'; } if ($issue_type === 'ImplementedParamTypeMismatch') { return 'MoreSpecificImplementedParamType'; } if ($issue_type === 'UndefinedDocblockClass') { return 'UndefinedClass'; } if ($issue_type === 'UnusedForeachValue') { return 'UnusedVariable'; } return null; } public function getReportingLevelForFile(string $issue_type, string $file_path) : string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForFile($file_path); } // this string is replaced by scoper for Phars, so be careful $issue_class = 'Psalm\\Issue\\' . $issue_type; if (!class_exists($issue_class) || !is_a($issue_class, CodeIssue::class, \true)) { return self::REPORT_ERROR; } /** @var int */ $issue_level = $issue_class::ERROR_LEVEL; if ($issue_level > 0 && $issue_level < $this->level) { return self::REPORT_INFO; } return self::REPORT_ERROR; } public function getReportingLevelForClass(string $issue_type, string $fq_classlike_name) : ?string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForClass($fq_classlike_name); } return null; } public function getReportingLevelForMethod(string $issue_type, string $method_id) : ?string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForMethod($method_id); } return null; } public function getReportingLevelForFunction(string $issue_type, string $function_id) : ?string { $level = null; if (isset($this->issue_handlers[$issue_type])) { $level = $this->issue_handlers[$issue_type]->getReportingLevelForFunction($function_id); if ($level === null && $issue_type === 'UndefinedFunction') { // undefined functions trigger global namespace fallback // so we should also check reporting levels for the symbol in global scope $root_function_id = preg_replace('/.*\\\\/', '', $function_id); if ($root_function_id !== $function_id) { /** @psalm-suppress PossiblyUndefinedStringArrayOffset https://github.com/vimeo/psalm/issues/7656 */ $level = $this->issue_handlers[$issue_type]->getReportingLevelForFunction($root_function_id); } } } return $level; } public function getReportingLevelForArgument(string $issue_type, string $function_id) : ?string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForArgument($function_id); } return null; } public function getReportingLevelForProperty(string $issue_type, string $property_id) : ?string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForProperty($property_id); } return null; } public function getReportingLevelForClassConstant(string $issue_type, string $constant_id) : ?string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForClassConstant($constant_id); } return null; } public function getReportingLevelForVariable(string $issue_type, string $var_name) : ?string { if (isset($this->issue_handlers[$issue_type])) { return $this->issue_handlers[$issue_type]->getReportingLevelForVariable($var_name); } return null; } /** * @return array */ public function getProjectDirectories() : array { if (!$this->project_files) { return []; } return $this->project_files->getDirectories(); } /** * @return array */ public function getProjectFiles() : array { if (!$this->project_files) { return []; } return $this->project_files->getFiles(); } /** * @return array */ public function getExtraDirectories() : array { if (!$this->extra_files) { return []; } return $this->extra_files->getDirectories(); } public function reportTypeStatsForFile(string $file_path) : bool { return $this->project_files && $this->project_files->allows($file_path) && $this->project_files->reportTypeStats($file_path); } public function useStrictTypesForFile(string $file_path) : bool { return $this->project_files && $this->project_files->useStrictTypes($file_path); } /** * @return array */ public function getFileExtensions() : array { return $this->file_extensions; } /** * @return array> */ public function getFiletypeScanners() : array { return $this->filetype_scanners; } /** * @return array> */ public function getFiletypeAnalyzers() : array { return $this->filetype_analyzers; } /** * @return array */ public function getMockClasses() : array { return $this->mock_classes; } public function visitPreloadedStubFiles(\Psalm\Codebase $codebase, ?Progress $progress = null) : void { if ($progress === null) { $progress = new VoidProgress(); } $core_generic_files = []; if (PHP_VERSION_ID < 80000 && $codebase->analysis_php_version_id >= 80000) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php80.phpstub'; if (!file_exists($stringable_path)) { throw new UnexpectedValueException('Cannot locate PHP 8.0 classes'); } $core_generic_files[] = $stringable_path; } if (PHP_VERSION_ID < 80100 && $codebase->analysis_php_version_id >= 80100) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php81.phpstub'; if (!file_exists($stringable_path)) { throw new UnexpectedValueException('Cannot locate PHP 8.1 classes'); } $core_generic_files[] = $stringable_path; } if (PHP_VERSION_ID < 80200 && $codebase->analysis_php_version_id >= 80200) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php82.phpstub'; if (!file_exists($stringable_path)) { throw new UnexpectedValueException('Cannot locate PHP 8.2 classes'); } $core_generic_files[] = $stringable_path; } $stub_files = array_merge($core_generic_files, $this->preloaded_stub_files); if (!$stub_files) { return; } foreach ($stub_files as $file_path) { $file_path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $file_path); $codebase->scanner->addFileToDeepScan($file_path); } $progress->debug('Registering preloaded stub files' . "\n"); $codebase->register_stub_files = \true; $codebase->scanFiles(); $codebase->register_stub_files = \false; $progress->debug('Finished registering preloaded stub files' . "\n"); } public function visitStubFiles(\Psalm\Codebase $codebase, ?Progress $progress = null) : void { if ($progress === null) { $progress = new VoidProgress(); } $codebase->register_stub_files = \true; $dir_lvl_2 = dirname(__DIR__, 2); $stubsDir = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR; $this->internal_stubs = [$stubsDir . 'CoreGenericFunctions.phpstub', $stubsDir . 'CoreGenericClasses.phpstub', $stubsDir . 'CoreGenericIterators.phpstub', $stubsDir . 'CoreImmutableClasses.phpstub', $stubsDir . 'Reflection.phpstub', $stubsDir . 'SPL.phpstub']; if ($codebase->analysis_php_version_id >= 70400) { $this->internal_stubs[] = $stubsDir . 'Php74.phpstub'; } if ($codebase->analysis_php_version_id >= 80000) { $this->internal_stubs[] = $stubsDir . 'CoreGenericAttributes.phpstub'; $this->internal_stubs[] = $stubsDir . 'Php80.phpstub'; } if ($codebase->analysis_php_version_id >= 80100) { $this->internal_stubs[] = $stubsDir . 'Php81.phpstub'; } if ($codebase->analysis_php_version_id >= 80200) { $this->internal_stubs[] = $stubsDir . 'Php82.phpstub'; $this->php_extensions['random'] = \true; // random is a part of the PHP core starting from PHP 8.2 } $ext_stubs_dir = $dir_lvl_2 . DIRECTORY_SEPARATOR . "stubs" . DIRECTORY_SEPARATOR . "extensions"; foreach ($this->php_extensions as $ext => $enabled) { if ($enabled) { $this->internal_stubs[] = $ext_stubs_dir . DIRECTORY_SEPARATOR . "{$ext}.phpstub"; } } /** @deprecated Will be removed in Psalm 6 */ $extensions_to_load_stubs_using_deprecated_way = ['apcu', 'random', 'redis']; foreach ($extensions_to_load_stubs_using_deprecated_way as $ext_name) { $ext_stub_path = $ext_stubs_dir . DIRECTORY_SEPARATOR . "{$ext_name}.phpstub"; $is_stub_already_loaded = in_array($ext_stub_path, $this->internal_stubs, \true); $is_ext_explicitly_disabled = ($this->php_extensions[$ext_name] ?? null) === \false; if (!$is_stub_already_loaded && !$is_ext_explicitly_disabled && extension_loaded($ext_name)) { $this->internal_stubs[] = $ext_stub_path; $this->config_warnings[] = "Psalm 6 will not automatically load stubs for ext-{$ext_name}." . " You should explicitly enable or disable this ext in composer.json or Psalm config."; } } foreach ($this->internal_stubs as $stub_path) { if (!file_exists($stub_path)) { throw new UnexpectedValueException('Cannot locate ' . $stub_path); } } $stub_files = array_merge($this->internal_stubs, $this->stub_files); $phpstorm_meta_path = $this->base_dir . DIRECTORY_SEPARATOR . '.phpstorm.meta.php'; if ($this->use_phpstorm_meta_path) { if (is_file($phpstorm_meta_path)) { $stub_files[] = $phpstorm_meta_path; } elseif (is_dir($phpstorm_meta_path)) { $phpstorm_meta_path = realpath($phpstorm_meta_path); foreach (glob($phpstorm_meta_path . '/*.meta.php', GLOB_NOSORT) as $glob) { if (is_file($glob) && realpath(dirname($glob)) === $phpstorm_meta_path) { $stub_files[] = $glob; } } } } foreach ($stub_files as $file_path) { $file_path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $file_path); $codebase->scanner->addFileToDeepScan($file_path); } $progress->debug('Registering stub files' . "\n"); $codebase->scanFiles(); $progress->debug('Finished registering stub files' . "\n"); $codebase->register_stub_files = \false; } public function getCacheDirectory() : ?string { if ($this->cache_directory === null) { return null; } if ($this->cache_directory_initialized) { return $this->cache_directory; } $cwd = null; if ($this->resolve_from_config_file) { $cwd = getcwd(); chdir($this->base_dir); } try { if (!is_dir($this->cache_directory)) { try { if (mkdir($this->cache_directory, 0777, \true) === \false) { // any other error than directory already exists/permissions issue throw new RuntimeException('Failed to create Psalm cache directory for unknown reasons'); } } catch (RuntimeException $e) { if (!is_dir($this->cache_directory)) { // rethrow the error with default message // it contains the reason why creation failed throw $e; } } } } finally { if ($cwd) { chdir($cwd); } } $this->cache_directory_initialized = \true; return $this->cache_directory; } public function getGlobalCacheDirectory() : ?string { return $this->global_cache_directory; } /** * @return array */ public function getPredefinedConstants() : array { return $this->predefined_constants; } public function collectPredefinedConstants() : void { $this->predefined_constants = get_defined_constants(); } /** * @return array */ public function getPredefinedFunctions() : array { return $this->predefined_functions; } public function collectPredefinedFunctions() : void { $defined_functions = get_defined_functions(); foreach ($defined_functions['user'] as $function_name) { $this->predefined_functions[$function_name] = \true; } foreach ($defined_functions['internal'] as $function_name) { $this->predefined_functions[$function_name] = \true; } } public function setIncludeCollector(IncludeCollector $include_collector) : void { $this->include_collector = $include_collector; } public function visitComposerAutoloadFiles(ProjectAnalyzer $project_analyzer, ?Progress $progress = null) : void { if ($progress === null) { $progress = new VoidProgress(); } if (!$this->include_collector) { throw new LogicException("IncludeCollector should be set at this point"); } $vendor_autoload_files_path = $this->base_dir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR . 'autoload_files.php'; if (file_exists($vendor_autoload_files_path)) { $this->include_collector->runAndCollect(static fn(): array => require $vendor_autoload_files_path); } $codebase = $project_analyzer->getCodebase(); $this->collectPredefinedFunctions(); if ($this->autoloader) { // somee classes that we think are missing may not actually be missing // as they might be autoloadable once we require the autoloader below $codebase->classlikes->forgetMissingClassLikes(); $this->include_collector->runAndCollect([$this, 'requireAutoloader']); } $this->collectPredefinedConstants(); $autoload_included_files = $this->include_collector->getFilteredIncludedFiles(); if ($autoload_included_files) { $codebase->register_autoload_files = \true; $progress->debug('Registering autoloaded files' . "\n"); foreach ($autoload_included_files as $file_path) { $file_path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $file_path); $progress->debug(' ' . $file_path . "\n"); $codebase->scanner->addFileToDeepScan($file_path); } $codebase->scanner->scanFiles($codebase->classlikes); $progress->debug('Finished registering autoloaded files' . "\n"); $codebase->register_autoload_files = \false; } } /** * @return string|false */ public function getComposerFilePathForClassLike(string $fq_classlike_name) { if (!$this->composer_class_loader) { return \false; } return $this->composer_class_loader->findFile($fq_classlike_name); } public function getPotentialComposerFilePathForClassLike(string $class) : ?string { if (!$this->composer_class_loader) { return null; } $psr4_prefixes = $this->composer_class_loader->getPrefixesPsr4(); // PSR-4 lookup $logicalPathPsr4 = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'; $candidate_path = null; $maxDepth = 0; $subPath = $class; while (\false !== ($lastPos = strrpos($subPath, '\\'))) { $subPath = substr($subPath, 0, $lastPos); $search = $subPath . '\\'; if (isset($psr4_prefixes[$search])) { $depth = substr_count($search, '\\'); $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($psr4_prefixes[$search] as $dir) { $dir = realpath($dir); if ($dir && $depth > $maxDepth && $this->isInProjectDirs($dir . DIRECTORY_SEPARATOR . 'testdummy.php')) { $maxDepth = $depth; $candidate_path = realpath($dir) . $pathEnd; } } } } return $candidate_path; } public static function removeCacheDirectory(string $dir) : void { clearstatcache(\true, $dir); if (is_dir($dir)) { $objects = scandir($dir, SCANDIR_SORT_NONE); if ($objects === \false) { throw new UnexpectedValueException('Not expecting false here'); } foreach ($objects as $object) { if ($object === '.' || $object === '..') { continue; } $full_path = $dir . '/' . $object; // if it was deleted in the meantime/race condition with other psalm process clearstatcache(\true, $full_path); if (!file_exists($full_path)) { continue; } if (is_dir($full_path)) { self::removeCacheDirectory($full_path); } else { $fp = fopen($full_path, 'c'); if ($fp === \false) { continue; } $max_wait_cycles = 5; $has_lock = \false; while ($max_wait_cycles > 0) { if (flock($fp, LOCK_EX)) { $has_lock = \true; break; } $max_wait_cycles--; usleep(50000); } try { if (!$has_lock) { throw new RuntimeException('Could not acquire lock for deletion of ' . $full_path); } unlink($full_path); fclose($fp); } catch (RuntimeException $e) { if (is_resource($fp)) { fclose($fp); } clearstatcache(\true, $full_path); if (file_exists($full_path)) { // rethrow the error with default message // it contains the reason why deletion failed throw $e; } } } } // may have been removed in the meantime clearstatcache(\true, $dir); if (is_dir($dir)) { rmdir($dir); } } } public function setServerMode() : void { if ($this->cache_directory !== null) { $this->cache_directory .= '-s'; } } public function addStubFile(string $stub_file) : void { $this->stub_files[$stub_file] = $stub_file; } public function hasStubFile(string $stub_file) : bool { return isset($this->stub_files[$stub_file]); } /** * @return array */ public function getStubFiles() : array { return $this->stub_files; } public function addPreloadedStubFile(string $stub_file) : void { $this->preloaded_stub_files[$stub_file] = $stub_file; } public function getPhpVersion() : ?string { return $this->getPhpVersionFromConfig() ?? $this->getPHPVersionFromComposerJson(); } public function getPhpVersionFromConfig() : ?string { return $this->configured_php_version; } private function setBooleanAttribute(string $name, bool $value) : void { $this->{$name} = $value; } /** * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess */ public function getPHPVersionFromComposerJson() : ?string { $composer_json_path = Composer::getJsonFilePath($this->base_dir); if (file_exists($composer_json_path)) { try { $composer_json = json_decode(file_get_contents($composer_json_path), \true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { $composer_json = null; } if (!$composer_json) { throw new UnexpectedValueException('Invalid composer.json at ' . $composer_json_path); } $php_version = $composer_json['require']['php'] ?? null; if (is_string($php_version)) { $version_parser = new VersionParser(); $constraint = $version_parser->parseConstraints($php_version); foreach (['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] as $candidate) { if ($constraint->matches(new Constraint('<=', "{$candidate}.0.0-dev")) || $constraint->matches(new Constraint('<=', "{$candidate}.999"))) { return $candidate; } } } } return null; } public function addUniversalObjectCrate(string $class) : void { if (!class_exists($class)) { throw new UnexpectedValueException($class . ' is not a known class'); } $this->universal_object_crates[] = strtolower($class); } /** * @return array */ public function getUniversalObjectCrates() : array { return $this->universal_object_crates; } /** @internal */ public function requireAutoloader() : void { /** @psalm-suppress UnresolvableInclude */ require $this->autoloader; } } config = $config; $this->codebase = $codebase; } public function addStubFile(string $file_name) : void { $this->config->addStubFile($file_name); } public function registerHooksFromClass(string $handler) : void { if (!class_exists($handler, \false)) { throw new InvalidArgumentException('Plugins must be loaded before registration'); } $this->config->eventDispatcher->registerClass($handler); if (is_subclass_of($handler, PropertyExistenceProviderInterface::class)) { $this->codebase->properties->property_existence_provider->registerClass($handler); } if (is_subclass_of($handler, PropertyVisibilityProviderInterface::class)) { $this->codebase->properties->property_visibility_provider->registerClass($handler); } if (is_subclass_of($handler, PropertyTypeProviderInterface::class)) { $this->codebase->properties->property_type_provider->registerClass($handler); } if (is_subclass_of($handler, MethodExistenceProviderInterface::class)) { $this->codebase->methods->existence_provider->registerClass($handler); } if (is_subclass_of($handler, MethodVisibilityProviderInterface::class)) { $this->codebase->methods->visibility_provider->registerClass($handler); } if (is_subclass_of($handler, MethodReturnTypeProviderInterface::class)) { $this->codebase->methods->return_type_provider->registerClass($handler); } if (is_subclass_of($handler, MethodParamsProviderInterface::class)) { $this->codebase->methods->params_provider->registerClass($handler); } if (is_subclass_of($handler, FunctionExistenceProviderInterface::class)) { $this->codebase->functions->existence_provider->registerClass($handler); } if (is_subclass_of($handler, FunctionParamsProviderInterface::class)) { $this->codebase->functions->params_provider->registerClass($handler); } if (is_subclass_of($handler, FunctionReturnTypeProviderInterface::class)) { $this->codebase->functions->return_type_provider->registerClass($handler); } if (is_subclass_of($handler, DynamicFunctionStorageProviderInterface::class)) { $this->codebase->functions->dynamic_storage_provider->registerClass($handler); } } } sortIssuesByLevelAndType(); $output = <<