From c8c5f1cb959dcef6449d9e45cf88423f82336bb9 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Mon, 30 Mar 2020 13:10:15 +0200 Subject: [PATCH] Upgrade PHPCompatibility to 9.3.5+ (9fb3244) --- .../AbstractComplexVersionSniff.php | 31 +- .../AbstractFunctionCallParameterSniff.php | 34 +- PHPCompatibility/AbstractNewFeatureSniff.php | 23 +- .../AbstractRemovedFeatureSniff.php | 27 +- PHPCompatibility/ComplexVersionInterface.php | 23 +- PHPCompatibility/PHPCSHelper.php | 56 +- PHPCompatibility/Sniff.php | 387 +++++++++++-- .../ForbiddenAbstractPrivateMethodsSniff.php | 90 ++++ .../Classes/NewAnonymousClassesSniff.php | 30 +- .../Sniffs/Classes/NewClassesSniff.php | 103 +++- .../Classes/NewConstVisibilitySniff.php | 24 +- .../Classes/NewLateStaticBindingSniff.php | 29 +- .../Classes/NewTypedPropertiesSniff.php | 132 +++++ .../Classes/RemovedOrphanedParentSniff.php | 115 ++++ .../Sniffs/Constants/NewConstantsSniff.php | 265 ++++++++- .../Constants/NewMagicClassConstantSniff.php | 29 +- .../Constants/RemovedConstantsSniff.php | 232 +++++++- .../DiscouragedSwitchContinueSniff.php | 37 +- ...ForbiddenBreakContinueOutsideLoopSniff.php | 31 +- ...denBreakContinueVariableArgumentsSniff.php | 35 +- ...enSwitchWithMultipleDefaultBlocksSniff.php | 26 +- .../NewExecutionDirectivesSniff.php | 74 ++- .../NewForeachExpressionReferencingSniff.php | 29 +- .../NewListInForeachSniff.php | 25 +- .../ControlStructures/NewMultiCatchSniff.php | 25 +- .../Extensions/RemovedExtensionsSniff.php | 73 ++- ...biddenParameterShadowSuperGlobalsSniff.php | 30 +- .../ForbiddenParametersWithSameNameSniff.php | 32 +- .../ForbiddenToStringParametersSniff.php | 99 ++++ ...orbiddenVariableNamesInClosureUseSniff.php | 28 +- .../FunctionDeclarations/NewClosureSniff.php | 51 +- .../NewExceptionsFromToStringSniff.php | 171 ++++++ .../NewNullableTypesSniff.php | 41 +- .../NewParamTypeDeclarationsSniff.php | 60 ++- .../NewReturnTypeDeclarationsSniff.php | 49 +- .../NonStaticMagicMethodsSniff.php | 64 ++- .../NewMagicMethodsSniff.php | 57 +- .../RemovedMagicAutoloadSniff.php | 34 +- .../RemovedNamespacedAssertSniff.php | 35 +- .../RemovedPHP4StyleConstructorsSniff.php | 51 +- .../ReservedFunctionNamesSniff.php | 83 ++- ...gumentFunctionsReportCurrentValueSniff.php | 32 +- .../ArgumentFunctionsUsageSniff.php | 31 +- .../NewFunctionParametersSniff.php | 53 +- .../Sniffs/FunctionUse/NewFunctionsSniff.php | 109 +++- ...ionalToRequiredFunctionParametersSniff.php | 32 +- .../RemovedFunctionParametersSniff.php | 93 +++- .../FunctionUse/RemovedFunctionsSniff.php | 508 +++++++++++------- ...uiredToOptionalFunctionParametersSniff.php | 56 +- .../Generators/NewGeneratorReturnSniff.php | 33 +- .../IniDirectives/NewIniDirectivesSniff.php | 212 +++++++- .../RemovedIniDirectivesSniff.php | 102 +++- .../NewConstantArraysUsingConstSniff.php | 28 +- .../NewConstantArraysUsingDefineSniff.php | 32 +- .../NewConstantScalarExpressionsSniff.php | 52 +- .../Sniffs/InitialValue/NewHeredocSniff.php | 30 +- .../Interfaces/InternalInterfacesSniff.php | 31 +- .../Sniffs/Interfaces/NewInterfacesSniff.php | 58 +- .../Keywords/CaseSensitiveKeywordsSniff.php | 25 +- .../ForbiddenNamesAsDeclaredSniff.php | 41 +- .../ForbiddenNamesAsInvokedFunctionsSniff.php | 27 +- .../Sniffs/Keywords/ForbiddenNamesSniff.php | 79 ++- .../Sniffs/Keywords/NewKeywordsSniff.php | 51 +- .../NewEmptyNonVariableSniff.php | 31 +- .../NewLanguageConstructsSniff.php | 41 +- .../Sniffs/Lists/AssignmentOrderSniff.php | 29 +- .../ForbiddenEmptyListAssignmentSniff.php | 29 +- .../Sniffs/Lists/NewKeyedListSniff.php | 42 +- .../Lists/NewListReferenceAssignmentSniff.php | 27 +- .../Sniffs/Lists/NewShortListSniff.php | 33 +- .../ForbiddenToStringParametersSniff.php | 103 ++++ .../MethodUse/NewDirectCallsToCloneSniff.php | 26 +- .../Miscellaneous/NewPHPOpenTagEOFSniff.php | 147 +++++ .../RemovedAlternativePHPTagsSniff.php | 38 +- .../Miscellaneous/ValidIntegersSniff.php | 54 +- .../ChangedConcatOperatorPrecedenceSniff.php | 199 +++++++ .../ForbiddenNegativeBitshiftSniff.php | 31 +- .../Sniffs/Operators/NewOperatorsSniff.php | 65 ++- .../Sniffs/Operators/NewShortTernarySniff.php | 47 +- .../RemovedTernaryAssociativitySniff.php | 157 ++++++ .../ForbiddenGetClassNullSniff.php | 30 +- ...orbiddenStripTagsSelfClosingXHTMLSniff.php | 113 ++++ .../NewArrayReduceInitialTypeSniff.php | 28 +- .../ParameterValues/NewFopenModesSniff.php | 25 +- .../NewHTMLEntitiesEncodingDefaultSniff.php | 92 ++++ .../NewHashAlgorithmsSniff.php | 36 +- .../NewIDNVariantDefaultSniff.php | 91 ++++ .../NewIconvMbstringCharsetDefaultSniff.php | 231 ++++++++ .../NewNegativeStringOffsetSniff.php | 27 +- .../ParameterValues/NewPCREModifiersSniff.php | 32 +- .../ParameterValues/NewPackFormatSniff.php | 27 +- .../NewPasswordAlgoConstantValuesSniff.php | 125 +++++ .../NewProcOpenCmdArraySniff.php | 136 +++++ .../NewStripTagsAllowableTagsArraySniff.php | 152 ++++++ .../RemovedHashAlgorithmsSniff.php | 34 +- .../RemovedIconvEncodingSniff.php | 32 +- .../RemovedImplodeFlexibleParamOrderSniff.php | 323 +++++++++++ ...emovedMbStrrposEncodingThirdParamSniff.php | 149 +++++ .../RemovedMbstringModifiersSniff.php | 35 +- .../RemovedNonCryptoHashSniff.php | 31 +- .../RemovedPCREModifiersSniff.php | 48 +- .../RemovedSetlocaleStringSniff.php | 31 +- .../ForbiddenCallTimePassByReferenceSniff.php | 42 +- .../NewArrayStringDereferencingSniff.php | 145 ++++- .../Sniffs/Syntax/NewArrayUnpackingSniff.php | 142 +++++ .../Syntax/NewClassMemberAccessSniff.php | 163 ++++-- .../Syntax/NewDynamicAccessToStaticSniff.php | 24 +- .../Syntax/NewFlexibleHeredocNowdocSniff.php | 32 +- .../NewFunctionArrayDereferencingSniff.php | 140 ++++- .../NewFunctionCallTrailingCommaSniff.php | 25 +- .../Sniffs/Syntax/NewShortArraySniff.php | 29 +- .../RemovedCurlyBraceArrayAccessSniff.php | 362 +++++++++++++ .../Syntax/RemovedNewReferenceSniff.php | 29 +- .../NewUnicodeEscapeSequenceSniff.php | 162 ++++++ .../Sniffs/TypeCasts/NewTypeCastsSniff.php | 39 +- .../TypeCasts/RemovedTypeCastsSniff.php | 49 +- .../Sniffs/Upgrade/LowPHPCSSniff.php | 64 ++- .../Sniffs/Upgrade/LowPHPSniff.php | 182 +++++++ .../NewGroupUseDeclarationsSniff.php | 37 +- .../NewUseConstFunctionSniff.php | 33 +- .../ForbiddenGlobalVariableVariableSniff.php | 27 +- .../ForbiddenThisUseContextsSniff.php | 25 +- .../NewUniformVariableSyntaxSniff.php | 27 +- .../RemovedPredefinedGlobalVariablesSniff.php | 47 +- PHPCompatibility/Tests/BaseSniffTest.php | 69 ++- ...orbiddenAbstractPrivateMethodsUnitTest.inc | 41 ++ ...orbiddenAbstractPrivateMethodsUnitTest.php | 126 +++++ .../Classes/NewAnonymousClassesUnitTest.php | 13 +- .../Tests/Classes/NewClassesUnitTest.inc | 6 + .../Tests/Classes/NewClassesUnitTest.php | 20 +- .../Classes/NewConstVisibilityUnitTest.php | 13 +- .../Classes/NewLateStaticBindingUnitTest.php | 13 +- .../Classes/NewTypedPropertiesUnitTest.inc | 66 +++ .../Classes/NewTypedPropertiesUnitTest.php | 140 +++++ .../Classes/RemovedOrphanedParentUnitTest.inc | 68 +++ .../Classes/RemovedOrphanedParentUnitTest.php | 113 ++++ .../Tests/Constants/NewConstantsUnitTest.inc | 58 ++ .../Tests/Constants/NewConstantsUnitTest.php | 73 ++- .../NewMagicClassConstantUnitTest.php | 13 +- .../Constants/RemovedConstantsUnitTest.inc | 68 ++- .../Constants/RemovedConstantsUnitTest.php | 129 ++++- .../DiscouragedSwitchContinueUnitTest.inc | 2 +- .../DiscouragedSwitchContinueUnitTest.php | 13 +- ...biddenBreakContinueOutsideLoopUnitTest.php | 15 +- ...BreakContinueVariableArgumentsUnitTest.php | 28 +- ...witchWithMultipleDefaultBlocksUnitTest.php | 13 +- .../NewExecutionDirectivesUnitTest.php | 13 +- ...ewForeachExpressionReferencingUnitTest.php | 13 +- .../NewListInForeachUnitTest.php | 13 +- .../NewMultiCatchUnitTest.php | 13 +- .../Extensions/RemovedExtensionsUnitTest.inc | 6 +- .../Extensions/RemovedExtensionsUnitTest.php | 22 +- ...denParameterShadowSuperGlobalsUnitTest.php | 13 +- ...orbiddenParametersWithSameNameUnitTest.inc | 4 +- ...orbiddenParametersWithSameNameUnitTest.php | 14 +- .../ForbiddenToStringParametersUnitTest.inc | 40 ++ .../ForbiddenToStringParametersUnitTest.php | 137 +++++ ...iddenVariableNamesInClosureUseUnitTest.php | 24 +- .../NewClosureUnitTest.php | 13 +- .../NewExceptionsFromToStringUnitTest.inc | 159 ++++++ .../NewExceptionsFromToStringUnitTest.php | 159 ++++++ .../NewNullableTypesUnitTest.php | 13 +- .../NewParamTypeDeclarationsUnitTest.inc | 2 +- .../NewParamTypeDeclarationsUnitTest.php | 14 +- .../NewReturnTypeDeclarationsUnitTest.php | 13 +- .../NonStaticMagicMethodsUnitTest.1.inc | 46 ++ .../NonStaticMagicMethodsUnitTest.2.inc | 8 + .../NonStaticMagicMethodsUnitTest.php | 113 ++-- .../NewMagicMethodsUnitTest.1.inc | 15 +- .../NewMagicMethodsUnitTest.2.inc | 4 + .../NewMagicMethodsUnitTest.php | 39 +- .../RemovedMagicAutoloadUnitTest.php | 34 +- .../RemovedNamespacedAssertUnitTest.php | 31 +- .../RemovedPHP4StyleConstructorsUnitTest.inc | 19 +- .../RemovedPHP4StyleConstructorsUnitTest.php | 15 +- .../ReservedFunctionNamesUnitTest.inc | 28 +- .../ReservedFunctionNamesUnitTest.php | 16 +- ...entFunctionsReportCurrentValueUnitTest.php | 8 +- .../ArgumentFunctionsUsageUnitTest.php | 13 +- .../NewFunctionParametersUnitTest.inc | 3 +- .../NewFunctionParametersUnitTest.php | 17 +- .../FunctionUse/NewFunctionsUnitTest.inc | 19 + .../FunctionUse/NewFunctionsUnitTest.php | 32 +- ...alToRequiredFunctionParametersUnitTest.inc | 3 + ...alToRequiredFunctionParametersUnitTest.php | 13 +- .../RemovedFunctionParametersUnitTest.inc | 6 + .../RemovedFunctionParametersUnitTest.php | 33 +- .../FunctionUse/RemovedFunctionsUnitTest.inc | 79 +++ .../FunctionUse/RemovedFunctionsUnitTest.php | 131 ++++- ...edToOptionalFunctionParametersUnitTest.inc | 4 + ...edToOptionalFunctionParametersUnitTest.php | 16 +- .../Generators/NewGeneratorReturnUnitTest.php | 13 +- .../NewIniDirectivesUnitTest.inc | 126 +++++ .../NewIniDirectivesUnitTest.php | 57 +- .../RemovedIniDirectivesUnitTest.inc | 61 ++- .../RemovedIniDirectivesUnitTest.php | 35 +- .../NewConstantArraysUsingConstUnitTest.php | 13 +- .../NewConstantArraysUsingDefineUnitTest.php | 13 +- .../NewConstantScalarExpressionsUnitTest.php | 13 +- .../Tests/InitialValue/NewHeredocUnitTest.php | 13 +- .../Interfaces/InternalInterfacesUnitTest.php | 13 +- .../Interfaces/NewInterfacesUnitTest.php | 15 +- .../CaseSensitiveKeywordsUnitTest.php | 13 +- .../Keywords/ForbiddenNames/class-const.php | 17 +- .../Keywords/ForbiddenNames/class-extends.php | 17 +- .../class-use-trait-alias-final-method.php | 17 +- .../class-use-trait-alias-method.php | 17 +- .../class-use-trait-alias-private-method.php | 17 +- ...class-use-trait-alias-protected-method.php | 17 +- .../class-use-trait-alias-public-method.php | 17 +- .../ForbiddenNames/class-use-trait-const.php | 17 +- .../class-use-trait-function.php | 17 +- .../ForbiddenNames/class-use-trait.php | 17 +- .../Tests/Keywords/ForbiddenNames/class.php | 17 +- .../Tests/Keywords/ForbiddenNames/const.php | 17 +- .../Tests/Keywords/ForbiddenNames/define.php | 17 +- .../function-declare-reference.php | 17 +- .../ForbiddenNames/function-declare.php | 17 +- .../ForbiddenNames/interface-extends.php | 17 +- .../Keywords/ForbiddenNames/interface.php | 17 +- .../ForbiddenNames/method-declare.php | 17 +- .../Keywords/ForbiddenNames/namespace.php | 17 +- .../ForbiddenNames/nested-namespace.php | 17 +- .../Tests/Keywords/ForbiddenNames/trait.php | 17 +- .../Tests/Keywords/ForbiddenNames/use-as.php | 17 +- .../Tests/Keywords/ForbiddenNames/use.php | 17 +- .../ForbiddenNamesAsDeclaredUnitTest.php | 13 +- ...rbiddenNamesAsInvokedFunctionsUnitTest.php | 13 +- .../Tests/Keywords/ForbiddenNamesUnitTest.inc | 61 +++ .../Tests/Keywords/ForbiddenNamesUnitTest.php | 15 +- .../Tests/Keywords/NewKeywordsUnitTest.php | 13 +- .../NewEmptyNonVariableUnitTest.php | 13 +- .../NewLanguageConstructsUnitTest.php | 15 +- .../Tests/Lists/AssignmentOrderUnitTest.php | 13 +- .../ForbiddenEmptyListAssignmentUnitTest.php | 13 +- .../Tests/Lists/NewKeyedListUnitTest.php | 13 +- .../NewListReferenceAssignmentUnitTest.php | 13 +- .../Tests/Lists/NewShortListUnitTest.php | 13 +- .../ForbiddenToStringParametersUnitTest.inc | 50 ++ .../ForbiddenToStringParametersUnitTest.php | 109 ++++ .../NewDirectCallsToCloneUnitTest.inc | 6 +- .../NewDirectCallsToCloneUnitTest.php | 27 +- .../NewPHPOpenTagEOFUnitTest.1.inc | 1 + .../NewPHPOpenTagEOFUnitTest.2.inc | 6 + .../NewPHPOpenTagEOFUnitTest.3.inc | 6 + .../NewPHPOpenTagEOFUnitTest.4.inc | 1 + .../NewPHPOpenTagEOFUnitTest.5.inc | 13 + .../NewPHPOpenTagEOFUnitTest.6.inc | 4 + .../NewPHPOpenTagEOFUnitTest.7.inc | 6 + .../NewPHPOpenTagEOFUnitTest.php | 236 ++++++++ .../RemovedAlternativePHPTagsUnitTest.php | 13 +- .../Miscellaneous/ValidIntegersUnitTest.inc | 3 + .../Miscellaneous/ValidIntegersUnitTest.php | 59 +- ...hangedConcatOperatorPrecedenceUnitTest.inc | 95 ++++ ...hangedConcatOperatorPrecedenceUnitTest.php | 93 ++++ .../ForbiddenNegativeBitshiftUnitTest.php | 13 +- .../Tests/Operators/NewOperatorsUnitTest.php | 14 +- .../Operators/NewShortTernaryUnitTest.php | 13 +- .../RemovedTernaryAssociativityUnitTest.inc | 126 +++++ .../RemovedTernaryAssociativityUnitTest.php | 134 +++++ .../ForbiddenGetClassNullUnitTest.php | 13 +- ...iddenStripTagsSelfClosingXHTMLUnitTest.inc | 15 + ...iddenStripTagsSelfClosingXHTMLUnitTest.php | 88 +++ .../NewArrayReduceInitialTypeUnitTest.php | 13 +- .../ParameterValues/NewFopenModesUnitTest.php | 13 +- ...NewHTMLEntitiesEncodingDefaultUnitTest.inc | 11 + ...NewHTMLEntitiesEncodingDefaultUnitTest.php | 108 ++++ .../NewHashAlgorithmsUnitTest.inc | 1 + .../NewHashAlgorithmsUnitTest.php | 14 +- .../NewIDNVariantDefaultUnitTest.inc | 13 + .../NewIDNVariantDefaultUnitTest.php | 109 ++++ ...NewIconvMbstringCharsetDefaultUnitTest.inc | 110 ++++ ...NewIconvMbstringCharsetDefaultUnitTest.php | 171 ++++++ .../NewNegativeStringOffsetUnitTest.inc | 4 +- .../NewNegativeStringOffsetUnitTest.php | 17 +- .../NewPCREModifiersUnitTest.php | 13 +- .../ParameterValues/NewPackFormatUnitTest.php | 13 +- .../NewPasswordAlgoConstantValuesUnitTest.inc | 26 + .../NewPasswordAlgoConstantValuesUnitTest.php | 92 ++++ .../NewProcOpenCmdArrayUnitTest.inc | 39 ++ .../NewProcOpenCmdArrayUnitTest.php | 137 +++++ ...NewStripTagsAllowableTagsArrayUnitTest.inc | 34 ++ ...NewStripTagsAllowableTagsArrayUnitTest.php | 169 ++++++ .../RemovedHashAlgorithmsUnitTest.php | 13 +- .../RemovedIconvEncodingUnitTest.php | 13 +- ...movedImplodeFlexibleParamOrderUnitTest.inc | 68 +++ ...movedImplodeFlexibleParamOrderUnitTest.php | 123 +++++ ...vedMbStrrposEncodingThirdParamUnitTest.inc | 24 + ...vedMbStrrposEncodingThirdParamUnitTest.php | 91 ++++ .../RemovedMbstringModifiersUnitTest.inc | 4 +- .../RemovedMbstringModifiersUnitTest.php | 13 +- .../RemovedNonCryptoHashUnitTest.php | 13 +- .../RemovedPCREModifiersUnitTest.php | 13 +- .../RemovedSetlocaleStringUnitTest.php | 13 +- ...rbiddenCallTimePassByReferenceUnitTest.php | 13 +- .../NewArrayStringDereferencingUnitTest.inc | 16 +- .../NewArrayStringDereferencingUnitTest.php | 69 ++- .../Syntax/NewArrayUnpackingUnitTest.inc | 43 ++ .../Syntax/NewArrayUnpackingUnitTest.php | 97 ++++ .../Syntax/NewClassMemberAccessUnitTest.inc | 15 +- .../Syntax/NewClassMemberAccessUnitTest.php | 64 ++- .../NewDynamicAccessToStaticUnitTest.php | 13 +- .../NewFlexibleHeredocNowdocUnitTest.3.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.4.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.5.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.6.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.7.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.8.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.9.inc | 2 +- .../NewFlexibleHeredocNowdocUnitTest.php | 23 +- .../NewFunctionArrayDereferencingUnitTest.inc | 18 + .../NewFunctionArrayDereferencingUnitTest.php | 66 ++- .../NewFunctionCallTrailingCommaUnitTest.php | 13 +- .../Tests/Syntax/NewShortArrayUnitTest.php | 17 +- .../RemovedCurlyBraceArrayAccessUnitTest.inc | 136 +++++ ...vedCurlyBraceArrayAccessUnitTest.inc.fixed | 136 +++++ .../RemovedCurlyBraceArrayAccessUnitTest.php | 131 +++++ .../Syntax/RemovedNewReferenceUnitTest.php | 13 +- .../NewUnicodeEscapeSequenceUnitTest.inc | 61 +++ .../NewUnicodeEscapeSequenceUnitTest.php | 152 ++++++ .../Tests/TypeCasts/NewTypeCastsUnitTest.php | 13 +- .../TypeCasts/RemovedTypeCastsUnitTest.inc | 9 +- .../TypeCasts/RemovedTypeCastsUnitTest.php | 17 +- .../Tests/Upgrade/LowPHPCSUnitTest.inc | 1 + .../Tests/Upgrade/LowPHPCSUnitTest.php | 83 +++ .../Tests/Upgrade/LowPHPUnitTest.inc | 1 + .../Tests/Upgrade/LowPHPUnitTest.php | 82 +++ .../NewGroupUseDeclarationsUnitTest.php | 13 +- .../NewUseConstFunctionUnitTest.php | 13 +- ...orbiddenGlobalVariableVariableUnitTest.php | 13 +- .../ForbiddenThisUseContextsUnitTest.php | 4 +- .../NewUniformVariableSyntaxUnitTest.php | 13 +- ...movedPredefinedGlobalVariablesUnitTest.php | 15 +- ...DoesFunctionCallHaveParametersUnitTest.php | 13 +- .../Util/Tests/Core/FunctionsUnitTest.php | 15 +- ...QClassNameFromDoubleColonTokenUnitTest.php | 13 +- .../GetFQClassNameFromNewTokenUnitTest.php | 13 +- .../Core/GetFQExtendedClassNameUnitTest.php | 13 +- .../GetFunctionParameterCountUnitTest.php | 13 +- .../Core/GetFunctionParametersUnitTest.php | 13 +- .../Tests/Core/IsClassConstantUnitTest.php | 13 +- .../Tests/Core/IsClassPropertyUnitTest.php | 13 +- .../Util/Tests/Core/IsNumberUnitTest.php | 15 +- .../Core/IsNumericCalculationUnitTest.php | 13 +- .../Util/Tests/Core/IsShortListUnitTest.php | 13 +- .../Core/IsUseOfGlobalConstantUnitTest.php | 13 +- .../Util/Tests/Core/TokenScopeUnitTest.php | 13 +- .../Util/Tests/CoreMethodTestFrame.php | 28 +- .../Util/Tests/TestHelperPHPCompatibility.php | 19 +- readme_moodle.txt | 7 +- thirdpartylibs.xml | 2 +- 351 files changed, 14801 insertions(+), 2318 deletions(-) create mode 100644 PHPCompatibility/Sniffs/Classes/ForbiddenAbstractPrivateMethodsSniff.php create mode 100644 PHPCompatibility/Sniffs/Classes/NewTypedPropertiesSniff.php create mode 100644 PHPCompatibility/Sniffs/Classes/RemovedOrphanedParentSniff.php create mode 100644 PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenToStringParametersSniff.php create mode 100644 PHPCompatibility/Sniffs/FunctionDeclarations/NewExceptionsFromToStringSniff.php create mode 100644 PHPCompatibility/Sniffs/MethodUse/ForbiddenToStringParametersSniff.php create mode 100644 PHPCompatibility/Sniffs/Miscellaneous/NewPHPOpenTagEOFSniff.php create mode 100644 PHPCompatibility/Sniffs/Operators/ChangedConcatOperatorPrecedenceSniff.php create mode 100644 PHPCompatibility/Sniffs/Operators/RemovedTernaryAssociativitySniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLSniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/NewHTMLEntitiesEncodingDefaultSniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/NewIDNVariantDefaultSniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/NewIconvMbstringCharsetDefaultSniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/NewPasswordAlgoConstantValuesSniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/NewProcOpenCmdArraySniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/NewStripTagsAllowableTagsArraySniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/RemovedImplodeFlexibleParamOrderSniff.php create mode 100644 PHPCompatibility/Sniffs/ParameterValues/RemovedMbStrrposEncodingThirdParamSniff.php create mode 100644 PHPCompatibility/Sniffs/Syntax/NewArrayUnpackingSniff.php create mode 100644 PHPCompatibility/Sniffs/Syntax/RemovedCurlyBraceArrayAccessSniff.php create mode 100644 PHPCompatibility/Sniffs/TextStrings/NewUnicodeEscapeSequenceSniff.php create mode 100644 PHPCompatibility/Sniffs/Upgrade/LowPHPSniff.php create mode 100644 PHPCompatibility/Tests/Classes/ForbiddenAbstractPrivateMethodsUnitTest.inc create mode 100644 PHPCompatibility/Tests/Classes/ForbiddenAbstractPrivateMethodsUnitTest.php create mode 100644 PHPCompatibility/Tests/Classes/NewTypedPropertiesUnitTest.inc create mode 100644 PHPCompatibility/Tests/Classes/NewTypedPropertiesUnitTest.php create mode 100644 PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.inc create mode 100644 PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.php create mode 100644 PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.inc create mode 100644 PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.php create mode 100644 PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.inc create mode 100644 PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.php create mode 100644 PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.inc create mode 100644 PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.php create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.1.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.2.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.3.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.4.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.5.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.6.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.7.inc create mode 100644 PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.php create mode 100644 PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.inc create mode 100644 PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.php create mode 100644 PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.inc create mode 100644 PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/NewHTMLEntitiesEncodingDefaultUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/NewHTMLEntitiesEncodingDefaultUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/NewIDNVariantDefaultUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/NewIDNVariantDefaultUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/NewProcOpenCmdArrayUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/NewProcOpenCmdArrayUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.php create mode 100644 PHPCompatibility/Tests/ParameterValues/RemovedMbStrrposEncodingThirdParamUnitTest.inc create mode 100644 PHPCompatibility/Tests/ParameterValues/RemovedMbStrrposEncodingThirdParamUnitTest.php create mode 100644 PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.inc create mode 100644 PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.php create mode 100644 PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc create mode 100644 PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc.fixed create mode 100644 PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.php create mode 100644 PHPCompatibility/Tests/TextStrings/NewUnicodeEscapeSequenceUnitTest.inc create mode 100644 PHPCompatibility/Tests/TextStrings/NewUnicodeEscapeSequenceUnitTest.php create mode 100644 PHPCompatibility/Tests/Upgrade/LowPHPCSUnitTest.inc create mode 100644 PHPCompatibility/Tests/Upgrade/LowPHPCSUnitTest.php create mode 100644 PHPCompatibility/Tests/Upgrade/LowPHPUnitTest.inc create mode 100644 PHPCompatibility/Tests/Upgrade/LowPHPUnitTest.php diff --git a/PHPCompatibility/AbstractComplexVersionSniff.php b/PHPCompatibility/AbstractComplexVersionSniff.php index d5fd60ac..95245b6a 100644 --- a/PHPCompatibility/AbstractComplexVersionSniff.php +++ b/PHPCompatibility/AbstractComplexVersionSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -12,11 +13,9 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\AbstractComplexVersionSniff. + * Abstract base class for sniffs based on complex arrays with PHP version information. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @since 7.1.0 */ abstract class AbstractComplexVersionSniff extends Sniff implements ComplexVersionInterface { @@ -26,6 +25,8 @@ abstract class AbstractComplexVersionSniff extends Sniff implements ComplexVersi * Handle the retrieval of relevant information and - if necessary - throwing of an * error/warning for an item. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. @@ -47,6 +48,8 @@ public function handleFeature(File $phpcsFile, $stackPtr, array $itemInfo) /** * Determine whether an error/warning should be thrown for an item based on collected information. * + * @since 7.1.0 + * * @param array $errorInfo Detail information about an item. * * @return bool @@ -57,6 +60,8 @@ abstract protected function shouldThrowError(array $errorInfo); /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -69,6 +74,8 @@ protected function getNonVersionArrayKeys() * Retrieve a subset of an item array containing only the array keys which * contain PHP version information. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about an item. * * @return array Array with only the version information. @@ -82,6 +89,8 @@ protected function getVersionArray(array $itemArray) /** * Get the item name to be used for the creation of the error code and in the error message. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * @param array $errorInfo Detail information about an item. * @@ -96,6 +105,8 @@ protected function getItemName(array $itemInfo, array $errorInfo) /** * Get the error message template for a specific sniff. * + * @since 7.1.0 + * * @return string */ abstract protected function getErrorMsgTemplate(); @@ -104,6 +115,8 @@ abstract protected function getErrorMsgTemplate(); /** * Allow for concrete child classes to filter the error message before it's passed to PHPCS. * + * @since 7.1.0 + * * @param string $error The error message which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. @@ -119,6 +132,8 @@ protected function filterErrorMsg($error, array $itemInfo, array $errorInfo) /** * Allow for concrete child classes to filter the error data before it's passed to PHPCS. * + * @since 7.1.0 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/AbstractFunctionCallParameterSniff.php b/PHPCompatibility/AbstractFunctionCallParameterSniff.php index e3244f8e..6db8cd08 100644 --- a/PHPCompatibility/AbstractFunctionCallParameterSniff.php +++ b/PHPCompatibility/AbstractFunctionCallParameterSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -14,13 +15,9 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\AbstractFunctionCallParameterSniff. - * * Abstract class to use as a base for examining the parameter values passed to function calls. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @since 8.2.0 */ abstract class AbstractFunctionCallParameterSniff extends Sniff { @@ -31,6 +28,8 @@ abstract class AbstractFunctionCallParameterSniff extends Sniff * the method called is of the right class/object. * Checking that is outside of the scope of this abstract sniff. * + * @since 8.2.0 + * * @var bool False (default) if the sniff is looking for function calls. * True if the sniff is looking for method calls. */ @@ -39,6 +38,8 @@ abstract class AbstractFunctionCallParameterSniff extends Sniff /** * Functions the sniff is looking for. Should be defined in the child class. * + * @since 8.2.0 + * * @var array The only requirement for this array is that the top level * array keys are the names of the functions you're looking for. * Other than that, the array can have arbitrary content @@ -50,6 +51,8 @@ abstract class AbstractFunctionCallParameterSniff extends Sniff * List of tokens which when they preceed the $stackPtr indicate that this * is not a function call. * + * @since 8.2.0 + * * @var array */ private $ignoreTokens = array( @@ -65,6 +68,8 @@ abstract class AbstractFunctionCallParameterSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -79,11 +84,14 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. * - * @return void + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. */ public function process(File $phpcsFile, $stackPtr) { @@ -135,6 +143,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Do a version check to determine if this sniff needs to run at all. * + * @since 8.2.0 + * * If the check done in a child class is not specific to one PHP version, * this function should return `false`. * @@ -148,6 +158,8 @@ abstract protected function bowOutEarly(); * * This method has to be made concrete in child classes. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. @@ -165,6 +177,8 @@ abstract public function processParameters(File $phpcsFile, $stackPtr, $function * Defaults to doing nothing. Can be overloaded in child classes to handle functions * were parameters are expected, but none found. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/AbstractNewFeatureSniff.php b/PHPCompatibility/AbstractNewFeatureSniff.php index ac6c2586..25406fda 100644 --- a/PHPCompatibility/AbstractNewFeatureSniff.php +++ b/PHPCompatibility/AbstractNewFeatureSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -12,11 +13,9 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\AbstractNewFeatureSniff. + * Base class for new feature sniffs. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @since 7.1.0 */ abstract class AbstractNewFeatureSniff extends AbstractComplexVersionSniff { @@ -25,6 +24,8 @@ abstract class AbstractNewFeatureSniff extends AbstractComplexVersionSniff /** * Determine whether an error/warning should be thrown for an item based on collected information. * + * @since 7.1.0 + * * @param array $errorInfo Detail information about an item. * * @return bool @@ -38,6 +39,8 @@ protected function shouldThrowError(array $errorInfo) /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -69,6 +72,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -80,6 +85,8 @@ protected function getErrorMsgTemplate() /** * Generates the error or warning for this item. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. diff --git a/PHPCompatibility/AbstractRemovedFeatureSniff.php b/PHPCompatibility/AbstractRemovedFeatureSniff.php index 3254ebbf..94103922 100644 --- a/PHPCompatibility/AbstractRemovedFeatureSniff.php +++ b/PHPCompatibility/AbstractRemovedFeatureSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -12,11 +13,9 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\AbstractRemovedFeatureSniff. + * Base class for removed feature sniffs. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @since 7.1.0 */ abstract class AbstractRemovedFeatureSniff extends AbstractComplexVersionSniff { @@ -25,6 +24,8 @@ abstract class AbstractRemovedFeatureSniff extends AbstractComplexVersionSniff /** * Determine whether an error/warning should be thrown for an item based on collected information. * + * @since 7.1.0 + * * @param array $errorInfo Detail information about an item. * * @return bool @@ -40,6 +41,8 @@ protected function shouldThrowError(array $errorInfo) * * By default, removed feature version arrays, contain an additional 'alternative' array key. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -51,6 +54,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -91,6 +96,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for suggesting an alternative for a specific sniff. * + * @since 7.1.0 + * * @return string */ protected function getAlternativeOptionTemplate() @@ -102,6 +109,8 @@ protected function getAlternativeOptionTemplate() /** * Generates the error or warning for this item. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. @@ -132,7 +141,7 @@ public function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $err } // Remove the last 'and' from the message. - $error = substr($error, 0, (strlen($error) - 5)); + $error = substr($error, 0, (\strlen($error) - 5)); if ($errorInfo['alternative'] !== '') { $error .= $this->getAlternativeOptionTemplate(); diff --git a/PHPCompatibility/ComplexVersionInterface.php b/PHPCompatibility/ComplexVersionInterface.php index 43c57547..f5f63bbf 100644 --- a/PHPCompatibility/ComplexVersionInterface.php +++ b/PHPCompatibility/ComplexVersionInterface.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -12,15 +13,13 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\ComplexVersionInterface. + * Complex Version Interface. * * Interface to be implemented by sniffs using a multi-dimensional array of * PHP features (functions, classes etc) being sniffed for with version * information in sub-arrays. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @since 7.1.0 */ interface ComplexVersionInterface { @@ -30,6 +29,8 @@ interface ComplexVersionInterface * Handle the retrieval of relevant information and - if necessary - throwing of an * error/warning for an item. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. @@ -43,6 +44,8 @@ public function handleFeature(File $phpcsFile, $stackPtr, array $itemInfo); /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -53,6 +56,8 @@ public function getItemArray(array $itemInfo); /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -64,6 +69,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo); /** * Generates the error or warning for this item. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. diff --git a/PHPCompatibility/PHPCSHelper.php b/PHPCompatibility/PHPCSHelper.php index 4016adad..4204beaf 100644 --- a/PHPCompatibility/PHPCSHelper.php +++ b/PHPCompatibility/PHPCSHelper.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -14,8 +15,6 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\PHPCSHelper - * * PHPCS cross-version compatibility helper class. * * A number of PHPCS classes were split up into several classes in PHPCS 3.x @@ -23,9 +22,15 @@ * This class provides helper methods for functions which were contained in * one of these classes and which are used within the PHPCompatibility library. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * Additionally, this class contains some duplicates of PHPCS native methods. + * These methods have received bug fixes or improved functionality between the + * lowest supported PHPCS version and the latest PHPCS stable version and + * to provide the same results cross-version, PHPCompatibility needs to use + * the up-to-date versions of these methods. + * + * @since 8.0.0 + * @since 8.2.0 The duplicate PHPCS methods have been moved from the `Sniff` + * base class to this class. */ class PHPCSHelper { @@ -33,11 +38,13 @@ class PHPCSHelper /** * Get the PHPCS version number. * + * @since 8.0.0 + * * @return string */ public static function getVersion() { - if (defined('\PHP_CodeSniffer\Config::VERSION')) { + if (\defined('\PHP_CodeSniffer\Config::VERSION')) { // PHPCS 3.x. return \PHP_CodeSniffer\Config::VERSION; } else { @@ -52,6 +59,8 @@ public static function getVersion() * * PHPCS cross-version compatibility helper. * + * @since 8.0.0 + * * @param string $key The name of the config value. * @param string|null $value The value to set. If null, the config entry * is deleted, reverting it to the default value. @@ -75,6 +84,8 @@ public static function setConfigData($key, $value, $temp = false) /** * Get the value of a single PHPCS config key. * + * @since 8.0.0 + * * @param string $key The name of the config value. * * @return string|null @@ -97,6 +108,8 @@ public static function getConfigData($key) * This config key can be set in the `CodeSniffer.conf` file, on the * command-line or in a ruleset. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param string $key The name of the config value. * @@ -132,7 +145,9 @@ public static function getCommandLineData(File $phpcsFile, $key) * that, this method can be removed and calls to it replaced with * `$phpcsFile->findStartOfStatement($start, $ignore)` calls. * - * Last synced with PHPCS version: PHPCS 3.3.2 at commit 6ad28354c04b364c3c71a34e4a18b629cc3b231e}} + * Last synced with PHPCS version: PHPCS 3.3.2 at commit 6ad28354c04b364c3c71a34e4a18b629cc3b231e} + * + * @since 9.1.0 * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $start The position to start searching from in the token stack. @@ -211,7 +226,9 @@ public static function findStartOfStatement(File $phpcsFile, $start, $ignore = n * that, this method can be removed and calls to it replaced with * `$phpcsFile->findEndOfStatement($start, $ignore)` calls. * - * Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit f5d899dcb5c534a1c3cca34668624517856ba823}} + * Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit f5d899dcb5c534a1c3cca34668624517856ba823} + * + * @since 8.2.0 * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $start The position to start searching from in the token stack. @@ -310,7 +327,10 @@ public static function findEndOfStatement(File $phpcsFile, $start, $ignore = nul * that, this method can be removed and calls to it replaced with * `$phpcsFile->findExtendedClassName($stackPtr)` calls. * - * Last synced with PHPCS version: PHPCS 3.1.0-alpha at commit a9efcc9b0703f3f9f4a900623d4e97128a6aafc6}} + * Last synced with PHPCS version: PHPCS 3.1.0-alpha at commit a9efcc9b0703f3f9f4a900623d4e97128a6aafc6} + * + * @since 7.1.4 + * @since 8.2.0 Moved from the `Sniff` class to this class. * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The position of the class token in the stack. @@ -375,7 +395,10 @@ public static function findExtendedClassName(File $phpcsFile, $stackPtr) * in PHPCS 2.8.0, so only defer to upstream for higher versions. * Once the minimum supported PHPCS version for this sniff library goes beyond * that, this method can be removed and calls to it replaced with - * `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.}} + * `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.} + * + * @since 7.0.3 + * @since 8.2.0 Moved from the `Sniff` class to this class. * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the class token. @@ -457,7 +480,10 @@ public static function findImplementedInterfaceNames(File $phpcsFile, $stackPtr) * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File` * class. * - * Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit 53a28408d345044c0360c2c1b4a2aaebf4a3b8c9}} + * Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit 53a28408d345044c0360c2c1b4a2aaebf4a3b8c9} + * + * @since 7.0.3 + * @since 8.2.0 Moved from the `Sniff` class to this class. * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The position in the stack of the diff --git a/PHPCompatibility/Sniff.php b/PHPCompatibility/Sniff.php index 987745a8..38fb7092 100644 --- a/PHPCompatibility/Sniff.php +++ b/PHPCompatibility/Sniff.php @@ -1,11 +1,11 @@ - * @copyright 2014 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility; @@ -17,22 +17,32 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniff. + * Base class from which all PHPCompatibility sniffs extend. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2014 Cu.be Solutions bvba + * @since 5.6 */ abstract class Sniff implements PHPCS_Sniff { + /** + * Regex to match variables in a double quoted string. + * + * This matches plain variables, but also more complex variables, such + * as $obj->prop, self::prop and $var[]. + * + * @since 7.1.2 + * + * @var string + */ const REGEX_COMPLEX_VARS = '`(?:(\{)?(?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(?:->\$?(?P>varname)|\[[^\]]+\]|::\$?(?P>varname)|\([^\)]*\))*(?(3)\}|)(?(2)\}|)(?(1)\}|)`'; /** * List of superglobals as an array of strings. * - * Used by the ParameterShadowSuperGlobals and ForbiddenClosureUseVariableNames sniffs. + * Used by the ForbiddenParameterShadowSuperGlobals and ForbiddenClosureUseVariableNames sniffs. + * + * @since 7.0.0 + * @since 7.1.4 Moved from the `ForbiddenParameterShadowSuperGlobals` sniff to the base `Sniff` class. * * @var array */ @@ -54,6 +64,9 @@ abstract class Sniff implements PHPCS_Sniff * Used by the new/removed hash algorithm sniffs. * Key is the function name, value is the 1-based parameter position in the function call. * + * @since 5.5 + * @since 7.0.7 Moved from the `RemovedHashAlgorithms` sniff to the base `Sniff` class. + * * @var array */ protected $hashAlgoFunctions = array( @@ -72,6 +85,8 @@ abstract class Sniff implements PHPCS_Sniff * Used by the new/removed ini directives sniffs. * Key is the function name, value is the 1-based parameter position in the function call. * + * @since 7.1.0 + * * @var array */ protected $iniFunctions = array( @@ -98,6 +113,9 @@ abstract class Sniff implements PHPCS_Sniff * PHP version numbers should always be in Major.Minor format. Both "5", "5.3.2" * would be treated as invalid, and ignored. * + * @since 7.0.0 + * @since 7.1.3 Now allows for partial ranges such as `5.2-`. + * * @return array $arrTestVersions will hold an array containing min/max version * of PHP that we are checking against (see above). If only a * single version number is specified, then this is used as @@ -167,6 +185,8 @@ private function getTestVersion() * * Should be used when sniffing for *old* PHP features (deprecated/removed). * + * @since 5.6 + * * @param string $phpVersion A PHP version number in 'major.minor' format. * * @return bool True if testVersion has not been provided or if the PHP version @@ -178,7 +198,7 @@ public function supportsAbove($phpVersion) $testVersion = $this->getTestVersion(); $testVersion = $testVersion[1]; - if (is_null($testVersion) + if (\is_null($testVersion) || version_compare($testVersion, $phpVersion) >= 0 ) { return true; @@ -194,6 +214,8 @@ public function supportsAbove($phpVersion) * * Should be used when sniffing for *new* PHP features. * + * @since 5.6 + * * @param string $phpVersion A PHP version number in 'major.minor' format. * * @return bool True if the PHP version is equal to or lower than the lowest @@ -205,7 +227,7 @@ public function supportsBelow($phpVersion) $testVersion = $this->getTestVersion(); $testVersion = $testVersion[0]; - if (is_null($testVersion) === false + if (\is_null($testVersion) === false && version_compare($testVersion, $phpVersion) <= 0 ) { return true; @@ -218,6 +240,8 @@ public function supportsBelow($phpVersion) /** * Add a PHPCS message to the output stack as either a warning or an error. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file the message applies to. * @param string $message The message. * @param int $stackPtr The position of the token @@ -246,6 +270,8 @@ public function addMessage(File $phpcsFile, $message, $stackPtr, $isError, $code * * Pre-empt issues with arbitrary strings being used as error codes in XML and PHP. * + * @since 7.1.0 + * * @param string $baseString Arbitrary string. * * @return string @@ -261,6 +287,8 @@ public function stringToErrorCode($baseString) * * Intended for use with the contents of a T_CONSTANT_ENCAPSED_STRING / T_DOUBLE_QUOTED_STRING. * + * @since 7.0.6 + * * @param string $string The raw string. * * @return string String without quotes around it. @@ -276,6 +304,8 @@ public function stripQuotes($string) * * Intended for use with the contents of a T_DOUBLE_QUOTED_STRING. * + * @since 7.1.2 + * * @param string $string The raw string. * * @return string String without variables in it. @@ -293,15 +323,15 @@ public function stripVariables($string) /** * Make all top level array keys in an array lowercase. * + * @since 7.1.0 + * * @param array $array Initial array. * * @return array Same array, but with all lowercase top level keys. */ public function arrayKeysToLowercase($array) { - $keys = array_keys($array); - $keys = array_map('strtolower', $keys); - return array_combine($keys, $array); + return array_change_key_case($array, \CASE_LOWER); } @@ -317,6 +347,8 @@ public function arrayKeysToLowercase($array) * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/120 * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/152 * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the function call token. * @@ -332,7 +364,7 @@ public function doesFunctionCallHaveParameters(File $phpcsFile, $stackPtr) } // Is this one of the tokens this function handles ? - if (in_array($tokens[$stackPtr]['code'], array(\T_STRING, \T_ARRAY, \T_OPEN_SHORT_ARRAY, \T_VARIABLE), true) === false) { + if (\in_array($tokens[$stackPtr]['code'], array(\T_STRING, \T_ARRAY, \T_OPEN_SHORT_ARRAY, \T_VARIABLE), true) === false) { return false; } @@ -387,6 +419,8 @@ public function doesFunctionCallHaveParameters(File $phpcsFile, $stackPtr) * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/114 * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/151 * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the function call token. * @@ -398,7 +432,7 @@ public function getFunctionCallParameterCount(File $phpcsFile, $stackPtr) return 0; } - return count($this->getFunctionCallParameters($phpcsFile, $stackPtr)); + return \count($this->getFunctionCallParameters($phpcsFile, $stackPtr)); } @@ -415,6 +449,8 @@ public function getFunctionCallParameterCount(File $phpcsFile, $stackPtr) * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer, * it will tokenize the values / key/value pairs contained in the array call. * + * @since 7.0.5 Split off from the `getFunctionCallParameterCount()` method. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the function call token. * @@ -446,7 +482,7 @@ public function getFunctionCallParameters(File $phpcsFile, $stackPtr) // Which nesting level is the one we are interested in ? if (isset($tokens[$opener]['nested_parenthesis'])) { - $nestedParenthesisCount += count($tokens[$opener]['nested_parenthesis']); + $nestedParenthesisCount += \count($tokens[$opener]['nested_parenthesis']); } $parameters = array(); @@ -479,7 +515,7 @@ public function getFunctionCallParameters(File $phpcsFile, $stackPtr) // Ignore comma's at a lower nesting level. if ($tokens[$nextComma]['type'] === 'T_COMMA' && isset($tokens[$nextComma]['nested_parenthesis']) - && count($tokens[$nextComma]['nested_parenthesis']) !== $nestedParenthesisCount + && \count($tokens[$nextComma]['nested_parenthesis']) !== $nestedParenthesisCount ) { continue; } @@ -494,9 +530,11 @@ public function getFunctionCallParameters(File $phpcsFile, $stackPtr) $parameters[$cnt]['end'] = $nextComma - 1; $parameters[$cnt]['raw'] = trim($phpcsFile->getTokensAsString($paramStart, ($nextComma - $paramStart))); - // Check if there are more tokens before the closing parenthesis. - // Prevents code like the following from setting a third parameter: - // functionCall( $param1, $param2, ); + /* + * Check if there are more tokens before the closing parenthesis. + * Prevents code like the following from setting a third parameter: + * `functionCall( $param1, $param2, );`. + */ $hasNextParam = $phpcsFile->findNext(Tokens::$emptyTokens, $nextComma + 1, $closer, true, null, true); if ($hasNextParam === false) { break; @@ -521,6 +559,8 @@ public function getFunctionCallParameters(File $phpcsFile, $stackPtr) * of the parameter at a specific offset. * If the specified parameter is not found, will return false. * + * @since 7.0.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the function call token. * @param int $paramOffset The 1-based index position of the parameter to retrieve. @@ -546,6 +586,8 @@ public function getFunctionCallParameter(File $phpcsFile, $stackPtr, $paramOffse * will check that the token has at least one condition which is of a * type defined in $validScopes. * + * @since 7.0.5 Largely split off from the `inClassScope()` method. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the token. * @param array|int $validScopes Optional. Array of valid scopes @@ -583,6 +625,8 @@ public function tokenHasScope(File $phpcsFile, $stackPtr, $validScopes = null) /** * Verify whether a token is within a class scope. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the token. * @param bool $strict Whether to strictly check for the T_CLASS @@ -594,7 +638,7 @@ public function tokenHasScope(File $phpcsFile, $stackPtr, $validScopes = null) public function inClassScope(File $phpcsFile, $stackPtr, $strict = true) { $validScopes = array(\T_CLASS); - if (defined('T_ANON_CLASS') === true) { + if (\defined('T_ANON_CLASS') === true) { $validScopes[] = \T_ANON_CLASS; } @@ -612,6 +656,8 @@ public function inClassScope(File $phpcsFile, $stackPtr, $strict = true) * * Returns an empty string if the class name could not be reliably inferred. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of a T_NEW token. * @@ -666,6 +712,8 @@ public function getFQClassNameFromNewToken(File $phpcsFile, $stackPtr) * Returns an empty string if the class does not extend another class or if * the class name could not be reliably inferred. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of a T_CLASS token. * @@ -688,7 +736,7 @@ public function getFQExtendedClassName(File $phpcsFile, $stackPtr) } $extends = PHPCSHelper::findExtendedClassName($phpcsFile, $stackPtr); - if (empty($extends) || is_string($extends) === false) { + if (empty($extends) || \is_string($extends) === false) { return ''; } @@ -702,6 +750,8 @@ public function getFQExtendedClassName(File $phpcsFile, $stackPtr) * * Returns an empty string if the class name could not be reliably inferred. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of a T_NEW token. * @@ -726,7 +776,7 @@ public function getFQClassNameFromDoubleColonToken(File $phpcsFile, $stackPtr) } // Nothing to do if 'parent' or 'static' as we don't know how far the class tree extends. - if (in_array($tokens[$stackPtr - 1]['code'], array(\T_PARENT, \T_STATIC), true)) { + if (\in_array($tokens[$stackPtr - 1]['code'], array(\T_PARENT, \T_STATIC), true)) { return ''; } @@ -766,6 +816,8 @@ public function getFQClassNameFromDoubleColonToken(File $phpcsFile, $stackPtr) * Checks if a class/function/constant name is already fully qualified and * if not, enrich it with the relevant namespace information. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the token. * @param string $name The class / function / constant name. @@ -797,10 +849,15 @@ public function getFQName(File $phpcsFile, $stackPtr, $name) /** * Is the class/function/constant name namespaced or global ? * + * @since 7.0.3 + * * @param string $FQName Fully Qualified name of a class, function etc. * I.e. should always start with a `\`. * * @return bool True if namespaced, false if global. + * + * @throws \PHP_CodeSniffer_Exception If the name in the passed parameter + * is not fully qualified. */ public function isNamespaced($FQName) { @@ -815,6 +872,8 @@ public function isNamespaced($FQName) /** * Determine the namespace name an arbitrary token lives in. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The token position for which to determine the namespace. * @@ -878,6 +937,8 @@ public function determineNamespace(File $phpcsFile, $stackPtr) * For hierarchical namespaces, the name will be composed of several tokens, * i.e. MyProject\Sub\Level which will be returned together as one string. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int|bool $stackPtr The position of a T_NAMESPACE token. * @@ -898,14 +959,16 @@ public function getDeclaredNamespaceName(File $phpcsFile, $stackPtr) } if ($tokens[($stackPtr + 1)]['code'] === \T_NS_SEPARATOR) { - // Not a namespace declaration, but use of, i.e. namespace\someFunction(); + // Not a namespace declaration, but use of, i.e. `namespace\someFunction();`. return false; } $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true); if ($tokens[$nextToken]['code'] === \T_OPEN_CURLY_BRACKET) { - // Declaration for global namespace when using multiple namespaces in a file. - // I.e.: namespace {} + /* + * Declaration for global namespace when using multiple namespaces in a file. + * I.e.: `namespace {}`. + */ return ''; } @@ -934,6 +997,8 @@ public function getDeclaredNamespaceName(File $phpcsFile, $stackPtr) * * Expects to be passed T_RETURN_TYPE, T_FUNCTION or T_CLOSURE token. * + * @since 7.1.2 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the token. * @@ -945,7 +1010,7 @@ public function getReturnTypeHintToken(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); - if (defined('T_RETURN_TYPE') && $tokens[$stackPtr]['code'] === \T_RETURN_TYPE) { + if (\defined('T_RETURN_TYPE') && $tokens[$stackPtr]['code'] === \T_RETURN_TYPE) { return $stackPtr; } @@ -1015,10 +1080,12 @@ public function getReturnTypeHintToken(File $phpcsFile, $stackPtr) * Expects to be passed a T_RETURN_TYPE token or the return value from a call to * the Sniff::getReturnTypeHintToken() method. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the return type token. * - * @return string|false The name of the return type token. + * @return string The name of the return type token. */ public function getReturnTypeHintName(File $phpcsFile, $stackPtr) { @@ -1030,7 +1097,7 @@ public function getReturnTypeHintName(File $phpcsFile, $stackPtr) || ($tokens[$colon]['code'] !== \T_COLON && $tokens[$colon]['code'] !== \T_INLINE_ELSE) ) { // Shouldn't happen, just in case. - return; + return ''; } $returnTypeHint = ''; @@ -1045,7 +1112,7 @@ public function getReturnTypeHintName(File $phpcsFile, $stackPtr) continue; } - if (defined('T_NULLABLE') === false && $tokens[$i]['code'] === \T_INLINE_THEN) { + if (\defined('T_NULLABLE') === false && $tokens[$i]['code'] === \T_INLINE_THEN) { // Old PHPCS. continue; } @@ -1065,6 +1132,8 @@ public function getReturnTypeHintName(File $phpcsFile, $stackPtr) * anonymous classes. Along the same lines, the`getMemberProperties()` * method does not support the `var` prefix. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The position in the stack of the * T_VARIABLE token to verify. @@ -1110,6 +1179,8 @@ public function isClassProperty(File $phpcsFile, $stackPtr) /** * Check whether a T_CONST token is a class constant declaration. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The position in the stack of the * T_CONST token to verify. @@ -1144,9 +1215,11 @@ public function isClassConstant(File $phpcsFile, $stackPtr) * * Used to check, for instance, if a T_CONST is a class constant. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The position in the stack of the - * T_CONST token to verify. + * token to verify. * @param array $validScopes Array of token types. * Keys should be the token types in string * format to allow for newer token types. @@ -1188,6 +1261,8 @@ protected function validDirectScope(File $phpcsFile, $stackPtr, $validScopes) * Strips potential nullable indicator and potential global namespace * indicator from the type hints before returning them. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the token. * @@ -1205,7 +1280,7 @@ public function getTypeHintsFromFunctionDeclaration(File $phpcsFile, $stackPtr) } $parameters = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr); - if (empty($parameters) || is_array($parameters) === false) { + if (empty($parameters) || \is_array($parameters) === false) { return array(); } @@ -1234,6 +1309,8 @@ public function getTypeHintsFromFunctionDeclaration(File $phpcsFile, $stackPtr) /** * Get the hash algorithm name from the parameter in a hash function call. * + * @since 7.0.7 Logic was originally contained in the `RemovedHashAlgorithms` sniff. + * * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile. * @param int $stackPtr The position of the T_STRING function token. * @@ -1278,8 +1355,10 @@ public function getHashAlgorithmParameter(File $phpcsFile, $stackPtr) /** * Determine whether an arbitrary T_STRING token is the use of a global constant. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the function call token. + * @param int $stackPtr The position of the T_STRING token. * * @return bool */ @@ -1410,6 +1489,8 @@ public function isUseOfGlobalConstant(File $phpcsFile, $stackPtr) * * Note: Zero is *not* regarded as a positive number. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Start of the snippet (inclusive), i.e. this * token will be examined as part of the snippet. @@ -1442,6 +1523,8 @@ public function isPositiveNumber(File $phpcsFile, $start, $end, $allowFloats = f * * Note: Zero is *not* regarded as a negative number. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Start of the snippet (inclusive), i.e. this * token will be examined as part of the snippet. @@ -1476,6 +1559,8 @@ public function isNegativeNumber(File $phpcsFile, $start, $end, $allowFloats = f * Mainly intended for examining variable assignments, function call parameters, array values * where the start and end of the snippet to examine is very clear. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Start of the snippet (inclusive), i.e. this * token will be examined as part of the snippet. @@ -1492,7 +1577,7 @@ public function isNegativeNumber(File $phpcsFile, $start, $end, $allowFloats = f */ protected function isNumber(File $phpcsFile, $start, $end, $allowFloats = false) { - $stringTokens = Tokens::$heredocTokens + Tokens::$stringTokens; + $stringTokens = Tokens::$heredocTokens + Tokens::$stringTokens; $validTokens = array(); $validTokens[\T_LNUMBER] = true; @@ -1578,7 +1663,7 @@ protected function isNumber(File $phpcsFile, $start, $end, $allowFloats = false) /* * Regexes based on the formats outlined in the manual, created by JRF. - * @link http://php.net/manual/en/language.types.float.php + * @link https://www.php.net/manual/en/language.types.float.php */ $regexInt = '`^\s*[0-9]+`'; $regexFloat = '`^\s*(?:[+-]?(?:(?:(?P[0-9]+)|(?P([0-9]*\.(?P>LNUM)|(?P>LNUM)\.[0-9]*)))[eE][+-]?(?P>LNUM))|(?P>DNUM))`'; @@ -1650,6 +1735,8 @@ protected function isNumber(File $phpcsFile, $start, $end, $allowFloats = false) * Mainly intended for examining variable assignments, function call parameters, array values * where the start and end of the snippet to examine is very clear. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Start of the snippet (inclusive), i.e. this * token will be examined as part of the snippet. @@ -1663,7 +1750,7 @@ protected function isNumericCalculation(File $phpcsFile, $start, $end) $arithmeticTokens = Tokens::$arithmeticTokens; // phpcs:disable PHPCompatibility.Constants.NewConstants.t_powFound - if (defined('T_POW') && isset($arithmeticTokens[\T_POW]) === false) { + if (\defined('T_POW') && isset($arithmeticTokens[\T_POW]) === false) { // T_POW was not added to the arithmetic array until PHPCS 2.9.0. $arithmeticTokens[\T_POW] = \T_POW; } @@ -1692,7 +1779,7 @@ protected function isNumericCalculation(File $phpcsFile, $start, $end) && isset($tokens[($arithmeticOperator + 1)]) === true ) { // Recognize T_POW for PHPCS < 2.4.0 on low PHP versions. - if (defined('T_POW') === false + if (\defined('T_POW') === false && $tokens[$arithmeticOperator]['code'] === \T_MULTIPLY && $tokens[($arithmeticOperator + 1)]['code'] === \T_MULTIPLY && isset($tokens[$arithmeticOperator + 2]) === true @@ -1724,12 +1811,56 @@ protected function isNumericCalculation(File $phpcsFile, $start, $end) } + + /** + * Determine whether a ternary is a short ternary, i.e. without "middle". + * + * N.B.: This is a back-fill for a new method which is expected to go into + * PHP_CodeSniffer 3.5.0. + * Once that method has been merged into PHPCS, this one should be moved + * to the PHPCSHelper.php file. + * + * @since 9.2.0 + * + * @codeCoverageIgnore Method as pulled upstream is accompanied by unit tests. + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the ternary operator + * in the stack. + * + * @return bool True if short ternary, or false otherwise. + */ + public function isShortTernary(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]) === false + || $tokens[$stackPtr]['code'] !== \T_INLINE_THEN + ) { + return false; + } + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + // Live coding or parse error. + return false; + } + + if ($tokens[$nextNonEmpty]['code'] === \T_INLINE_ELSE) { + return true; + } + + return false; + } + + /** * Determine whether a T_OPEN/CLOSE_SHORT_ARRAY token is a list() construct. * * Note: A variety of PHPCS versions have bugs in the tokenizing of short arrays. * In that case, the tokens are identified as T_OPEN/CLOSE_SQUARE_BRACKET. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the function call token. * @@ -1873,6 +2004,8 @@ public function isShortList(File $phpcsFile, $stackPtr) /** * Determine whether the tokens between $start and $end could together represent a variable. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Starting point stack pointer. Inclusive. * I.e. this token should be taken into account. @@ -1921,7 +2054,7 @@ public function isVariable(File $phpcsFile, $start, $end, $targetNestingLevel) // Check if the variable found is at the right level. Deeper levels are always an error. if (isset($tokens[$hasVariable]['nested_parenthesis']) - && count($tokens[$hasVariable]['nested_parenthesis']) !== $targetNestingLevel + && \count($tokens[$hasVariable]['nested_parenthesis']) !== $targetNestingLevel ) { return false; } @@ -1957,4 +2090,178 @@ public function isVariable(File $phpcsFile, $start, $end, $targetNestingLevel) return true; } + + /** + * Determine whether a T_MINUS/T_PLUS token is a unary operator. + * + * N.B.: This is a back-fill for a new method which is expected to go into + * PHP_CodeSniffer 3.5.0. + * Once that method has been merged into PHPCS, this one should be moved + * to the PHPCSHelper.php file. + * + * @since 9.2.0 + * + * @codeCoverageIgnore Method as pulled upstream is accompanied by unit tests. + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the plus/minus token. + * + * @return bool True if the token passed is a unary operator. + * False otherwise or if the token is not a T_PLUS/T_MINUS token. + */ + public static function isUnaryPlusMinus(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if (isset($tokens[$stackPtr]) === false + || ($tokens[$stackPtr]['code'] !== \T_PLUS + && $tokens[$stackPtr]['code'] !== \T_MINUS) + ) { + return false; + } + + $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($next === false) { + // Live coding or parse error. + return false; + } + + if (isset(Tokens::$operators[$tokens[$next]['code']]) === true) { + // Next token is an operator, so this is not a unary. + return false; + } + + $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + + if ($tokens[$prev]['code'] === \T_RETURN) { + // Just returning a positive/negative value; eg. (return -1). + return true; + } + + if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) { + // Just trying to operate on a positive/negative value; eg. ($var * -1). + return true; + } + + if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) { + // Just trying to compare a positive/negative value; eg. ($var === -1). + return true; + } + + if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) { + // Just trying to compare a positive/negative value; eg. ($var || -1 === $b). + return true; + } + + if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) { + // Just trying to assign a positive/negative value; eg. ($var = -1). + return true; + } + + if (isset(Tokens::$castTokens[$tokens[$prev]['code']]) === true) { + // Just casting a positive/negative value; eg. (string) -$var. + return true; + } + + // Other indicators that a plus/minus sign is a unary operator. + $invalidTokens = array( + \T_COMMA => true, + \T_OPEN_PARENTHESIS => true, + \T_OPEN_SQUARE_BRACKET => true, + \T_OPEN_SHORT_ARRAY => true, + \T_COLON => true, + \T_INLINE_THEN => true, + \T_INLINE_ELSE => true, + \T_CASE => true, + \T_OPEN_CURLY_BRACKET => true, + \T_STRING_CONCAT => true, + ); + + if (isset($invalidTokens[$tokens[$prev]['code']]) === true) { + // Just trying to use a positive/negative value; eg. myFunction($var, -2). + return true; + } + + return false; + } + + /** + * Get the complete contents of a multi-line text string. + * + * N.B.: This is a back-fill for a new method which is expected to go into + * PHP_CodeSniffer 3.5.0. + * Once that method has been merged into PHPCS, this one should be moved + * to the PHPCSHelper.php file. + * + * @since 9.3.0 + * + * @codeCoverageIgnore Method as pulled upstream is accompanied by unit tests. + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr Pointer to the first text string token + * of a multi-line text string or to a + * Nowdoc/Heredoc opener. + * @param bool $stripQuotes Optional. Whether to strip text delimiter + * quotes off the resulting text string. + * Defaults to true. + * + * @return string + * + * @throws \PHP_CodeSniffer_Exception If the specified position is not a + * valid text string token or if the + * token is not the first text string token. + */ + public function getCompleteTextString(File $phpcsFile, $stackPtr, $stripQuotes = true) + { + $tokens = $phpcsFile->getTokens(); + + // Must be the start of a text string token. + if ($tokens[$stackPtr]['code'] !== \T_START_HEREDOC + && $tokens[$stackPtr]['code'] !== \T_START_NOWDOC + && $tokens[$stackPtr]['code'] !== \T_CONSTANT_ENCAPSED_STRING + && $tokens[$stackPtr]['code'] !== \T_DOUBLE_QUOTED_STRING + ) { + throw new PHPCS_Exception('$stackPtr must be of type T_START_HEREDOC, T_START_NOWDOC, T_CONSTANT_ENCAPSED_STRING or T_DOUBLE_QUOTED_STRING'); + } + + if ($tokens[$stackPtr]['code'] === \T_CONSTANT_ENCAPSED_STRING + || $tokens[$stackPtr]['code'] === \T_DOUBLE_QUOTED_STRING + ) { + $prev = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true); + if ($tokens[$stackPtr]['code'] === $tokens[$prev]['code']) { + throw new PHPCS_Exception('$stackPtr must be the start of the text string'); + } + } + + switch ($tokens[$stackPtr]['code']) { + case \T_START_HEREDOC: + $stripQuotes = false; + $targetType = \T_HEREDOC; + $current = ($stackPtr + 1); + break; + + case \T_START_NOWDOC: + $stripQuotes = false; + $targetType = \T_NOWDOC; + $current = ($stackPtr + 1); + break; + + default: + $targetType = $tokens[$stackPtr]['code']; + $current = $stackPtr; + break; + } + + $string = ''; + do { + $string .= $tokens[$current]['content']; + ++$current; + } while ($tokens[$current]['code'] === $targetType); + + if ($stripQuotes === true) { + return $this->stripQuotes($string); + } + + return $string; + } } diff --git a/PHPCompatibility/Sniffs/Classes/ForbiddenAbstractPrivateMethodsSniff.php b/PHPCompatibility/Sniffs/Classes/ForbiddenAbstractPrivateMethodsSniff.php new file mode 100644 index 00000000..d0d74dfe --- /dev/null +++ b/PHPCompatibility/Sniffs/Classes/ForbiddenAbstractPrivateMethodsSniff.php @@ -0,0 +1,90 @@ + true, + 'T_TRAIT' => true, + 'T_ANON_CLASS' => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + return array(\T_FUNCTION); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('5.1') === false) { + return; + } + + if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) { + // Function, not method. + return; + } + + $properties = $phpcsFile->getMethodProperties($stackPtr); + if ($properties['scope'] !== 'private' || $properties['is_abstract'] !== true) { + return; + } + + $phpcsFile->addError( + 'Abstract methods cannot be declared as private since PHP 5.1', + $stackPtr, + 'Found' + ); + } +} diff --git a/PHPCompatibility/Sniffs/Classes/NewAnonymousClassesSniff.php b/PHPCompatibility/Sniffs/Classes/NewAnonymousClassesSniff.php index 3741ffb7..572b6fcc 100644 --- a/PHPCompatibility/Sniffs/Classes/NewAnonymousClassesSniff.php +++ b/PHPCompatibility/Sniffs/Classes/NewAnonymousClassesSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Classes; @@ -16,15 +15,14 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Classes\NewAnonymousClasses. - * - * Anonymous classes are supported in PHP 7.0 + * Anonymous classes are supported since PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/language.oop5.anonymous.php + * @link https://wiki.php.net/rfc/anonymous_classes + * + * @since 7.0.0 */ class NewAnonymousClassesSniff extends Sniff { @@ -35,6 +33,8 @@ class NewAnonymousClassesSniff extends Sniff * The dedicated anonymous class token is added from the `register()` * method if the token is available. * + * @since 7.1.2 + * * @var array */ private $indicators = array( @@ -44,11 +44,13 @@ class NewAnonymousClassesSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() { - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $this->indicators[\T_ANON_CLASS] = \T_ANON_CLASS; } @@ -59,6 +61,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Classes/NewClassesSniff.php b/PHPCompatibility/Sniffs/Classes/NewClassesSniff.php index 1939cb90..19e5e439 100644 --- a/PHPCompatibility/Sniffs/Classes/NewClassesSniff.php +++ b/PHPCompatibility/Sniffs/Classes/NewClassesSniff.php @@ -1,11 +1,11 @@ - * @copyright 2013 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Classes; @@ -14,12 +14,21 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Classes\NewClassesSniff. + * Detect use of new PHP native classes. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2013 Cu.be Solutions bvba + * The sniff analyses the following constructs to find usage of new classes: + * - Class instantiation using the `new` keyword. + * - (Anonymous) Class declarations to detect new classes being extended by userland classes. + * - Static use of class properties, constants or functions using the double colon. + * - Function/closure declarations to detect new classes used as parameter type declarations. + * - Function/closure declarations to detect new classes used as return type declarations. + * - Try/catch statements to detect new exception classes being caught. + * + * PHP version All + * + * @since 5.5 + * @since 5.6 Now extends the base `Sniff` class. + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` class. */ class NewClassesSniff extends AbstractNewFeatureSniff { @@ -30,6 +39,8 @@ class NewClassesSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the class appears. * + * @since 5.5 + * * @var array(string => array(string => bool)) */ protected $newClasses = array( @@ -385,6 +396,26 @@ class NewClassesSniff extends AbstractNewFeatureSniff '7.1' => true, ), + 'FFI' => array( + '7.3' => false, + '7.4' => true, + ), + 'FFI\CData' => array( + '7.3' => false, + '7.4' => true, + ), + 'FFI\CType' => array( + '7.3' => false, + '7.4' => true, + ), + 'ReflectionReference' => array( + '7.3' => false, + '7.4' => true, + ), + 'WeakReference' => array( + '7.3' => false, + '7.4' => true, + ), ); /** @@ -396,9 +427,11 @@ class NewClassesSniff extends AbstractNewFeatureSniff * {@internal Classes listed here do not need to be added to the $newClasses * property as well. * This list is automatically added to the $newClasses property - * in the `register()` method.}} + * in the `register()` method.} * - * {@internal Helper to update this list: https://3v4l.org/MhlUp}} + * {@internal Helper to update this list: https://3v4l.org/MhlUp} + * + * @since 7.1.4 * * @var array(string => array(string => bool)) */ @@ -565,12 +598,32 @@ class NewClassesSniff extends AbstractNewFeatureSniff '7.2' => false, '7.3' => true, ), + + 'FFI\Exception' => array( + '7.3' => false, + '7.4' => true, + ), + 'FFI\ParserException' => array( + '7.3' => false, + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * @since 7.0.3 - Now also targets the `class` keyword to detect extended classes. + * - Now also targets double colons to detect static class use. + * @since 7.1.4 - Now also targets anonymous classes to detect extended classes. + * - Now also targets functions/closures to detect new classes used + * as parameter type declarations. + * - Now also targets the `catch` control structure to detect new + * exception classes being caught. + * @since 8.2.0 Now also targets the `T_RETURN_TYPE` token to detect new classes used + * as return type declarations. + * * @return array */ public function register() @@ -591,11 +644,11 @@ public function register() \T_CATCH, ); - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $targets[] = \T_ANON_CLASS; } - if (defined('T_RETURN_TYPE')) { + if (\defined('T_RETURN_TYPE')) { $targets[] = \T_RETURN_TYPE; } @@ -606,6 +659,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -647,6 +702,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Processes this test for when a token resulting in a singular class name is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -690,7 +747,9 @@ private function processSingularToken(File $phpcsFile, $stackPtr) /** * Processes this test for when a function token is encountered. * - * - Detect new classes when used as a type hint. + * - Detect new classes when used as a parameter type declaration. + * + * @since 7.1.4 * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in @@ -702,7 +761,7 @@ private function processFunctionToken(File $phpcsFile, $stackPtr) { // Retrieve typehints stripped of global NS indicator and/or nullable indicator. $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr); - if (empty($typeHints) || is_array($typeHints) === false) { + if (empty($typeHints) || \is_array($typeHints) === false) { return; } @@ -726,6 +785,8 @@ private function processFunctionToken(File $phpcsFile, $stackPtr) * * - Detect exceptions when used in a catch statement. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -792,6 +853,8 @@ private function processCatchToken(File $phpcsFile, $stackPtr) * * - Detect new classes when used as a return type declaration. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -800,7 +863,11 @@ private function processCatchToken(File $phpcsFile, $stackPtr) */ private function processReturnTypeToken(File $phpcsFile, $stackPtr) { - $returnTypeHint = $this->getReturnTypeHintName($phpcsFile, $stackPtr); + $returnTypeHint = $this->getReturnTypeHintName($phpcsFile, $stackPtr); + if (empty($returnTypeHint)) { + return; + } + $returnTypeHint = ltrim($returnTypeHint, '\\'); $returnTypeHintLc = strtolower($returnTypeHint); @@ -820,6 +887,8 @@ private function processReturnTypeToken(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -833,6 +902,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/Classes/NewConstVisibilitySniff.php b/PHPCompatibility/Sniffs/Classes/NewConstVisibilitySniff.php index c3a3093d..d2cb3feb 100644 --- a/PHPCompatibility/Sniffs/Classes/NewConstVisibilitySniff.php +++ b/PHPCompatibility/Sniffs/Classes/NewConstVisibilitySniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Classes; @@ -16,21 +15,22 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Classes\NewConstVisibility. - * * Visibility for class constants is available since PHP 7.1. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/class_const_visibility + * @link https://www.php.net/manual/en/language.oop5.constants.php#language.oop5.basic.class.this + * + * @since 7.0.7 */ class NewConstVisibilitySniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.7 + * * @return array */ public function register() @@ -41,6 +41,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Classes/NewLateStaticBindingSniff.php b/PHPCompatibility/Sniffs/Classes/NewLateStaticBindingSniff.php index 85a6beec..9658a76f 100644 --- a/PHPCompatibility/Sniffs/Classes/NewLateStaticBindingSniff.php +++ b/PHPCompatibility/Sniffs/Classes/NewLateStaticBindingSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Classes; @@ -16,19 +15,27 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Classes\NewLateStaticBindingSniff. + * Detect use of late static binding as introduced in PHP 5.3. + * + * Checks for: + * - Late static binding as introduced in PHP 5.3. + * - Late static binding being used outside of class scope (unsupported). * * PHP version 5.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/language.oop5.late-static-bindings.php + * @link https://wiki.php.net/rfc/lsb_parentself_forwarding + * + * @since 7.0.3 + * @since 9.0.0 Renamed from `LateStaticBindingSniff` to `NewLateStaticBindingSniff`. */ class NewLateStaticBindingSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.3 + * * @return array */ public function register() @@ -40,6 +47,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Classes/NewTypedPropertiesSniff.php b/PHPCompatibility/Sniffs/Classes/NewTypedPropertiesSniff.php new file mode 100644 index 00000000..a609acc1 --- /dev/null +++ b/PHPCompatibility/Sniffs/Classes/NewTypedPropertiesSniff.php @@ -0,0 +1,132 @@ + \T_PRIVATE, + \T_PROTECTED => \T_PROTECTED, + \T_PUBLIC => \T_PUBLIC, + \T_STATIC => \T_STATIC, + \T_VAR => \T_VAR, + ); + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + return array(\T_VARIABLE); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->isClassProperty($phpcsFile, $stackPtr) === false) { + return; + } + + $find = $this->modifierKeywords; + $find += array( + \T_SEMICOLON => \T_SEMICOLON, + \T_OPEN_CURLY_BRACKET => \T_OPEN_CURLY_BRACKET, + ); + + $tokens = $phpcsFile->getTokens(); + $modifier = $phpcsFile->findPrevious($find, ($stackPtr - 1)); + if ($modifier === false + || $tokens[$modifier]['code'] === \T_SEMICOLON + || $tokens[$modifier]['code'] === \T_OPEN_CURLY_BRACKET + ) { + // Parse error. Ignore. + return; + } + + $type = $phpcsFile->findNext(Tokens::$emptyTokens, ($modifier + 1), null, true); + if ($tokens[$type]['code'] === \T_VARIABLE) { + return; + } + + // Still here ? In that case, this will be a typed property. + if ($this->supportsBelow('7.3') === true) { + $phpcsFile->addError( + 'Typed properties are not supported in PHP 7.3 or earlier', + $type, + 'Found' + ); + } + + if ($this->supportsAbove('7.4') === true) { + // Examine the type to verify it's valid. + if ($tokens[$type]['type'] === 'T_NULLABLE' + // Needed to support PHPCS < 3.5.0 which doesn't correct to the nullable token type yet. + || $tokens[$type]['code'] === \T_INLINE_THEN + ) { + $type = $phpcsFile->findNext(Tokens::$emptyTokens, ($type + 1), null, true); + } + + $content = $tokens[$type]['content']; + if ($content === 'void' || $content === 'callable') { + $phpcsFile->addError( + '%s is not supported as a type declaration for properties', + $type, + 'InvalidType', + array($content) + ); + } + } + + $endOfStatement = $phpcsFile->findNext(\T_SEMICOLON, ($stackPtr + 1)); + if ($endOfStatement !== false) { + // Don't throw the same error multiple times for multi-property declarations. + return ($endOfStatement + 1); + } + } +} diff --git a/PHPCompatibility/Sniffs/Classes/RemovedOrphanedParentSniff.php b/PHPCompatibility/Sniffs/Classes/RemovedOrphanedParentSniff.php new file mode 100644 index 00000000..7ecfd6ca --- /dev/null +++ b/PHPCompatibility/Sniffs/Classes/RemovedOrphanedParentSniff.php @@ -0,0 +1,115 @@ + true, + 'T_ANON_CLASS' => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + return array(\T_PARENT); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('7.4') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + + if (empty($tokens[$stackPtr]['conditions']) === true) { + // Use within the global namespace. Not our concern. + return; + } + + /* + * Find the class within which this parent keyword is used. + */ + $conditions = $tokens[$stackPtr]['conditions']; + $conditions = array_reverse($conditions, true); + $classPtr = false; + + foreach ($conditions as $ptr => $type) { + if (isset($this->classScopeTokens[$tokens[$ptr]['type']])) { + $classPtr = $ptr; + break; + } + } + + if ($classPtr === false) { + // Use outside of a class scope. Not our concern. + return; + } + + if (isset($tokens[$classPtr]['scope_opener']) === false) { + // No scope opener known. Probably a parse error. + return; + } + + $extends = $phpcsFile->findNext(\T_EXTENDS, ($classPtr + 1), $tokens[$classPtr]['scope_opener']); + if ($extends !== false) { + // Class has a parent. + return; + } + + $phpcsFile->addError( + 'Using "parent" inside a class without parent is deprecated since PHP 7.4', + $stackPtr, + 'Deprecated' + ); + } +} diff --git a/PHPCompatibility/Sniffs/Constants/NewConstantsSniff.php b/PHPCompatibility/Sniffs/Constants/NewConstantsSniff.php index 7a2193a9..27ec4217 100644 --- a/PHPCompatibility/Sniffs/Constants/NewConstantsSniff.php +++ b/PHPCompatibility/Sniffs/Constants/NewConstantsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Constants; @@ -13,11 +14,11 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Constants\NewConstantsSniff. + * Detect use of new PHP native global constants. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version All + * + * @since 8.1.0 */ class NewConstantsSniff extends AbstractNewFeatureSniff { @@ -28,9 +29,11 @@ class NewConstantsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the constant appears. * - * Note: PHP Constants are case-sensitive! + * Note: PHP constants are case-sensitive! + * + * @since 8.1.0 * - * @var array(string => array(string => bool|string|null)) + * @var array(string => array(string => bool)) */ protected $newConstants = array( 'E_STRICT' => array( @@ -2687,6 +2690,22 @@ class NewConstantsSniff extends AbstractNewFeatureSniff '7.1' => false, '7.2' => true, ), + 'SODIUM_BASE64_VARIANT_ORIGINAL' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_BASE64_VARIANT_URLSAFE' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING' => array( + '7.1' => false, + '7.2' => true, + ), 'SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES' => array( '7.1' => false, '7.2' => true, @@ -2735,6 +2754,22 @@ class NewConstantsSniff extends AbstractNewFeatureSniff '7.1' => false, '7.2' => true, ), + 'SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES' => array( + '7.1' => false, + '7.2' => true, + ), 'SODIUM_CRYPTO_AUTH_BYTES' => array( '7.1' => false, '7.2' => true, @@ -2835,6 +2870,10 @@ class NewConstantsSniff extends AbstractNewFeatureSniff '7.1' => false, '7.2' => true, ), + 'SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13' => array( + '7.1' => false, + '7.2' => true, + ), 'SODIUM_CRYPTO_PWHASH_ALG_DEFAULT' => array( '7.1' => false, '7.2' => true, @@ -2923,6 +2962,38 @@ class NewConstantsSniff extends AbstractNewFeatureSniff '7.1' => false, '7.2' => true, ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY' => array( + '7.1' => false, + '7.2' => true, + ), + 'SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL' => array( + '7.1' => false, + '7.2' => true, + ), 'SODIUM_CRYPTO_SIGN_BYTES' => array( '7.1' => false, '7.2' => true, @@ -3444,12 +3515,180 @@ class NewConstantsSniff extends AbstractNewFeatureSniff '7.2' => false, '7.3' => true, ), + + 'CURL_VERSION_ALTSVC' => array( + '7.3.5' => false, + '7.3.6' => true, + ), + 'CURL_VERSION_CURLDEBUG' => array( + '7.3.5' => false, + '7.3.6' => true, + ), + + 'IMG_FILTER_SCATTER' => array( + '7.3' => false, + '7.4' => true, + ), + 'MB_ONIGURUMA_VERSION' => array( + '7.3' => false, + '7.4' => true, + ), + 'SO_LABEL' => array( + '7.3' => false, + '7.4' => true, + ), + 'SO_PEERLABEL' => array( + '7.3' => false, + '7.4' => true, + ), + 'SO_LISTENQLIMIT' => array( + '7.3' => false, + '7.4' => true, + ), + 'SO_LISTENQLEN' => array( + '7.3' => false, + '7.4' => true, + ), + 'SO_USER_COOKIE' => array( + '7.3' => false, + '7.4' => true, + ), + 'PASSWORD_ARGON2_PROVIDER' => array( + '7.3' => false, + '7.4' => true, + ), + 'PHP_WINDOWS_EVENT_CTRL_C' => array( + '7.3' => false, + '7.4' => true, + ), + 'PHP_WINDOWS_EVENT_CTRL_BREAK' => array( + '7.3' => false, + '7.4' => true, + ), + 'T_BAD_CHARACTER' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_ARTICLE' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_ASIDE' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_AUDIO' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_BDI' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_CANVAS' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_COMMAND' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_DATALIST' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_DETAILS' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_DIALOG' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_FIGCAPTION' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_FIGURE' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_FOOTER' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_HEADER' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_HGROUP' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_MAIN' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_MARK' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_MENUITEM' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_METER' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_NAV' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_OUTPUT' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_PROGRESS' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_SECTION' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_SOURCE' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_SUMMARY' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_TEMPLATE' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_TIME' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_TRACK' => array( + '7.3' => false, + '7.4' => true, + ), + 'TIDY_TAG_VIDEO' => array( + '7.3' => false, + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 8.1.0 + * * @return array */ public function register() @@ -3460,6 +3699,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -3489,6 +3730,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 8.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -3502,6 +3745,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 8.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/Constants/NewMagicClassConstantSniff.php b/PHPCompatibility/Sniffs/Constants/NewMagicClassConstantSniff.php index cb03a24c..ccd58641 100644 --- a/PHPCompatibility/Sniffs/Constants/NewMagicClassConstantSniff.php +++ b/PHPCompatibility/Sniffs/Constants/NewMagicClassConstantSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Constants; @@ -16,16 +15,18 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Constants\NewMagicClassConstantSniff. + * Detect usage of the magic `::class` constant introduced in PHP 5.5. * - * The special ClassName::class constant is available as of PHP 5.5.0, and allows for - * fully qualified class name resolution at compile. + * The special `ClassName::class` constant is available as of PHP 5.5.0, and allows + * for fully qualified class name resolution at compile time. * * PHP version 5.5 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/class_name_scalars + * @link https://www.php.net/manual/en/language.oop5.constants.php#example-186 + * + * @since 7.1.4 + * @since 7.1.5 Removed the incorrect checks against invalid usage of the constant. */ class NewMagicClassConstantSniff extends Sniff { @@ -33,6 +34,8 @@ class NewMagicClassConstantSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.4 + * * @return array */ public function register() @@ -43,6 +46,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Constants/RemovedConstantsSniff.php b/PHPCompatibility/Sniffs/Constants/RemovedConstantsSniff.php index 2d1cd2e0..b6c8f8d7 100644 --- a/PHPCompatibility/Sniffs/Constants/RemovedConstantsSniff.php +++ b/PHPCompatibility/Sniffs/Constants/RemovedConstantsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Constants; @@ -13,11 +14,11 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Constants\RemovedConstantsSniff. + * Detect use of deprecated and/or removed PHP native global constants. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version All + * + * @since 8.1.0 */ class RemovedConstantsSniff extends AbstractRemovedFeatureSniff { @@ -28,9 +29,14 @@ class RemovedConstantsSniff extends AbstractRemovedFeatureSniff * The array lists : version number with false (deprecated) or true (removed). * If's sufficient to list the first version where the constant was deprecated/removed. * + * Optional, the array can contain an `alternative` key listing an alternative constant + * to be used instead. + * * Note: PHP Constants are case-sensitive! * - * @var array(string => array(string => bool|string|null)) + * @since 8.1.0 + * + * @var array(string => array(string => bool|string)) */ protected $removedConstants = array( // Disabled since PHP 5.3.0 due to thread safety issues. @@ -60,6 +66,12 @@ class RemovedConstantsSniff extends AbstractRemovedFeatureSniff 'PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' => array( '7.0' => true, ), + 'T_CHARACTER' => array( + '7.0' => true, + ), + 'T_BAD_CHARACTER' => array( + '7.0' => true, + ), 'INTL_IDNA_VARIANT_2003' => array( '7.2' => false, @@ -292,12 +304,208 @@ class RemovedConstantsSniff extends AbstractRemovedFeatureSniff 'FILTER_FLAG_HOST_REQUIRED' => array( '7.3' => false, ), + + 'CURLPIPE_HTTP1' => array( + '7.4' => false, + ), + 'FILTER_SANITIZE_MAGIC_QUOTES' => array( + '7.4' => false, + 'alternative' => 'FILTER_SANITIZE_ADD_SLASHES', + ), + 'IBASE_BKP_CONVERT' => array( + '7.4' => true, + ), + 'IBASE_BKP_IGNORE_CHECKSUMS' => array( + '7.4' => true, + ), + 'IBASE_BKP_IGNORE_LIMBO' => array( + '7.4' => true, + ), + 'IBASE_BKP_METADATA_ONLY' => array( + '7.4' => true, + ), + 'IBASE_BKP_NO_GARBAGE_COLLECT' => array( + '7.4' => true, + ), + 'IBASE_BKP_NON_TRANSPORTABLE' => array( + '7.4' => true, + ), + 'IBASE_BKP_OLD_DESCRIPTIONS' => array( + '7.4' => true, + ), + 'IBASE_COMMITTED' => array( + '7.4' => true, + ), + 'IBASE_CONCURRENCY' => array( + '7.4' => true, + ), + 'IBASE_CONSISTENCY' => array( + '7.4' => true, + ), + 'IBASE_DEFAULT' => array( + '7.4' => true, + ), + 'IBASE_FETCH_ARRAYS' => array( + '7.4' => true, + ), + 'IBASE_FETCH_BLOBS' => array( + '7.4' => true, + ), + 'IBASE_NOWAIT' => array( + '7.4' => true, + ), + 'IBASE_PRP_ACCESS_MODE' => array( + '7.4' => true, + ), + 'IBASE_PRP_ACTIVATE' => array( + '7.4' => true, + ), + 'IBASE_PRP_AM_READONLY' => array( + '7.4' => true, + ), + 'IBASE_PRP_AM_READWRITE' => array( + '7.4' => true, + ), + 'IBASE_PRP_DENY_NEW_ATTACHMENTS' => array( + '7.4' => true, + ), + 'IBASE_PRP_DENY_NEW_TRANSACTIONS' => array( + '7.4' => true, + ), + 'IBASE_PRP_DB_ONLINE' => array( + '7.4' => true, + ), + 'IBASE_PRP_PAGE_BUFFERS' => array( + '7.4' => true, + ), + 'IBASE_PRP_RES' => array( + '7.4' => true, + ), + 'IBASE_PRP_RES_USE_FULL' => array( + '7.4' => true, + ), + 'IBASE_PRP_RESERVE_SPACE' => array( + '7.4' => true, + ), + 'IBASE_PRP_SET_SQL_DIALECT' => array( + '7.4' => true, + ), + 'IBASE_PRP_SHUTDOWN_DB' => array( + '7.4' => true, + ), + 'IBASE_PRP_SWEEP_INTERVAL' => array( + '7.4' => true, + ), + 'IBASE_PRP_WM_ASYNC' => array( + '7.4' => true, + ), + 'IBASE_PRP_WM_SYNC' => array( + '7.4' => true, + ), + 'IBASE_PRP_WRITE_MODE' => array( + '7.4' => true, + ), + 'IBASE_READ' => array( + '7.4' => true, + ), + 'IBASE_RES_CREATE' => array( + '7.4' => true, + ), + 'IBASE_RES_DEACTIVATE_IDX' => array( + '7.4' => true, + ), + 'IBASE_RES_NO_SHADOW' => array( + '7.4' => true, + ), + 'IBASE_RES_NO_VALIDITY' => array( + '7.4' => true, + ), + 'IBASE_RES_ONE_AT_A_TIME' => array( + '7.4' => true, + ), + 'IBASE_RES_REPLACE' => array( + '7.4' => true, + ), + 'IBASE_RES_USE_ALL_SPACE' => array( + '7.4' => true, + ), + 'IBASE_RPR_CHECK_DB' => array( + '7.4' => true, + ), + 'IBASE_RPR_FULL' => array( + '7.4' => true, + ), + 'IBASE_RPR_IGNORE_CHECKSUM' => array( + '7.4' => true, + ), + 'IBASE_RPR_KILL_SHADOWS' => array( + '7.4' => true, + ), + 'IBASE_RPR_MEND_DB' => array( + '7.4' => true, + ), + 'IBASE_RPR_SWEEP_DB' => array( + '7.4' => true, + ), + 'IBASE_RPR_VALIDATE_DB' => array( + '7.4' => true, + ), + 'IBASE_STS_DATA_PAGES' => array( + '7.4' => true, + ), + 'IBASE_STS_DB_LOG' => array( + '7.4' => true, + ), + 'IBASE_STS_HDR_PAGES' => array( + '7.4' => true, + ), + 'IBASE_STS_IDX_PAGES' => array( + '7.4' => true, + ), + 'IBASE_STS_SYS_RELATIONS' => array( + '7.4' => true, + ), + 'IBASE_SVC_GET_ENV' => array( + '7.4' => true, + ), + 'IBASE_SVC_GET_ENV_LOCK' => array( + '7.4' => true, + ), + 'IBASE_SVC_GET_ENV_MSG' => array( + '7.4' => true, + ), + 'IBASE_SVC_GET_USERS' => array( + '7.4' => true, + ), + 'IBASE_SVC_IMPLEMENTATION' => array( + '7.4' => true, + ), + 'IBASE_SVC_SERVER_VERSION' => array( + '7.4' => true, + ), + 'IBASE_SVC_SVR_DB_INFO' => array( + '7.4' => true, + ), + 'IBASE_SVC_USER_DBPATH' => array( + '7.4' => true, + ), + 'IBASE_UNIXTIME' => array( + '7.4' => true, + ), + 'IBASE_WAIT' => array( + '7.4' => true, + ), + 'IBASE_WRITE' => array( + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 8.1.0 + * * @return array */ public function register() @@ -309,6 +517,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -338,6 +548,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 8.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -351,6 +563,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 8.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/ControlStructures/DiscouragedSwitchContinueSniff.php b/PHPCompatibility/Sniffs/ControlStructures/DiscouragedSwitchContinueSniff.php index 23f0f5c1..8d56b93a 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/DiscouragedSwitchContinueSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/DiscouragedSwitchContinueSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -16,15 +15,21 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\ControlStructures\DiscouragedSwitchContinue. + * Detect use of `continue` in `switch` control structures. * - * PHP 7.3 will throw a warning when continue is used to target a switch control structure. + * As of PHP 7.3, PHP will throw a warning when `continue` is used to target a `switch` + * control structure. + * The sniff takes numeric arguments used with `continue` into account. * * PHP version 7.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration73.incompatible.php#migration73.incompatible.core.continue-targeting-switch + * @link https://wiki.php.net/rfc/continue_on_switch_deprecation + * @link https://github.com/php/php-src/commit/04e3523b7d095341f65ed5e71a3cac82fca690e4 + * (actual implementation which is different from the RFC). + * @link https://www.php.net/manual/en/control-structures.switch.php + * + * @since 8.2.0 */ class DiscouragedSwitchContinueSniff extends Sniff { @@ -32,6 +37,8 @@ class DiscouragedSwitchContinueSniff extends Sniff /** * Token codes of control structures which can be targeted using continue. * + * @since 8.2.0 + * * @var array */ protected $loopStructures = array( @@ -45,6 +52,8 @@ class DiscouragedSwitchContinueSniff extends Sniff /** * Tokens which start a new case within a switch. * + * @since 8.2.0 + * * @var array */ protected $caseTokens = array( @@ -57,6 +66,8 @@ class DiscouragedSwitchContinueSniff extends Sniff * * This array is enriched with the arithmetic operators in the register() method. * + * @since 8.2.0 + * * @var array */ protected $acceptedLevelTokens = array( @@ -69,6 +80,8 @@ class DiscouragedSwitchContinueSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -82,6 +95,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueOutsideLoopSniff.php b/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueOutsideLoopSniff.php index 172558fa..2cfbcc53 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueOutsideLoopSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueOutsideLoopSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -15,15 +14,15 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ControlStructures\ForbiddenBreakContinueOutsideLoop. - * - * Forbids use of break or continue statements outside of looping structures. + * Detect using `break` and/or `continue` statements outside of a looping structure. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.break-continue + * @link https://www.php.net/manual/en/control-structures.break.php + * @link https://www.php.net/manual/en/control-structures.continue.php + * + * @since 7.0.7 */ class ForbiddenBreakContinueOutsideLoopSniff extends Sniff { @@ -31,6 +30,8 @@ class ForbiddenBreakContinueOutsideLoopSniff extends Sniff /** * Token codes of control structure in which usage of break/continue is valid. * + * @since 7.0.7 + * * @var array */ protected $validLoopStructures = array( @@ -44,6 +45,8 @@ class ForbiddenBreakContinueOutsideLoopSniff extends Sniff /** * Token codes which did not correctly get a condition assigned in older PHPCS versions. * + * @since 7.0.7 + * * @var array */ protected $backCompat = array( @@ -54,6 +57,8 @@ class ForbiddenBreakContinueOutsideLoopSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.7 + * * @return array */ public function register() @@ -67,6 +72,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueVariableArgumentsSniff.php b/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueVariableArgumentsSniff.php index b7170578..f63c8488 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueVariableArgumentsSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/ForbiddenBreakContinueVariableArgumentsSniff.php @@ -1,13 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -17,16 +15,20 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\ControlStructures\ForbiddenBreakContinueVariableArguments. + * Detects using 0 and variable numeric arguments on `break` and `continue` statements. * - * Forbids variable arguments on break or continue statements. + * This sniff checks for: + * - Using `break` and/or `continue` with a variable as the numeric argument. + * - Using `break` and/or `continue` with a zero - 0 - as the numeric argument. * * PHP version 5.4 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * @link https://www.php.net/manual/en/migration54.incompatible.php + * @link https://www.php.net/manual/en/control-structures.break.php + * @link https://www.php.net/manual/en/control-structures.continue.php + * + * @since 5.5 + * @since 5.6 Now extends the base `Sniff` class. */ class ForbiddenBreakContinueVariableArgumentsSniff extends Sniff { @@ -35,6 +37,9 @@ class ForbiddenBreakContinueVariableArgumentsSniff extends Sniff * * Array key is the error code. Array value will be used as part of the error message. * + * @since 7.0.5 + * @since 7.1.0 Changed from class constants to property. + * * @var array */ private $errorTypes = array( @@ -45,6 +50,8 @@ class ForbiddenBreakContinueVariableArgumentsSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -55,6 +62,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -80,7 +89,7 @@ public function process(File $phpcsFile, $stackPtr) break; } - } elseif (in_array($tokens[$curToken]['type'], array('T_VARIABLE', 'T_FUNCTION', 'T_CLOSURE'), true)) { + } elseif (\in_array($tokens[$curToken]['type'], array('T_VARIABLE', 'T_FUNCTION', 'T_CLOSURE'), true)) { $errorType = 'variableArgument'; break; diff --git a/PHPCompatibility/Sniffs/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksSniff.php b/PHPCompatibility/Sniffs/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksSniff.php index f9c3457a..26f41c07 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -15,15 +14,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ControlStructures\ForbiddenSwitchWithMultipleDefaultBlocksSniff. - * - * Switch statements can not have multiple default blocks since PHP 7.0 + * Switch statements can not have multiple default blocks since PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://wiki.php.net/rfc/switch.default.multiple + * @link https://www.php.net/manual/en/control-structures.switch.php + * + * @since 7.0.0 */ class ForbiddenSwitchWithMultipleDefaultBlocksSniff extends Sniff { @@ -31,6 +29,8 @@ class ForbiddenSwitchWithMultipleDefaultBlocksSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -41,6 +41,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/ControlStructures/NewExecutionDirectivesSniff.php b/PHPCompatibility/Sniffs/ControlStructures/NewExecutionDirectivesSniff.php index c4714e05..0e71a261 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/NewExecutionDirectivesSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/NewExecutionDirectivesSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -15,11 +16,24 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\ControlStructures\NewExecutionDirectivesSniff. + * Check for valid execution directives set with `declare()`. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * The sniff contains three distinct checks: + * - Check if the execution directive used is valid. PHP currently only supports + * three execution directives. + * - Check if the execution directive used is available in the PHP versions + * for which support is being checked. + * In the case of the `encoding` directive on PHP 5.3, support is conditional + * on the `--enable-zend-multibyte` compilation option. This will be indicated as such. + * - Check whether the value for the directive is valid. + * + * PHP version All + * + * @link https://www.php.net/manual/en/control-structures.declare.php + * @link https://wiki.php.net/rfc/scalar_type_hints_v5#strict_types_declare_directive + * + * @since 7.0.3 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class. */ class NewExecutionDirectivesSniff extends AbstractNewFeatureSniff { @@ -31,7 +45,9 @@ class NewExecutionDirectivesSniff extends AbstractNewFeatureSniff * If the execution order is conditional, add the condition as a string to the version nr. * If's sufficient to list the first version where the execution directive appears. * - * @var array(string => array(string => int|string|null)) + * @since 7.0.3 + * + * @var array(string => array(string => bool|string|array)) */ protected $newDirectives = array( 'ticks' => array( @@ -56,6 +72,8 @@ class NewExecutionDirectivesSniff extends AbstractNewFeatureSniff /** * Tokens to ignore when trying to find the value for the directive. * + * @since 7.0.3 + * * @var array */ protected $ignoreTokens = array(); @@ -64,6 +82,8 @@ class NewExecutionDirectivesSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.3 + * * @return array */ public function register() @@ -78,6 +98,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -141,6 +163,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Determine whether an error/warning should be thrown for an item based on collected information. * + * @since 7.1.0 + * * @param array $errorInfo Detail information about an item. * * @return bool @@ -154,6 +178,8 @@ protected function shouldThrowError(array $errorInfo) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -167,6 +193,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -181,6 +209,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -188,7 +218,7 @@ protected function getNonVersionArrayKeys() */ public function getErrorInfo(array $itemArray, array $itemInfo) { - $errorInfo = parent::getErrorInfo($itemArray, $itemInfo); + $errorInfo = parent::getErrorInfo($itemArray, $itemInfo); $errorInfo['conditional_version'] = ''; $errorInfo['condition'] = ''; @@ -196,7 +226,7 @@ public function getErrorInfo(array $itemArray, array $itemInfo) if (empty($versionArray) === false) { foreach ($versionArray as $version => $present) { - if (is_string($present) === true && $this->supportsBelow($version) === true) { + if (\is_string($present) === true && $this->supportsBelow($version) === true) { // We cannot test for compilation option (ok, except by scraping the output of phpinfo...). $errorInfo['conditional_version'] = $version; $errorInfo['condition'] = $present; @@ -211,6 +241,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -222,6 +254,11 @@ protected function getErrorMsgTemplate() /** * Generates the error or warning for this item. * + * @since 7.0.3 + * @since 7.1.0 This method now overloads the method from the `AbstractNewFeatureSniff` class. + * - Renamed from `maybeAddError()` to `addError()`. + * - Changed visibility from `protected` to `public`. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. @@ -252,6 +289,9 @@ public function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $err /** * Generates a error or warning for this sniff. * + * @since 7.0.3 + * @since 7.0.6 Renamed from `addErrorOnInvalidValue()` to `addWarningOnInvalidValue()`. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the execution directive value * in the token array. @@ -270,11 +310,11 @@ protected function addWarningOnInvalidValue(File $phpcsFile, $stackPtr, $directi $isError = false; if (isset($this->newDirectives[$directive]['valid_values'])) { - if (in_array($value, $this->newDirectives[$directive]['valid_values']) === false) { + if (\in_array($value, $this->newDirectives[$directive]['valid_values']) === false) { $isError = true; } } elseif (isset($this->newDirectives[$directive]['valid_value_callback'])) { - $valid = call_user_func(array($this, $this->newDirectives[$directive]['valid_value_callback']), $value); + $valid = \call_user_func(array($this, $this->newDirectives[$directive]['valid_value_callback']), $value); if ($valid === false) { $isError = true; } @@ -298,6 +338,8 @@ protected function addWarningOnInvalidValue(File $phpcsFile, $stackPtr, $directi * * Callback function to test whether the value for an execution directive is valid. * + * @since 7.0.3 + * * @param mixed $value The value to test. * * @return bool @@ -313,6 +355,8 @@ protected function isNumeric($value) * * Callback function to test whether the value for an execution directive is valid. * + * @since 7.0.3 + * * @param mixed $value The value to test. * * @return bool @@ -324,11 +368,11 @@ protected function validEncoding($value) $encodings = mb_list_encodings(); } - if (empty($encodings) || is_array($encodings) === false) { + if (empty($encodings) || \is_array($encodings) === false) { // If we can't test the encoding, let it pass through. return true; } - return in_array($value, $encodings, true); + return \in_array($value, $encodings, true); } } diff --git a/PHPCompatibility/Sniffs/ControlStructures/NewForeachExpressionReferencingSniff.php b/PHPCompatibility/Sniffs/ControlStructures/NewForeachExpressionReferencingSniff.php index 40098420..a2227692 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/NewForeachExpressionReferencingSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/NewForeachExpressionReferencingSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -15,16 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * New `foreach` Expression Referencing. + * Detect `foreach` expression referencing. * - * Before PHP 5.5.0, referencing $value is only possible if the iterated array - * can be referenced (i.e. if it is a variable). + * Before PHP 5.5.0, referencing `$value` in a `foreach` was only possible + * if the iterated array could be referenced (i.e. if it is a variable). * * PHP version 5.5 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/control-structures.foreach.php + * + * @since 9.0.0 */ class NewForeachExpressionReferencingSniff extends Sniff { @@ -32,6 +31,8 @@ class NewForeachExpressionReferencingSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() @@ -42,6 +43,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -79,7 +82,7 @@ public function process(File $phpcsFile, $stackPtr) $nestingLevel = 0; if ($asToken !== ($opener + 1) && isset($tokens[$opener + 1]['nested_parenthesis'])) { - $nestingLevel = count($tokens[$opener + 1]['nested_parenthesis']); + $nestingLevel = \count($tokens[$opener + 1]['nested_parenthesis']); } if ($this->isVariable($phpcsFile, ($opener + 1), $asToken, $nestingLevel) === true) { diff --git a/PHPCompatibility/Sniffs/ControlStructures/NewListInForeachSniff.php b/PHPCompatibility/Sniffs/ControlStructures/NewListInForeachSniff.php index 4a3a9c52..2398cf17 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/NewListInForeachSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/NewListInForeachSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -15,13 +14,15 @@ use PHP_CodeSniffer_File as File; /** - * Detect unpacking nested arrays with list() in a foreach(). + * Detect unpacking nested arrays with `list()` in a `foreach()` as available since PHP 5.5. * * PHP version 5.5 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration55.new-features.php#migration55.new-features.foreach-list + * @link https://wiki.php.net/rfc/foreachlist + * @link https://www.php.net/manual/en/control-structures.foreach.php#control-structures.foreach.list + * + * @since 9.0.0 */ class NewListInForeachSniff extends Sniff { @@ -29,6 +30,8 @@ class NewListInForeachSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() @@ -39,6 +42,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/ControlStructures/NewMultiCatchSniff.php b/PHPCompatibility/Sniffs/ControlStructures/NewMultiCatchSniff.php index a785a0ac..256f37ff 100644 --- a/PHPCompatibility/Sniffs/ControlStructures/NewMultiCatchSniff.php +++ b/PHPCompatibility/Sniffs/ControlStructures/NewMultiCatchSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ControlStructures; @@ -15,21 +14,23 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ControlStructures\NewMultiCatch. - * * Catching multiple exception types in one statement is available since PHP 7.1. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.mulit-catch-exception-handling + * @link https://wiki.php.net/rfc/multiple-catch + * @link https://www.php.net/manual/en/language.exceptions.php#language.exceptions.catch + * + * @since 7.0.7 */ class NewMultiCatchSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.7 + * * @return array */ public function register() @@ -40,6 +41,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Extensions/RemovedExtensionsSniff.php b/PHPCompatibility/Sniffs/Extensions/RemovedExtensionsSniff.php index 20dbd525..13f5dfce 100644 --- a/PHPCompatibility/Sniffs/Extensions/RemovedExtensionsSniff.php +++ b/PHPCompatibility/Sniffs/Extensions/RemovedExtensionsSniff.php @@ -1,11 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Extensions; @@ -15,14 +15,25 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Extensions\RemovedExtensionsSniff. + * Detect the use of deprecated and/or removed PHP extensions. * - * Discourages the use of removed extensions. Suggests alternative extensions if available + * This sniff examines function calls made and flags function calls to functions + * prefixed with the dedicated prefix from a deprecated/removed native PHP extension. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * Suggests alternative extensions if available. + * + * As userland functions may be prefixed with a prefix also used by a native + * PHP extension, the sniff offers the ability to whitelist specific functions + * from being flagged by this sniff via a property in a custom ruleset + * (since PHPCompatibility 7.0.2). + * + * {@internal This sniff is a candidate for removal once all functions from all + * deprecated/removed extensions have been added to the RemovedFunctions sniff.} + * + * PHP version All + * + * @since 5.5 + * @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class. */ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff { @@ -39,17 +50,21 @@ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff * * * + * @since 7.0.2 + * * @var array */ public $functionWhitelist; /** - * A list of removed extensions with their alternative, if any + * A list of removed extensions with their alternative, if any. * * The array lists : version number with false (deprecated) and true (removed). * If's sufficient to list the first version where the extension was deprecated/removed. * - * @var array(string|null) + * @since 5.5 + * + * @var array(string => array(string => bool|string|null)) */ protected $removedExtensions = array( 'activescript' => array( @@ -97,6 +112,10 @@ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff '5.2' => true, 'alternative' => null, ), + 'ibase' => array( + '7.4' => true, + 'alternative' => 'pecl/ibase', + ), 'ingres' => array( '5.1' => true, 'alternative' => 'pecl/ingres', @@ -112,7 +131,7 @@ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff ), 'mcve' => array( '5.1' => true, - 'alternative' => 'pecl/mvce', + 'alternative' => 'pecl/mcve', ), 'ming' => array( '5.3' => true, @@ -147,10 +166,14 @@ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff '5.1' => true, 'alternative' => null, ), - 'pfpro' => array( - '5.3' => true, + 'pfpro_' => array( + '5.1' => true, 'alternative' => null, ), + 'recode' => array( + '7.4' => true, + 'alternative' => 'iconv or mbstring', + ), 'sqlite' => array( '5.4' => true, 'alternative' => null, @@ -168,6 +191,10 @@ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff '5.1' => true, 'alternative' => 'pecl/ffi', ), + 'wddx' => array( + '7.4' => true, + 'alternative' => 'pecl/wddx', + ), 'yp' => array( '5.1' => true, 'alternative' => null, @@ -177,6 +204,8 @@ class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -190,6 +219,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -257,6 +288,8 @@ public function process(File $phpcsFile, $stackPtr) * * Parsing the list late as it may be provided as a property, but also inline. * + * @since 7.0.2 + * * @param string $content Content of the current token. * * @return bool @@ -267,7 +300,7 @@ protected function isWhiteListed($content) return false; } - if (is_string($this->functionWhitelist) === true) { + if (\is_string($this->functionWhitelist) === true) { if (strpos($this->functionWhitelist, ',') !== false) { $this->functionWhitelist = explode(',', $this->functionWhitelist); } else { @@ -275,9 +308,9 @@ protected function isWhiteListed($content) } } - if (is_array($this->functionWhitelist) === true) { + if (\is_array($this->functionWhitelist) === true) { $this->functionWhitelist = array_map('strtolower', $this->functionWhitelist); - return in_array($content, $this->functionWhitelist, true); + return \in_array($content, $this->functionWhitelist, true); } return false; @@ -287,6 +320,8 @@ protected function isWhiteListed($content) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -300,6 +335,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsSniff.php index ada0d213..0f52af71 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsSniff.php @@ -1,13 +1,11 @@ - * @copyright 2015 Declan Kelly + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -17,18 +15,15 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\ForbiddenParameterShadowSuperGlobalsSniff + * Detect the use of superglobals as parameters for functions, support for which was removed in PHP 5.4. * - * Discourages use of superglobals as parameters for functions. - * - * {@internal List of superglobals is maintained in the parent class.}} + * {@internal List of superglobals is maintained in the parent class.} * * PHP version 5.4 * - * @category PHP - * @package PHPCompatibility - * @author Declan Kelly - * @copyright 2015 Declan Kelly + * @link https://www.php.net/manual/en/migration54.incompatible.php + * + * @since 7.0.0 */ class ForbiddenParameterShadowSuperGlobalsSniff extends Sniff { @@ -36,6 +31,9 @@ class ForbiddenParameterShadowSuperGlobalsSniff extends Sniff /** * Register the tokens to listen for. * + * @since 7.0.0 + * @since 7.1.3 Allows for closures. + * * @return array */ public function register() @@ -49,6 +47,8 @@ public function register() /** * Processes the test. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token. * @@ -62,7 +62,7 @@ public function process(File $phpcsFile, $stackPtr) // Get all parameters from function signature. $parameters = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr); - if (empty($parameters) || is_array($parameters) === false) { + if (empty($parameters) || \is_array($parameters) === false) { return; } diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParametersWithSameNameSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParametersWithSameNameSniff.php index d94f36f0..e94461ca 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParametersWithSameNameSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenParametersWithSameNameSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -16,15 +15,13 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\ForbiddenParametersWithSameName. - * - * Functions can not have multiple parameters with the same name since PHP 7.0 + * Functions can not have multiple parameters with the same name since PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.func-parameters + * + * @since 7.0.0 */ class ForbiddenParametersWithSameNameSniff extends Sniff { @@ -32,6 +29,9 @@ class ForbiddenParametersWithSameNameSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * @since 7.1.3 Allows for closures. + * * @return array */ public function register() @@ -45,6 +45,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -66,16 +68,16 @@ public function process(File $phpcsFile, $stackPtr) // Get all parameters from method signature. $parameters = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr); - if (empty($parameters) || is_array($parameters) === false) { + if (empty($parameters) || \is_array($parameters) === false) { return; } $paramNames = array(); foreach ($parameters as $param) { - $paramNames[] = strtolower($param['name']); + $paramNames[] = $param['name']; } - if (count($paramNames) !== count(array_unique($paramNames))) { + if (\count($paramNames) !== \count(array_unique($paramNames))) { $phpcsFile->addError( 'Functions can not have multiple parameters with the same name since PHP 7.0', $stackPtr, diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenToStringParametersSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenToStringParametersSniff.php new file mode 100644 index 00000000..1625a4fa --- /dev/null +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenToStringParametersSniff.php @@ -0,0 +1,99 @@ + true, + 'T_INTERFACE' => true, + 'T_TRAIT' => true, + 'T_ANON_CLASS' => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + return array(\T_FUNCTION); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('5.3') === false) { + return; + } + + $functionName = $phpcsFile->getDeclarationName($stackPtr); + if (strtolower($functionName) !== '__tostring') { + // Not the right function. + return; + } + + if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) { + // Function, not method. + return; + } + + $params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr); + if (empty($params)) { + // Function declared without parameters. + return; + } + + $phpcsFile->addError( + 'The __toString() magic method can no longer accept arguments since PHP 5.3', + $stackPtr, + 'Declared' + ); + } +} diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenVariableNamesInClosureUseSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenVariableNamesInClosureUseSniff.php index b17fc1b9..2e72c05e 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenVariableNamesInClosureUseSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/ForbiddenVariableNamesInClosureUseSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -17,16 +16,17 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * PHP 7.1 Forbidden variable names in closure use statements. + * Detect variable names forbidden to be used in closure `use` statements. * - * Variables bound to a closure via the use construct cannot use the same name - * as any superglobals, $this, or any parameter since PHP 7.1. + * Variables bound to a closure via the `use` construct cannot use the same name + * as any superglobals, `$this`, or any parameter since PHP 7.1. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration71.incompatible.php#migration71.incompatible.lexical-names + * @link https://www.php.net/manual/en/functions.anonymous.php + * + * @since 7.1.4 */ class ForbiddenVariableNamesInClosureUseSniff extends Sniff { @@ -34,6 +34,8 @@ class ForbiddenVariableNamesInClosureUseSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.4 + * * @return array */ public function register() @@ -44,6 +46,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/NewClosureSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/NewClosureSniff.php index 63bca97e..b71bb320 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/NewClosureSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/NewClosureSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -16,21 +15,35 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\NewClosure. + * Detect closures and verify that the features used are supported. + * + * Version based checks: + * - Closures are available since PHP 5.3. + * - Closures can be declared as `static` since PHP 5.4. + * - Closures can use the `$this` variable within a class context since PHP 5.4. + * - Closures can use `self`/`parent`/`static` since PHP 5.4. * - * Closures are available since PHP 5.3 + * Version independent checks: + * - Static closures don't have access to the `$this` variable. + * - Closures declared outside of a class context don't have access to the `$this` + * variable unless bound to an object. * * PHP version 5.3 + * PHP version 5.4 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/functions.anonymous.php + * @link https://wiki.php.net/rfc/closures + * @link https://wiki.php.net/rfc/closures/object-extension + * + * @since 7.0.0 */ class NewClosureSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -41,11 +54,19 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * @since 7.1.4 - Added check for closure being declared as static < 5.4. + * - Added check for use of `$this` variable in class context < 5.4. + * - Added check for use of `$this` variable in static closures (unsupported). + * - Added check for use of `$this` variable outside class context (unsupported). + * @since 8.2.0 Added check for use of `self`/`static`/`parent` < 5.4. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. * - * @return void + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. */ public function process(File $phpcsFile, $stackPtr) { @@ -161,6 +182,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Check whether the closure is declared as static. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -179,6 +202,8 @@ protected function isClosureStatic(File $phpcsFile, $stackPtr) /** * Check if the code within a closure uses the $this variable. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $startToken The position within the closure to continue searching from. * @param int $endToken The closure scope closer to stop searching at. @@ -205,6 +230,8 @@ protected function findThisUsageInClosure(File $phpcsFile, $startToken, $endToke /** * Check if the code within a closure uses "self/parent/static". * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $startToken The position within the closure to continue searching from. * @param int $endToken The closure scope closer to stop searching at. diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/NewExceptionsFromToStringSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/NewExceptionsFromToStringSniff.php new file mode 100644 index 00000000..05648961 --- /dev/null +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/NewExceptionsFromToStringSniff.php @@ -0,0 +1,171 @@ + true, + 'T_TRAIT' => true, + 'T_ANON_CLASS' => true, + ); + + /** + * Tokens which should be ignored when they preface a function declaration + * when trying to find the docblock (if any). + * + * Array will be added to in the register() method. + * + * @since 9.3.0 + * + * @var array + */ + private $docblockIgnoreTokens = array( + \T_WHITESPACE => \T_WHITESPACE, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + // Enhance the array of tokens to ignore for finding the docblock. + $this->docblockIgnoreTokens += Tokens::$methodPrefixes; + if (isset(Tokens::$phpcsCommentTokens)) { + $this->docblockIgnoreTokens += Tokens::$phpcsCommentTokens; + } + + return array(\T_FUNCTION); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsBelow('7.3') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) { + // Abstract function, interface function, live coding or parse error. + return; + } + + $functionName = $phpcsFile->getDeclarationName($stackPtr); + if (strtolower($functionName) !== '__tostring') { + // Not the right function. + return; + } + + if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) { + // Function, not method. + return; + } + + /* + * Examine the content of the function. + */ + $error = 'Throwing exceptions from __toString() was not allowed prior to PHP 7.4'; + $throwPtr = $tokens[$stackPtr]['scope_opener']; + $errorThrown = false; + + do { + $throwPtr = $phpcsFile->findNext(\T_THROW, ($throwPtr + 1), $tokens[$stackPtr]['scope_closer']); + if ($throwPtr === false) { + break; + } + + $conditions = $tokens[$throwPtr]['conditions']; + $conditions = array_reverse($conditions, true); + $inTryCatch = false; + foreach ($conditions as $ptr => $type) { + if ($type === \T_TRY) { + $inTryCatch = true; + break; + } + + if ($ptr === $stackPtr) { + // Don't check the conditions outside the function scope. + break; + } + } + + if ($inTryCatch === false) { + $phpcsFile->addError($error, $throwPtr, 'Found'); + $errorThrown = true; + } + } while (true); + + if ($errorThrown === true) { + // We've already thrown an error for this method, no need to examine the docblock. + return; + } + + /* + * Check whether the function has a docblock and if so, whether it contains a @throws tag. + * + * {@internal This can be partially replaced by the findCommentAboveFunction() + * utility function in due time.} + */ + $commentEnd = $phpcsFile->findPrevious($this->docblockIgnoreTokens, ($stackPtr - 1), null, true); + if ($commentEnd === false || $tokens[$commentEnd]['code'] !== \T_DOC_COMMENT_CLOSE_TAG) { + return; + } + + $commentStart = $tokens[$commentEnd]['comment_opener']; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] !== '@throws') { + continue; + } + + // Found a throws tag. + $phpcsFile->addError($error, $stackPtr, 'ThrowsTagFoundInDocblock'); + break; + } + } +} diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/NewNullableTypesSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/NewNullableTypesSniff.php index 96284a4b..8dc0c416 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/NewNullableTypesSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/NewNullableTypesSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -17,15 +16,15 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\NewNullableTypes. - * - * Nullable type hints and return types are available since PHP 7.1. + * Nullable parameter type declarations and return types are available since PHP 7.1. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.nullable-types + * @link https://wiki.php.net/rfc/nullable_types + * @link https://www.php.net/manual/en/functions.arguments.php#example-146 + * + * @since 7.0.7 */ class NewNullableTypesSniff extends Sniff { @@ -34,7 +33,9 @@ class NewNullableTypesSniff extends Sniff * * {@internal Not sniffing for T_NULLABLE which was introduced in PHPCS 2.7.2 * as in that case we can't distinguish between parameter type hints and - * return type hints for the error message.}} + * return type hints for the error message.} + * + * @since 7.0.7 * * @return array */ @@ -45,7 +46,7 @@ public function register() \T_CLOSURE, ); - if (defined('T_RETURN_TYPE')) { + if (\defined('T_RETURN_TYPE')) { $tokens[] = \T_RETURN_TYPE; } @@ -56,6 +57,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -89,6 +92,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Process this test for function tokens. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -99,7 +104,7 @@ protected function processFunctionDeclaration(File $phpcsFile, $stackPtr) { $params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr); - if (empty($params) === false && is_array($params)) { + if (empty($params) === false && \is_array($params)) { foreach ($params as $param) { if ($param['nullable_type'] === true) { $phpcsFile->addError( @@ -117,6 +122,8 @@ protected function processFunctionDeclaration(File $phpcsFile, $stackPtr) /** * Process this test for return type tokens. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -149,8 +156,8 @@ protected function processReturnType(File $phpcsFile, $stackPtr) } // T_NULLABLE token was introduced in PHPCS 2.7.2. Before that it identified as T_INLINE_THEN. - if ((defined('T_NULLABLE') === true && $tokens[$previous]['type'] === 'T_NULLABLE') - || (defined('T_NULLABLE') === false && $tokens[$previous]['code'] === \T_INLINE_THEN) + if ((\defined('T_NULLABLE') === true && $tokens[$previous]['type'] === 'T_NULLABLE') + || (\defined('T_NULLABLE') === false && $tokens[$previous]['code'] === \T_INLINE_THEN) ) { $phpcsFile->addError( 'Nullable return types are not supported in PHP 7.0 or earlier.', diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/NewParamTypeDeclarationsSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/NewParamTypeDeclarationsSniff.php index 003c2929..6ad98b6a 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/NewParamTypeDeclarationsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/NewParamTypeDeclarationsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -14,11 +15,33 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\NewParamTypeDeclarationsSniff. + * Detect and verify the use of parameter type declarations in function declarations. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * Parameter type declarations - class/interface names only - is available since PHP 5.0. + * - Since PHP 5.1, the `array` keyword can be used. + * - Since PHP 5.2, `self` and `parent` can be used. Previously, those were interpreted as + * class names. + * - Since PHP 5.4, the `callable` keyword. + * - Since PHP 7.0, scalar type declarations are available. + * - Since PHP 7.1, the `iterable` pseudo-type is available. + * - Since PHP 7.2, the generic `object` type is available. + * + * Additionally, this sniff does a cursory check for typical invalid type declarations, + * such as: + * - `boolean` (should be `bool`), `integer` (should be `int`) and `static`. + * - `self`/`parent` as type declaration used outside class context throws a fatal error since PHP 7.0. + * + * PHP version 5.0+ + * + * @link https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration + * @link https://wiki.php.net/rfc/callable + * @link https://wiki.php.net/rfc/scalar_type_hints_v5 + * @link https://wiki.php.net/rfc/iterable + * @link https://wiki.php.net/rfc/object-typehint + * + * @since 7.0.0 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class. + * @since 9.0.0 Renamed from `NewScalarTypeDeclarationsSniff` to `NewParamTypeDeclarationsSniff`. */ class NewParamTypeDeclarationsSniff extends AbstractNewFeatureSniff { @@ -29,7 +52,10 @@ class NewParamTypeDeclarationsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the keyword appears. * - * @var array(string => array(string => int|string|null)) + * @since 7.0.0 + * @since 7.0.3 Now lists all param type declarations, not just the PHP 7+ scalar ones. + * + * @var array(string => array(string => bool)) */ protected $newTypes = array( 'array' => array( @@ -80,6 +106,8 @@ class NewParamTypeDeclarationsSniff extends AbstractNewFeatureSniff * * The array lists : the invalid type hint => what was probably intended/alternative. * + * @since 7.0.3 + * * @var array(string => string) */ protected $invalidTypes = array( @@ -92,6 +120,9 @@ class NewParamTypeDeclarationsSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * @since 7.1.3 Now also checks closures. + * * @return array */ public function register() @@ -106,6 +137,13 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * @since 7.0.3 - Added check for non-scalar type declarations. + * - Added check for invalid type declarations. + * - Added check for usage of `self` type declaration outside + * class scope. + * @since 8.2.0 Added check for `parent` type declaration outside class scope. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -173,6 +211,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -186,6 +226,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/NewReturnTypeDeclarationsSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/NewReturnTypeDeclarationsSniff.php index 37f6e53b..3e225616 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/NewReturnTypeDeclarationsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/NewReturnTypeDeclarationsSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -15,13 +14,24 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\NewReturnTypeDeclarationsSniff. + * Detect and verify the use of return type declarations in function declarations. + * + * Return type declarations are available since PHP 7.0. + * - Since PHP 7.1, the `iterable` and `void` pseudo-types are available. + * - Since PHP 7.2, the generic `object` type is available. * - * PHP version 7.0 + * PHP version 7.0+ * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.return-type-declarations + * @link https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration + * @link https://wiki.php.net/rfc/return_types + * @link https://wiki.php.net/rfc/iterable + * @link https://wiki.php.net/rfc/void_return_type + * @link https://wiki.php.net/rfc/object-typehint + * + * @since 7.0.0 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class. + * @since 7.1.2 Renamed from `NewScalarReturnTypeDeclarationsSniff` to `NewReturnTypeDeclarationsSniff`. */ class NewReturnTypeDeclarationsSniff extends AbstractNewFeatureSniff { @@ -32,7 +42,9 @@ class NewReturnTypeDeclarationsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the keyword appears. * - * @var array(string => array(string => int|string|null)) + * @since 7.0.0 + * + * @var array(string => array(string => bool)) */ protected $newTypes = array( 'int' => array( @@ -91,6 +103,9 @@ class NewReturnTypeDeclarationsSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * @since 7.1.2 Now also checks based on the function and closure keywords. + * * @return array */ public function register() @@ -100,7 +115,7 @@ public function register() \T_CLOSURE, ); - if (defined('T_RETURN_TYPE')) { + if (\defined('T_RETURN_TYPE')) { $tokens[] = \T_RETURN_TYPE; } @@ -111,6 +126,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -138,7 +155,7 @@ public function process(File $phpcsFile, $stackPtr) } // Handle class name based return types. elseif ($tokens[$stackPtr]['code'] === \T_STRING - || (defined('T_RETURN_TYPE') && $tokens[$stackPtr]['code'] === \T_RETURN_TYPE) + || (\defined('T_RETURN_TYPE') && $tokens[$stackPtr]['code'] === \T_RETURN_TYPE) ) { $itemInfo = array( 'name' => 'Class name', @@ -151,6 +168,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -164,6 +183,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/FunctionDeclarations/NonStaticMagicMethodsSniff.php b/PHPCompatibility/Sniffs/FunctionDeclarations/NonStaticMagicMethodsSniff.php index 0a361fb3..a86d56ef 100644 --- a/PHPCompatibility/Sniffs/FunctionDeclarations/NonStaticMagicMethodsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionDeclarations/NonStaticMagicMethodsSniff.php @@ -1,13 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionDeclarations; @@ -16,14 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionDeclarations\NonStaticMagicMethodsSniff. - * * Verifies the use of the correct visibility and static properties of magic methods. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * The requirements have always existed, but as of PHP 5.3, a warning will be thrown + * when magic methods have the wrong modifiers. + * + * PHP version 5.3 + * + * @link https://www.php.net/manual/en/language.oop5.magic.php + * + * @since 5.5 + * @since 5.6 Now extends the base `Sniff` class. */ class NonStaticMagicMethodsSniff extends Sniff { @@ -37,9 +38,23 @@ class NonStaticMagicMethodsSniff extends Sniff * When a method does not have a specific requirement for either visibility or static, * do *not* add the key. * + * @since 5.5 + * @since 5.6 The array format has changed to allow the sniff to also verify the + * use of the correct visibility for a magic method. + * * @var array(string) */ protected $magicMethods = array( + '__construct' => array( + 'static' => false, + ), + '__destruct' => array( + 'visibility' => 'public', + 'static' => false, + ), + '__clone' => array( + 'static' => false, + ), '__get' => array( 'visibility' => 'public', 'static' => false, @@ -71,14 +86,35 @@ class NonStaticMagicMethodsSniff extends Sniff 'visibility' => 'public', ), '__set_state' => array( + 'visibility' => 'public', 'static' => true, ), + '__debuginfo' => array( + 'visibility' => 'public', + 'static' => false, + ), + '__invoke' => array( + 'visibility' => 'public', + 'static' => false, + ), + '__serialize' => array( + 'visibility' => 'public', + 'static' => false, + ), + '__unserialize' => array( + 'visibility' => 'public', + 'static' => false, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * @since 5.6 Now also checks traits. + * @since 7.1.4 Now also checks anonymous classes. + * * @return array */ public function register() @@ -89,7 +125,7 @@ public function register() \T_TRAIT, ); - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $targets[] = \T_ANON_CLASS; } @@ -100,6 +136,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/FunctionNameRestrictions/NewMagicMethodsSniff.php b/PHPCompatibility/Sniffs/FunctionNameRestrictions/NewMagicMethodsSniff.php index 5f3d6db9..4dc08f88 100644 --- a/PHPCompatibility/Sniffs/FunctionNameRestrictions/NewMagicMethodsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionNameRestrictions/NewMagicMethodsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionNameRestrictions; @@ -13,13 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionNameRestrictions\NewMagicMethodsSniff. - * * Warns for non-magic behaviour of magic methods prior to becoming magic. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version 5.0+ + * + * @link https://www.php.net/manual/en/language.oop5.magic.php + * @link https://wiki.php.net/rfc/closures#additional_goodyinvoke + * @link https://wiki.php.net/rfc/debug-info + * + * @since 7.0.4 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class. */ class NewMagicMethodsSniff extends AbstractNewFeatureSniff { @@ -31,9 +35,19 @@ class NewMagicMethodsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not magic) or true (magic). * If's sufficient to list the first version where the method became magic. * - * @var array(string => array(string => int|string|null)) + * @since 7.0.4 + * + * @var array(string => array(string => bool|string)) */ protected $newMagicMethods = array( + '__construct' => array( + '4.4' => false, + '5.0' => true, + ), + '__destruct' => array( + '4.4' => false, + '5.0' => true, + ), '__get' => array( '4.4' => false, '5.0' => true, @@ -73,12 +87,23 @@ class NewMagicMethodsSniff extends AbstractNewFeatureSniff '5.2' => true, 'message' => 'The method %s() was not truly magical in PHP version %s and earlier. The associated magic functionality will only be called when directly combined with echo or print.', ), + + '__serialize' => array( + '7.3' => false, + '7.4' => true, + ), + '__unserialize' => array( + '7.3' => false, + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.4 + * * @return array */ public function register() @@ -90,6 +115,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -120,6 +147,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -133,6 +162,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -144,6 +175,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -166,6 +199,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -177,6 +212,8 @@ protected function getErrorMsgTemplate() /** * Allow for concrete child classes to filter the error message before it's passed to PHPCS. * + * @since 7.1.0 + * * @param string $error The error message which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedMagicAutoloadSniff.php b/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedMagicAutoloadSniff.php index 3a1384cf..0ea5b0d6 100644 --- a/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedMagicAutoloadSniff.php +++ b/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedMagicAutoloadSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionNameRestrictions; @@ -15,16 +14,25 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedMagicAutoloadSniff. + * Detect declaration of the magic `__autoload()` method. + * + * This method has been deprecated in PHP 7.2 in favour of `spl_autoload_register()`. + * + * PHP version 7.2 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/migration72.deprecated.php#migration72.deprecated.__autoload-method + * @link https://wiki.php.net/rfc/deprecations_php_7_2#autoload + * @link https://www.php.net/manual/en/function.autoload.php + * + * @since 8.1.0 + * @since 9.0.0 Renamed from `DeprecatedMagicAutoloadSniff` to `RemovedMagicAutoloadSniff`. */ class RemovedMagicAutoloadSniff extends Sniff { /** - * Scopes to look for when testing using validDirectScope + * Scopes to look for when testing using validDirectScope. + * + * @since 8.1.0 * * @var array */ @@ -39,6 +47,8 @@ class RemovedMagicAutoloadSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.1.0 + * * @return array */ public function register() @@ -49,6 +59,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedNamespacedAssertSniff.php b/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedNamespacedAssertSniff.php index a81198b1..3c3feadc 100644 --- a/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedNamespacedAssertSniff.php +++ b/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedNamespacedAssertSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionNameRestrictions; @@ -15,24 +14,30 @@ use PHP_CodeSniffer_File as File; /** - * Removed Namespaced Assert. + * Detect declaration of a namespaced function called `assert()`. * * As of PHP 7.3, a compile-time deprecation warning will be thrown when a function * called `assert()` is declared. In PHP 8 this will become a compile-error. * * Methods are unaffected. - * Global, non-namespaced, assert() function declarations were always a fatal + * Global, non-namespaced, `assert()` function declarations were always a fatal * "function already declared" error, so not the concern of this sniff. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version 7.3 + * + * @link https://www.php.net/manual/en/migration73.deprecated.php#migration73.deprecated.core.assert + * @link https://wiki.php.net/rfc/deprecations_php_7_3#defining_a_free-standing_assert_function + * @link https://www.php.net/manual/en/function.assert.php + * + * @since 9.0.0 */ class RemovedNamespacedAssertSniff extends Sniff { /** * Scopes in which an `assert` function can be declared without issue. * + * @since 9.0.0 + * * @var array */ private $scopes = array( @@ -45,12 +50,14 @@ class RemovedNamespacedAssertSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() { // Enrich the scopes list. - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $this->scopes[] = \T_ANON_CLASS; } @@ -60,6 +67,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -83,7 +92,7 @@ public function process(File $phpcsFile, $stackPtr) } if ($this->determineNamespace($phpcsFile, $stackPtr) === '') { - // Not a namespaced function declaration. Parse error, but not our concern. + // Not a namespaced function declaration. This may be a parse error, but not our concern. return; } diff --git a/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedPHP4StyleConstructorsSniff.php b/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedPHP4StyleConstructorsSniff.php index eb00e17e..a888f51d 100644 --- a/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedPHP4StyleConstructorsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionNameRestrictions/RemovedPHP4StyleConstructorsSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionNameRestrictions; @@ -16,13 +15,26 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedPHP4StyleConstructorsSniff. + * Detect declarations of PHP 4 style constructors which are deprecated as of PHP 7.0.0. + * + * PHP 4 style constructors - methods that have the same name as the class they are defined in - + * are deprecated as of PHP 7.0.0, and will be removed in the future. + * PHP 7 will emit `E_DEPRECATED` if a PHP 4 constructor is the only constructor defined + * within a class. Classes that implement a `__construct()` method are unaffected. + * + * Note: Methods with the same name as the class they are defined in _within a namespace_ + * are not recognized as constructors anyway and therefore outside the scope of this sniff. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Koen Eelen + * @link https://www.php.net/manual/en/migration70.deprecated.php#migration70.deprecated.php4-constructors + * @link https://wiki.php.net/rfc/remove_php4_constructors + * @link https://www.php.net/manual/en/language.oop5.decon.php + * + * @since 7.0.0 + * @since 7.0.8 This sniff now throws a warning instead of an error as the functionality is + * only deprecated (for now). + * @since 9.0.0 Renamed from `DeprecatedPHP4StyleConstructorsSniff` to `RemovedPHP4StyleConstructorsSniff`. */ class RemovedPHP4StyleConstructorsSniff extends Sniff { @@ -30,19 +42,24 @@ class RemovedPHP4StyleConstructorsSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() { return array( \T_CLASS, - \T_INTERFACE, ); } /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * @since 7.0.8 The message is downgraded from error to warning as - for now - support + * for PHP4-style constructors is just deprecated, not yet removed. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -83,7 +100,7 @@ public function process(File $phpcsFile, $stackPtr) $scopeCloser = $class['scope_closer']; $className = $tokens[$nextNonEmpty]['content']; - if (empty($className) || is_string($className) === false) { + if (empty($className) || \is_string($className) === false) { return; } @@ -92,7 +109,13 @@ public function process(File $phpcsFile, $stackPtr) $newConstructorFound = false; $oldConstructorFound = false; $oldConstructorPos = -1; - while (($nextFunc = $phpcsFile->findNext(\T_FUNCTION, ($nextFunc + 1), $scopeCloser)) !== false) { + while (($nextFunc = $phpcsFile->findNext(array(\T_FUNCTION, \T_DOC_COMMENT_OPEN_TAG), ($nextFunc + 1), $scopeCloser)) !== false) { + // Skip over docblocks. + if ($tokens[$nextFunc]['code'] === \T_DOC_COMMENT_OPEN_TAG) { + $nextFunc = $tokens[$nextFunc]['comment_closer']; + continue; + } + $functionScopeCloser = $nextFunc; if (isset($tokens[$nextFunc]['scope_closer'])) { // Normal (non-interface, non-abstract) method. @@ -100,7 +123,7 @@ public function process(File $phpcsFile, $stackPtr) } $funcName = $phpcsFile->getDeclarationName($nextFunc); - if (empty($funcName) || is_string($funcName) === false) { + if (empty($funcName) || \is_string($funcName) === false) { $nextFunc = $functionScopeCloser; continue; } diff --git a/PHPCompatibility/Sniffs/FunctionNameRestrictions/ReservedFunctionNamesSniff.php b/PHPCompatibility/Sniffs/FunctionNameRestrictions/ReservedFunctionNamesSniff.php index 821cd4fa..9ddb9e96 100644 --- a/PHPCompatibility/Sniffs/FunctionNameRestrictions/ReservedFunctionNamesSniff.php +++ b/PHPCompatibility/Sniffs/FunctionNameRestrictions/ReservedFunctionNamesSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionNameRestrictions; @@ -15,10 +16,10 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionNameRestrictions\ReservedFunctionNamesSniff. - * * All function and method names starting with double underscore are reserved by PHP. * + * PHP version All + * * {@internal Extends an upstream sniff to benefit from the properties contained therein. * The properties are lists of valid PHP magic function and method names, which * should be ignored for the purposes of this sniff. @@ -28,22 +29,25 @@ * the logic in this sniff is largely the same as used upstream. * Extending the upstream sniff instead of including it via the ruleset, however, * prevents hard to debug issues of errors not being reported from the upstream sniff - * if this library is used in combination with other rulesets.}} + * if this library is used in combination with other rulesets.} * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/language.oop5.magic.php + * + * @since 8.2.0 This was previously, since 7.0.3, checked by the upstream sniff. + * @since 9.3.2 The sniff will now ignore functions marked as `@deprecated` by design. */ class ReservedFunctionNamesSniff extends PHPCS_CamelCapsFunctionNameSniff { /** * Overload the constructor to work round various PHPCS cross-version compatibility issues. + * + * @since 8.2.0 */ public function __construct() { $scopeTokens = array(\T_CLASS, \T_INTERFACE, \T_TRAIT); - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $scopeTokens[] = \T_ANON_CLASS; } @@ -58,6 +62,8 @@ public function __construct() /** * Processes the tokens within the scope. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being processed. * @param int $stackPtr The position where this token was * found. @@ -82,6 +88,15 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop return; } + if ($this->isFunctionDeprecated($phpcsFile, $stackPtr) === true) { + /* + * Deprecated functions don't have to comply with the naming conventions, + * otherwise functions deprecated in favour of a function with a compliant + * name would still trigger an error. + */ + return; + } + $methodName = $phpcsFile->getDeclarationName($stackPtr); if ($methodName === null) { // Ignore closures. @@ -114,6 +129,8 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop /** * Processes the tokens outside the scope. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being processed. * @param int $stackPtr The position where this token was * found. @@ -122,6 +139,15 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop */ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr) { + if ($this->isFunctionDeprecated($phpcsFile, $stackPtr) === true) { + /* + * Deprecated functions don't have to comply with the naming conventions, + * otherwise functions deprecated in favour of a function with a compliant + * name would still trigger an error. + */ + return; + } + $functionName = $phpcsFile->getDeclarationName($stackPtr); if ($functionName === null) { // Ignore closures. @@ -141,4 +167,39 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr) } } } + + + /** + * Check whether a function has been marked as deprecated via a @deprecated tag + * in the function docblock. + * + * @since 9.3.2 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of a T_FUNCTION + * token in the stack. + * + * @return bool + */ + private function isFunctionDeprecated(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $find = Tokens::$methodPrefixes; + $find[] = \T_WHITESPACE; + + $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true); + if ($tokens[$commentEnd]['code'] !== \T_DOC_COMMENT_CLOSE_TAG) { + // Function doesn't have a doc comment or is using the wrong type of comment. + return false; + } + + $commentStart = $tokens[$commentEnd]['comment_opener']; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] === '@deprecated') { + return true; + } + } + + return false; + } } diff --git a/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsReportCurrentValueSniff.php b/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsReportCurrentValueSniff.php index b69626dd..4e904dea 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsReportCurrentValueSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsReportCurrentValueSniff.php @@ -3,7 +3,7 @@ * PHPCompatibility, an external standard for PHP_CodeSniffer. * * @package PHPCompatibility - * @copyright 2012-2018 PHPCompatibility Contributors + * @copyright 2012-2019 PHPCompatibility Contributors * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 * @link https://github.com/PHPCompatibility/PHPCompatibility */ @@ -20,11 +20,13 @@ * instead of the original since PHP 7.0. * * `func_get_arg()`, `func_get_args()`, `debug_backtrace()` and exception backtraces - * will no longer report the original value that was passed to a parameter, but will - * instead provide the current value (which might have been modified). + * will no longer report the original parameter value as was passed to the function, + * but will instead provide the current value (which might have been modified). * * PHP version 7.0 * + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.func-parameter-modified + * * @since 9.1.0 */ class ArgumentFunctionsReportCurrentValueSniff extends Sniff @@ -34,6 +36,8 @@ class ArgumentFunctionsReportCurrentValueSniff extends Sniff * A list of functions that, when called, can behave differently in PHP 7 * when dealing with parameters of the function they're called in. * + * @since 9.1.0 + * * @var array */ protected $changedFunctions = array( @@ -46,6 +50,8 @@ class ArgumentFunctionsReportCurrentValueSniff extends Sniff /** * Tokens to look out for to allow us to skip past nested scoped structures. * + * @since 9.1.0 + * * @var array */ private $skipPastNested = array( @@ -66,6 +72,8 @@ class ArgumentFunctionsReportCurrentValueSniff extends Sniff * Similarly, as constants won't have parentheses, those don't need to be checked * for either. * + * @since 9.1.0 + * * @var array */ private $noneFunctionCallIndicators = array( @@ -76,6 +84,8 @@ class ArgumentFunctionsReportCurrentValueSniff extends Sniff /** * The tokens for variable incrementing/decrementing. * + * @since 9.1.0 + * * @var array */ private $plusPlusMinusMinus = array( @@ -86,6 +96,8 @@ class ArgumentFunctionsReportCurrentValueSniff extends Sniff /** * Tokens to ignore when determining the start of a statement. * + * @since 9.1.0 + * * @var array */ private $ignoreForStartOfStatement = array( @@ -98,6 +110,8 @@ class ArgumentFunctionsReportCurrentValueSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.1.0 + * * @return array */ public function register() @@ -111,6 +125,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -222,7 +238,7 @@ public function process(File $phpcsFile, $stackPtr) * higher than the number of named parameters. * * {@internal Note: This does not take calculations into account! - * Should be exceptionally rare and can - if needs be - be addressed at a later stage.}} + * Should be exceptionally rare and can - if needs be - be addressed at a later stage.} */ case 'func_get_arg': $number = $phpcsFile->findNext(\T_LNUMBER, $paramOne['start'], ($paramOne['end'] + 1)); @@ -244,7 +260,7 @@ public function process(File $phpcsFile, $stackPtr) * In that case, we can ignore it. * * {@internal Note: This does not take offset calculations into account! - * Should be exceptionally rare and can - if needs be - be addressed at a later stage.}} + * Should be exceptionally rare and can - if needs be - be addressed at a later stage.} */ if ($prev !== false && $tokens[$prev]['code'] === \T_OPEN_PARENTHESIS) { @@ -267,7 +283,7 @@ public function process(File $phpcsFile, $stackPtr) } // Slice starts at a named argument, but we know which params are being accessed. - $paramNamesSubset = array_slice($paramNames, $tokens[$number]['content']); + $paramNamesSubset = \array_slice($paramNames, $tokens[$number]['content']); } } } @@ -348,11 +364,11 @@ public function process(File $phpcsFile, $stackPtr) continue; } } elseif ($foundFunctionName === 'func_get_args' && isset($paramNamesSubset)) { - if (in_array($tokens[$j]['content'], $paramNamesSubset, true) === false) { + if (\in_array($tokens[$j]['content'], $paramNamesSubset, true) === false) { // Different param than the ones requested by func_get_args(). continue; } - } elseif (in_array($tokens[$j]['content'], $paramNames, true) === false) { + } elseif (\in_array($tokens[$j]['content'], $paramNames, true) === false) { // Variable is not one of the function parameters. continue; } diff --git a/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsUsageSniff.php b/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsUsageSniff.php index 38f63784..7c251119 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsUsageSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/ArgumentFunctionsUsageSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -16,10 +15,10 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionUse\ArgumentFunctionsUsageSniff. + * Detect usage of `func_get_args()`, `func_get_arg()` and `func_num_args()` in invalid context. * + * Checks for: * - Prior to PHP 5.3, these functions could not be used as a function call parameter. - * * - Calling these functions from the outermost scope of a file which has been included by * calling `include` or `require` from within a function in the calling file, worked * prior to PHP 5.3. As of PHP 5.3, this will generate a warning and will always return false/-1. @@ -28,9 +27,9 @@ * * PHP version 5.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration53.incompatible.php + * + * @since 8.2.0 */ class ArgumentFunctionsUsageSniff extends Sniff { @@ -38,6 +37,8 @@ class ArgumentFunctionsUsageSniff extends Sniff /** * The target functions for this sniff. * + * @since 8.2.0 + * * @var array */ protected $targetFunctions = array( @@ -50,6 +51,8 @@ class ArgumentFunctionsUsageSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -61,6 +64,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -100,7 +105,7 @@ public function process(File $phpcsFile, $stackPtr) $data = $tokens[$stackPtr]['content']; /* - * Check for usage of the functions in the global scope. + * Check for use of the functions in the global scope. * * As PHPCS can not determine whether a file is included from within a function in * another file, so always throw a warning/error. @@ -118,7 +123,7 @@ public function process(File $phpcsFile, $stackPtr) } /* - * Check for usage of the functions as a parameter in a function call. + * Check for use of the functions as a parameter in a function call. */ if ($this->supportsBelow('5.2') === false) { return; diff --git a/PHPCompatibility/Sniffs/FunctionUse/NewFunctionParametersSniff.php b/PHPCompatibility/Sniffs/FunctionUse/NewFunctionParametersSniff.php index ff5f5884..7b0d60bd 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/NewFunctionParametersSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/NewFunctionParametersSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -14,21 +15,27 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionUse\newFunctionParametersSniff. + * Detect use of new function parameters in calls to native PHP functions. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * PHP version All + * + * @link https://www.php.net/manual/en/doc.changelog.php + * + * @since 7.0.0 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. */ class NewFunctionParametersSniff extends AbstractNewFeatureSniff { /** - * A list of new functions, not present in older versions. + * A list of functions which have new parameters, not present in older versions. * * The array lists : version number with false (not present) or true (present). * The index is the location of the parameter in the parameter list, starting at 0 ! * If's sufficient to list the first version where the function appears. * + * @since 7.0.0 + * @since 7.0.2 Visibility changed from `public` to `protected`. + * * @var array */ protected $newFunctionParameters = array( @@ -709,6 +716,18 @@ class NewFunctionParametersSniff extends AbstractNewFeatureSniff '5.0' => false, '5.1' => true, ), + 5 => array( + 'name' => 'flags', + '7.3' => false, + '7.4' => true, + ), + ), + 'preg_replace_callback_array' => array( + 4 => array( + 'name' => 'flags', + '7.3' => false, + '7.4' => true, + ), ), 'round' => array( 2 => array( @@ -925,6 +944,8 @@ class NewFunctionParametersSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -938,6 +959,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -993,6 +1016,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -1006,6 +1031,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -1017,6 +1044,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -1034,6 +1063,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the item name to be used for the creation of the error code. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * @param array $errorInfo Detail information about an item. * @@ -1048,6 +1079,8 @@ protected function getItemName(array $itemInfo, array $errorInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -1059,6 +1092,8 @@ protected function getErrorMsgTemplate() /** * Allow for concrete child classes to filter the error data before it's passed to PHPCS. * + * @since 7.1.0 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/Sniffs/FunctionUse/NewFunctionsSniff.php b/PHPCompatibility/Sniffs/FunctionUse/NewFunctionsSniff.php index 8c3593b3..c1a0ec4e 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/NewFunctionsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/NewFunctionsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -13,11 +14,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionUse\newFunctionsSniff. + * Detect calls to new native PHP functions. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * PHP version All + * + * @since 5.5 + * @since 5.6 Now extends the base `Sniff` class instead of the upstream + * `Generic.PHP.ForbiddenFunctions` sniff. + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. */ class NewFunctionsSniff extends AbstractNewFeatureSniff { @@ -27,7 +31,14 @@ class NewFunctionsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the function appears. * - * @var array(string => array(string => int|string|null)) + * @since 5.5 + * @since 5.6 Visibility changed from `protected` to `public`. + * @since 7.0.2 Visibility changed back from `public` to `protected`. + * The earlier change was made to be in line with the upstream sniff, + * but that sniff is no longer being extended. + * @since 7.0.8 Renamed from `$forbiddenFunctions` to the more descriptive `$newFunctions`. + * + * @var array(string => array(string => bool)) */ protected $newFunctions = array( 'iterator_count' => array( @@ -849,10 +860,6 @@ class NewFunctionsSniff extends AbstractNewFeatureSniff '5.4' => false, '5.5' => true, ), - 'datefmt_get_calendar_object' => array( - '5.4' => false, - '5.5' => true, - ), 'intlcal_create_instance' => array( '5.4' => false, '5.5' => true, @@ -1117,6 +1124,31 @@ class NewFunctionsSniff extends AbstractNewFeatureSniff '5.4' => false, '5.5' => true, ), + 'opcache_compile_file' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache_get_configuration' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache_get_status' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache_invalidate' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache_reset' => array( + '5.4' => false, + '5.5' => true, + ), + + 'opcache_is_script_cached' => array( + '5.5.10' => false, + '5.5.11' => true, + ), 'gmp_root' => array( '5.5' => false, @@ -1746,6 +1778,16 @@ class NewFunctionsSniff extends AbstractNewFeatureSniff '7.1' => false, '7.2' => true, ), + // Introduced in 7.2.14 and 7.3.1 simultanously. + 'oci_set_call_timeout' => array( + '7.2.13' => false, + '7.2.14' => true, + ), + // Introduced in 7.2.14 and 7.3.1 simultanously. + 'oci_set_db_operation' => array( + '7.2.13' => false, + '7.2.14' => true, + ), 'hrtime' => array( '7.2' => false, @@ -1839,12 +1881,47 @@ class NewFunctionsSniff extends AbstractNewFeatureSniff '7.2' => false, '7.3' => true, ), + + 'get_mangled_object_vars' => array( + '7.3' => false, + '7.4' => true, + ), + 'imagecreatefromtga' => array( + '7.3' => false, + '7.4' => true, + ), + 'mb_str_split' => array( + '7.3' => false, + '7.4' => true, + ), + 'openssl_x509_verify' => array( + '7.3' => false, + '7.4' => true, + ), + 'password_algos' => array( + '7.3' => false, + '7.4' => true, + ), + 'pcntl_unshare' => array( + '7.3' => false, + '7.4' => true, + ), + 'sapi_windows_set_ctrl_handler' => array( + '7.3' => false, + '7.4' => true, + ), + 'sapi_windows_generate_ctrl_event' => array( + '7.3' => false, + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 5.6 + * * @return array */ public function register() @@ -1858,6 +1935,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -1903,6 +1982,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -1916,6 +1997,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/FunctionUse/OptionalToRequiredFunctionParametersSniff.php b/PHPCompatibility/Sniffs/FunctionUse/OptionalToRequiredFunctionParametersSniff.php index d884d98c..301e5e07 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/OptionalToRequiredFunctionParametersSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/OptionalToRequiredFunctionParametersSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -13,11 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionUse\OptionalToRequiredFunctionParametersSniff. + * Detect missing required function parameters in calls to native PHP functions. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * Specifically when those function parameters used to be optional in older PHP versions. + * + * PHP version All + * + * @link https://www.php.net/manual/en/doc.changelog.php + * + * @since 8.1.0 + * @since 9.0.0 Renamed from `OptionalRequiredFunctionParametersSniff` to `OptionalToRequiredFunctionParametersSniff`. */ class OptionalToRequiredFunctionParametersSniff extends RequiredToOptionalFunctionParametersSniff { @@ -30,6 +36,8 @@ class OptionalToRequiredFunctionParametersSniff extends RequiredToOptionalFuncti * The index is the location of the parameter in the parameter list, starting at 0 ! * If's sufficient to list the last version in which the parameter was not yet required. * + * @since 8.1.0 + * * @var array */ protected $functionParameters = array( @@ -53,6 +61,8 @@ class OptionalToRequiredFunctionParametersSniff extends RequiredToOptionalFuncti /** * Determine whether an error/warning should be thrown for an item based on collected information. * + * @since 8.1.0 + * * @param array $errorInfo Detail information about an item. * * @return bool @@ -68,6 +78,8 @@ protected function shouldThrowError(array $errorInfo) /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 8.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -109,6 +121,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Generates the error or warning for this item. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. @@ -151,7 +165,7 @@ public function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $err } // Remove the last 'and' from the message. - $error = substr($error, 0, (strlen($error) - 5)); + $error = substr($error, 0, (\strlen($error) - 5)); } $this->addMessage($phpcsFile, $error, $stackPtr, $errorInfo['error'], $errorCode, $data); diff --git a/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionParametersSniff.php b/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionParametersSniff.php index 838544b2..b4cad10b 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionParametersSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionParametersSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -14,11 +15,14 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionParametersSniff. + * Detect use of deprecated/removed function parameters in calls to native PHP functions. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * PHP version All + * + * @link https://www.php.net/manual/en/doc.changelog.php + * + * @since 7.0.0 + * @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class. */ class RemovedFunctionParametersSniff extends AbstractRemovedFeatureSniff { @@ -29,9 +33,24 @@ class RemovedFunctionParametersSniff extends AbstractRemovedFeatureSniff * The index is the location of the parameter in the parameter list, starting at 0 ! * If's sufficient to list the first version where the function parameter was deprecated/removed. * + * The optional `callback` key can be used to pass a method name which should be called for an + * additional check. The method will be passed the parameter info and should return true + * if the notice should be thrown or false otherwise. + * + * @since 7.0.0 + * @since 7.0.2 Visibility changed from `public` to `protected`. + * @since 9.3.0 Optional `callback` key. + * * @var array */ protected $removedFunctionParameters = array( + 'curl_version' => array( + 0 => array( + 'name' => 'age', + '7.4' => false, + 'callback' => 'curlVersionInvalidValue', + ), + ), 'define' => array( 2 => array( 'name' => 'case_insensitive', @@ -70,6 +89,8 @@ class RemovedFunctionParametersSniff extends AbstractRemovedFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -83,6 +104,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -113,7 +136,8 @@ public function process(File $phpcsFile, $stackPtr) return; } - $parameterCount = $this->getFunctionCallParameterCount($phpcsFile, $stackPtr); + $parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr); + $parameterCount = \count($parameters); if ($parameterCount === 0) { return; } @@ -124,6 +148,12 @@ public function process(File $phpcsFile, $stackPtr) foreach ($this->removedFunctionParameters[$functionLc] as $offset => $parameterDetails) { if ($offset <= $parameterOffsetFound) { + if (isset($parameterDetails['callback']) && method_exists($this, $parameterDetails['callback'])) { + if ($this->{$parameterDetails['callback']}($phpcsFile, $parameters[($offset + 1)]) === false) { + continue; + } + } + $itemInfo = array( 'name' => $function, 'nameLc' => $functionLc, @@ -138,6 +168,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -151,17 +183,21 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() { - return array('name'); + return array('name', 'callback'); } /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -179,6 +215,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the item name to be used for the creation of the error code. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * @param array $errorInfo Detail information about an item. * @@ -193,6 +231,8 @@ protected function getItemName(array $itemInfo, array $errorInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -204,6 +244,8 @@ protected function getErrorMsgTemplate() /** * Filter the error data before it's passed to PHPCS. * + * @since 7.1.0 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. @@ -216,4 +258,35 @@ protected function filterErrorData(array $data, array $itemInfo, array $errorInf array_unshift($data, $errorInfo['paramName'], $itemInfo['name']); return $data; } + + /** + * Check whether curl_version() was passed the default CURLVERSION_NOW. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $parameter Parameter info array. + * + * @return bool True if the value was not CURLVERSION_NOW, false otherwise. + */ + protected function curlVersionInvalidValue(File $phpcsFile, array $parameter) + { + $tokens = $phpcsFile->getTokens(); + $raw = ''; + for ($i = $parameter['start']; $i <= $parameter['end']; $i++) { + if (isset(Tokens::$emptyTokens[$tokens[$i]['code']])) { + continue; + } + + $raw .= $tokens[$i]['content']; + } + + if ($raw !== 'CURLVERSION_NOW' + && $raw !== (string) \CURLVERSION_NOW + ) { + return true; + } + + return false; + } } diff --git a/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionsSniff.php b/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionsSniff.php index 3de11a43..33f3d393 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionsSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/RemovedFunctionsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -13,11 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionsSniff. + * Detect calls to deprecated/removed native PHP functions. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * Suggests alternative if available. + * + * PHP version All + * + * @since 5.5 + * @since 5.6 Now extends the base `Sniff` class instead of the upstream + * `Generic.PHP.ForbiddenFunctions` sniff. + * @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class. + * @since 9.0.0 Renamed from `DeprecatedFunctionsSniff` to `RemovedFunctionsSniff`. */ class RemovedFunctionsSniff extends AbstractRemovedFeatureSniff { @@ -27,6 +34,13 @@ class RemovedFunctionsSniff extends AbstractRemovedFeatureSniff * The array lists : version number with false (deprecated) or true (removed) and an alternative function. * If no alternative exists, it is NULL, i.e, the function should just not be used. * + * @since 5.5 + * @since 5.6 Visibility changed from `protected` to `public`. + * @since 7.0.2 Visibility changed back from `public` to `protected`. + * The earlier change was made to be in line with the upstream sniff, + * but that sniff is no longer being extended. + * @since 7.0.8 Property renamed from `$forbiddenFunctions` to `$removedFunctions`. + * * @var array(string => array(string => bool|string|null)) */ protected $removedFunctions = array( @@ -34,65 +48,64 @@ class RemovedFunctionsSniff extends AbstractRemovedFeatureSniff '5.0.5' => true, 'alternative' => null, ), + + 'pfpro_cleanup' => array( + '5.1' => true, + 'alternative' => null, + ), + 'pfpro_init' => array( + '5.1' => true, + 'alternative' => null, + ), + 'pfpro_process_raw' => array( + '5.1' => true, + 'alternative' => null, + ), + 'pfpro_process' => array( + '5.1' => true, + 'alternative' => null, + ), + 'pfpro_version' => array( + '5.1' => true, + 'alternative' => null, + ), + 'call_user_method' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'call_user_func()', ), 'call_user_method_array' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'call_user_func_array()', ), 'define_syslog_variables' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => null, ), 'dl' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => null, ), 'ereg' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'preg_match()', ), 'ereg_replace' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'preg_replace()', ), 'eregi' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'preg_match()', ), 'eregi_replace' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'preg_replace()', ), @@ -135,495 +148,339 @@ class RemovedFunctionsSniff extends AbstractRemovedFeatureSniff ), 'mcrypt_generic_end' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'mcrypt_generic_deinit()', ), 'mysql_db_query' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'mysqli::select_db() and mysqli::query()', ), 'mysql_escape_string' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'mysqli::real_escape_string()', ), 'mysql_list_dbs' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'mysqli_bind_param' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli_stmt::bind_param()', ), 'mysqli_bind_result' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli_stmt::bind_result()', ), 'mysqli_client_encoding' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli::character_set_name()', ), 'mysqli_fetch' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli_stmt::fetch()', ), 'mysqli_param_count' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli_stmt_param_count()', ), 'mysqli_get_metadata' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli_stmt::result_metadata()', ), 'mysqli_send_long_data' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => 'mysqli_stmt::send_long_data()', ), 'magic_quotes_runtime' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'session_register' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => '$_SESSION', ), 'session_unregister' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => '$_SESSION', ), 'session_is_registered' => array( '5.3' => false, '5.4' => true, - '5.5' => true, - '5.6' => true, 'alternative' => '$_SESSION', ), 'set_magic_quotes_runtime' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'set_socket_blocking' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'stream_set_blocking()', ), 'split' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'preg_split()', ), 'spliti' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'preg_split()', ), 'sql_regcase' => array( '5.3' => false, - '5.4' => false, - '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'php_logo_guid' => array( '5.5' => true, - '5.6' => true, 'alternative' => null, ), 'php_egg_logo_guid' => array( '5.5' => true, - '5.6' => true, 'alternative' => null, ), 'php_real_logo_guid' => array( '5.5' => true, - '5.6' => true, 'alternative' => null, ), 'zend_logo_guid' => array( '5.5' => true, - '5.6' => true, 'alternative' => null, ), 'datefmt_set_timezone_id' => array( '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => 'IntlDateFormatter::setTimeZone()', ), 'mcrypt_ecb' => array( '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'mcrypt_cbc' => array( '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'mcrypt_cfb' => array( '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'mcrypt_ofb' => array( '5.5' => false, - '5.6' => false, '7.0' => true, 'alternative' => null, ), 'ocibindbyname' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_bind_by_name()', ), 'ocicancel' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_cancel()', ), 'ocicloselob' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::close()', ), 'ocicollappend' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::append()', ), 'ocicollassign' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::assign()', ), 'ocicollassignelem' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::assignElem()', ), 'ocicollgetelem' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::getElem()', ), 'ocicollmax' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::max()', ), 'ocicollsize' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::size()', ), 'ocicolltrim' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::trim()', ), 'ocicolumnisnull' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_is_null()', ), 'ocicolumnname' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_name()', ), 'ocicolumnprecision' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_precision()', ), 'ocicolumnscale' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_scale()', ), 'ocicolumnsize' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_size()', ), 'ocicolumntype' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_type()', ), 'ocicolumntyperaw' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_field_type_raw()', ), 'ocicommit' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_commit()', ), 'ocidefinebyname' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_define_by_name()', ), 'ocierror' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_error()', ), 'ociexecute' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_execute()', ), 'ocifetch' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_fetch()', ), 'ocifetchinto' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => null, ), 'ocifetchstatement' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_fetch_all()', ), 'ocifreecollection' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Collection::free()', ), 'ocifreecursor' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_free_statement()', ), 'ocifreedesc' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::free()', ), 'ocifreestatement' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_free_statement()', ), 'ociinternaldebug' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_internal_debug()', ), 'ociloadlob' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::load()', ), 'ocilogoff' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_close()', ), 'ocilogon' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_connect()', ), 'ocinewcollection' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_new_collection()', ), 'ocinewcursor' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_new_cursor()', ), 'ocinewdescriptor' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_new_descriptor()', ), 'ocinlogon' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_new_connect()', ), 'ocinumcols' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_num_fields()', ), 'ociparse' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_parse()', ), 'ociplogon' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_pconnect()', ), 'ociresult' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_result()', ), 'ocirollback' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_rollback()', ), 'ocirowcount' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_num_rows()', ), 'ocisavelob' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::save()', ), 'ocisavelobfile' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::import()', ), 'ociserverversion' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_server_version()', ), 'ocisetprefetch' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_set_prefetch()', ), 'ocistatementtype' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'oci_statement_type()', ), 'ociwritelobtofile' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::export()', ), 'ociwritetemporarylob' => array( '5.4' => false, - '5.5' => false, - '5.6' => false, 'alternative' => 'OCI-Lob::writeTemporary()', ), 'mysqli_get_cache_stats' => array( @@ -884,12 +741,283 @@ class RemovedFunctionsSniff extends AbstractRemovedFeatureSniff '7.3' => false, 'alternative' => null, ), + + 'convert_cyr_string' => array( + '7.4' => false, + 'alternative' => 'mb_convert_encoding(), iconv() or UConverter', + ), + 'ezmlm_hash' => array( + '7.4' => false, + 'alternative' => null, + ), + 'get_magic_quotes_gpc' => array( + '7.4' => false, + 'alternative' => null, + ), + 'get_magic_quotes_runtime' => array( + '7.4' => false, + 'alternative' => null, + ), + 'hebrevc' => array( + '7.4' => false, + 'alternative' => null, + ), + 'is_real' => array( + '7.4' => false, + 'alternative' => 'is_float()', + ), + 'money_format' => array( + '7.4' => false, + 'alternative' => 'NumberFormatter::formatCurrency()', + ), + 'restore_include_path' => array( + '7.4' => false, + 'alternative' => "ini_restore('include_path')", + ), + 'ibase_add_user' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_affected_rows' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_backup' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_add' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_cancel' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_close' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_create' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_echo' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_get' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_import' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_info' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_blob_open' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_close' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_commit_ret' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_commit' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_connect' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_db_info' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_delete_user' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_drop_db' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_errcode' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_errmsg' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_execute' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_fetch_assoc' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_fetch_object' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_fetch_row' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_field_info' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_free_event_handler' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_free_query' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_free_result' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_gen_id' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_maintain_db' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_modify_user' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_name_result' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_num_fields' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_num_params' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_param_info' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_pconnect' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_prepare' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_query' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_restore' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_rollback_ret' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_rollback' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_server_info' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_service_attach' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_service_detach' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_set_event_handler' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_trans' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ibase_wait_event' => array( + '7.4' => true, + 'alternative' => null, + ), + 'ldap_control_paged_result_response' => array( + '7.4' => false, + 'alternative' => 'ldap_search()', + ), + 'ldap_control_paged_result' => array( + '7.4' => false, + 'alternative' => 'ldap_search()', + ), + 'recode_file' => array( + '7.4' => true, + 'alternative' => 'the iconv or mbstring extension', + ), + 'recode_string' => array( + '7.4' => true, + 'alternative' => 'the iconv or mbstring extension', + ), + 'recode' => array( + '7.4' => true, + 'alternative' => 'the iconv or mbstring extension', + ), + 'wddx_add_vars' => array( + '7.4' => true, + 'alternative' => null, + ), + 'wddx_deserialize' => array( + '7.4' => true, + 'alternative' => null, + ), + 'wddx_packet_end' => array( + '7.4' => true, + 'alternative' => null, + ), + 'wddx_packet_start' => array( + '7.4' => true, + 'alternative' => null, + ), + 'wddx_serialize_value' => array( + '7.4' => true, + 'alternative' => null, + ), + 'wddx_serialize_vars' => array( + '7.4' => true, + 'alternative' => null, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 5.6 + * * @return array */ public function register() @@ -904,6 +1032,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -948,6 +1078,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -961,6 +1093,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/FunctionUse/RequiredToOptionalFunctionParametersSniff.php b/PHPCompatibility/Sniffs/FunctionUse/RequiredToOptionalFunctionParametersSniff.php index e90f4508..9c32ccca 100644 --- a/PHPCompatibility/Sniffs/FunctionUse/RequiredToOptionalFunctionParametersSniff.php +++ b/PHPCompatibility/Sniffs/FunctionUse/RequiredToOptionalFunctionParametersSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\FunctionUse; @@ -14,11 +15,17 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\FunctionUse\RequiredToOptionalFunctionParametersSniff. + * Detect missing required function parameters in calls to native PHP functions. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * Specifically when those function parameters are no longer required in more recent PHP versions. + * + * PHP version All + * + * @link https://www.php.net/manual/en/doc.changelog.php + * + * @since 7.0.3 + * @since 7.1.0 Now extends the `AbstractComplexVersionSniff` instead of the base `Sniff` class. + * @since 9.0.0 Renamed from `RequiredOptionalFunctionParametersSniff` to `RequiredToOptionalFunctionParametersSniff`. */ class RequiredToOptionalFunctionParametersSniff extends AbstractComplexVersionSniff { @@ -31,9 +38,25 @@ class RequiredToOptionalFunctionParametersSniff extends AbstractComplexVersionSn * The index is the location of the parameter in the parameter list, starting at 0 ! * If's sufficient to list the last version in which the parameter was still required. * + * @since 7.0.3 + * * @var array */ protected $functionParameters = array( + 'array_merge' => array( + 0 => array( + 'name' => 'array(s) to merge', + '7.3' => true, + '7.4' => false, + ), + ), + 'array_merge_recursive' => array( + 0 => array( + 'name' => 'array(s) to merge', + '7.3' => true, + '7.4' => false, + ), + ), 'array_push' => array( 1 => array( 'name' => 'element to push', @@ -138,6 +161,8 @@ class RequiredToOptionalFunctionParametersSniff extends AbstractComplexVersionSn /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.3 + * * @return array */ public function register() @@ -151,6 +176,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -166,6 +193,7 @@ public function process(File $phpcsFile, $stackPtr) \T_OBJECT_OPERATOR => true, \T_FUNCTION => true, \T_CONST => true, + \T_NEW => true, ); $prevToken = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true); @@ -207,6 +235,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Determine whether an error/warning should be thrown for an item based on collected information. * + * @since 7.1.0 + * * @param array $errorInfo Detail information about an item. * * @return bool @@ -220,6 +250,8 @@ protected function shouldThrowError(array $errorInfo) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -233,6 +265,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -244,6 +278,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -275,6 +311,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -286,6 +324,8 @@ protected function getErrorMsgTemplate() /** * Generates the error or warning for this item. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the relevant token in * the stack. diff --git a/PHPCompatibility/Sniffs/Generators/NewGeneratorReturnSniff.php b/PHPCompatibility/Sniffs/Generators/NewGeneratorReturnSniff.php index 0d3e98ba..98ca2461 100644 --- a/PHPCompatibility/Sniffs/Generators/NewGeneratorReturnSniff.php +++ b/PHPCompatibility/Sniffs/Generators/NewGeneratorReturnSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Generators; @@ -16,21 +15,23 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Generators\NewGeneratorReturnSniff. - * - * As of PHP 7.0, a return statement can be used within a generator for a final expression to be returned. + * As of PHP 7.0, a `return` statement can be used within a generator for a final expression to be returned. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.generator-return-expressions + * @link https://wiki.php.net/rfc/generator-return-expressions + * @link https://www.php.net/manual/en/language.generators.syntax.php + * + * @since 8.2.0 */ class NewGeneratorReturnSniff extends Sniff { /** * Scope conditions within which a yield can exist. * + * @since 9.0.0 + * * @var array */ private $validConditions = array( @@ -42,6 +43,8 @@ class NewGeneratorReturnSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -69,7 +72,7 @@ public function register() $targets[] = \T_STRING; } - if (defined('T_YIELD_FROM')) { + if (\defined('T_YIELD_FROM')) { $targets[] = \T_YIELD_FROM; } @@ -79,6 +82,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -123,7 +128,7 @@ public function process(File $phpcsFile, $stackPtr) } $targets = array(\T_RETURN, \T_CLOSURE, \T_FUNCTION, \T_CLASS); - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $targets[] = \T_ANON_CLASS; } diff --git a/PHPCompatibility/Sniffs/IniDirectives/NewIniDirectivesSniff.php b/PHPCompatibility/Sniffs/IniDirectives/NewIniDirectivesSniff.php index 99875d67..cd40fa68 100644 --- a/PHPCompatibility/Sniffs/IniDirectives/NewIniDirectivesSniff.php +++ b/PHPCompatibility/Sniffs/IniDirectives/NewIniDirectivesSniff.php @@ -1,11 +1,11 @@ - * @copyright 2013 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\IniDirectives; @@ -14,14 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\IniDirectives\NewIniDirectivesSniff. + * Detect the use of new INI directives through `ini_set()` or `ini_get()`. * - * Discourages the use of new INI directives through ini_set() or ini_get(). + * PHP version All * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2013 Cu.be Solutions bvba + * @link https://www.php.net/manual/en/ini.list.php + * @link https://www.php.net/manual/en/ini.core.php + * + * @since 5.5 + * @since 7.0.7 When a new directive is used with `ini_set()`, the sniff will now throw an error + * instead of a warning. + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. */ class NewIniDirectivesSniff extends AbstractNewFeatureSniff { @@ -31,6 +34,9 @@ class NewIniDirectivesSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the ini directive appears. * + * @since 5.5 + * @since 7.0.3 Support for 'alternative' has been added. + * * @var array(string) */ protected $newIniDirectives = array( @@ -427,6 +433,130 @@ class NewIniDirectivesSniff extends AbstractNewFeatureSniff '5.4' => false, '5.5' => true, ), + 'opcache.enable' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.enable_cli' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.memory_consumption' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.interned_strings_buffer' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.max_accelerated_files' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.max_wasted_percentage' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.use_cwd' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.validate_timestamps' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.revalidate_freq' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.revalidate_path' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.save_comments' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.load_comments' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.fast_shutdown' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.enable_file_override' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.optimization_level' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.inherited_hack' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.dups_fix' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.blacklist_filename' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.max_file_size' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.consistency_checks' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.force_restart_timeout' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.error_log' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.log_verbosity_level' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.preferred_memory_model' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.protect_memory' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.mmap_base' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.restrict_api' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.file_update_protection' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.huge_code_pages' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.lockfile_path' => array( + '5.4' => false, + '5.5' => true, + ), + 'opcache.opt_debug_level' => array( + '5.4' => false, + '5.5' => true, + ), 'session.use_strict_mode' => array( '5.5.1' => false, @@ -454,6 +584,31 @@ class NewIniDirectivesSniff extends AbstractNewFeatureSniff '5.6' => false, '7.0' => true, ), + 'opcache.file_cache' => array( + '5.6' => false, + '7.0' => true, + ), + 'opcache.file_cache_only' => array( + '5.6' => false, + '7.0' => true, + ), + 'opcache.file_cache_consistency_checks' => array( + '5.6' => false, + '7.0' => true, + ), + 'opcache.file_cache_fallback' => array( + '5.6' => false, + '7.0' => true, + ), // Windows only. + + 'opcache.validate_permission' => array( + '7.0.13' => false, + '7.0.14' => true, + ), + 'opcache.validate_root' => array( + '7.0.13' => false, + '7.0.14' => true, + ), 'hard_timeout' => array( '7.0' => false, @@ -502,11 +657,34 @@ class NewIniDirectivesSniff extends AbstractNewFeatureSniff '7.2' => false, '7.3' => true, ), + + 'ffi.enable' => array( + '7.3' => false, + '7.4' => true, + ), + 'ffi.preload' => array( + '7.3' => false, + '7.4' => true, + ), + 'opcache.cache_id' => array( + '7.3' => false, + '7.4' => true, + ), + 'opcache.preload' => array( + '7.3' => false, + '7.4' => true, + ), + 'zend.exception_ignore_args' => array( + '7.3' => false, + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -517,6 +695,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -566,6 +746,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -579,6 +761,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -590,6 +774,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -616,6 +802,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -627,6 +815,8 @@ protected function getErrorMsgTemplate() /** * Allow for concrete child classes to filter the error message before it's passed to PHPCS. * + * @since 7.1.0 + * * @param string $error The error message which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. @@ -646,6 +836,8 @@ protected function filterErrorMsg($error, array $itemInfo, array $errorInfo) /** * Allow for concrete child classes to filter the error data before it's passed to PHPCS. * + * @since 7.1.0 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/Sniffs/IniDirectives/RemovedIniDirectivesSniff.php b/PHPCompatibility/Sniffs/IniDirectives/RemovedIniDirectivesSniff.php index fd6bf2ba..a6928ae0 100644 --- a/PHPCompatibility/Sniffs/IniDirectives/RemovedIniDirectivesSniff.php +++ b/PHPCompatibility/Sniffs/IniDirectives/RemovedIniDirectivesSniff.php @@ -1,11 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\IniDirectives; @@ -14,23 +14,31 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\IniDirectives\RemovedIniDirectivesSniff. + * Detect the use of deprecated and removed INI directives through `ini_set()` or `ini_get()`. * - * Discourages the use of deprecated and removed INI directives through ini_set() or ini_get(). + * PHP version All * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * @link https://www.php.net/manual/en/ini.list.php + * @link https://www.php.net/manual/en/ini.core.php + * + * @since 5.5 + * @since 7.0.0 This sniff now throws a warning (deprecated) or an error (removed) depending + * on the `testVersion` set. Previously it would always throw a warning. + * @since 7.0.1 The sniff will now only throw warnings for `ini_get()`. + * @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class. + * @since 9.0.0 Renamed from `DeprecatedIniDirectivesSniff` to `RemovedIniDirectivesSniff`. */ class RemovedIniDirectivesSniff extends AbstractRemovedFeatureSniff { /** - * A list of deprecated INI directives. + * A list of deprecated/removed INI directives. * * The array lists : version number with false (deprecated) and true (removed). * If's sufficient to list the first version where the ini directive was deprecated/removed. * + * @since 5.5 + * @since 7.0.3 Support for 'alternative' has been added. + * * @var array(string) */ protected $deprecatedIniDirectives = array( @@ -38,6 +46,27 @@ class RemovedIniDirectivesSniff extends AbstractRemovedFeatureSniff '5.1' => true, 'alternative' => 'fbsql.batchsize', ), + 'pfpro.defaulthost' => array( + '5.1' => true, + ), + 'pfpro.defaultport' => array( + '5.1' => true, + ), + 'pfpro.defaulttimeout' => array( + '5.1' => true, + ), + 'pfpro.proxyaddress' => array( + '5.1' => true, + ), + 'pfpro.proxyport' => array( + '5.1' => true, + ), + 'pfpro.proxylogon' => array( + '5.1' => true, + ), + 'pfpro.proxypassword' => array( + '5.1' => true, + ), 'ifx.allow_persistent' => array( '5.2.1' => true, @@ -183,6 +212,9 @@ class RemovedIniDirectivesSniff extends AbstractRemovedFeatureSniff 'xsl.security_prefs' => array( '7.0' => true, ), + 'opcache.load_comments' => array( + '7.0' => true, + ), 'mcrypt.algorithms_dir' => array( '7.1' => false, @@ -228,11 +260,47 @@ class RemovedIniDirectivesSniff extends AbstractRemovedFeatureSniff 'pdo_odbc.db2_instance_name' => array( '7.3' => false, // Has been marked as deprecated in the manual from before this time. Now hard-deprecated. ), + + 'allow_url_include' => array( + '7.4' => false, + ), + 'ibase.allow_persistent' => array( + '7.4' => true, + ), + 'ibase.max_persistent' => array( + '7.4' => true, + ), + 'ibase.max_links' => array( + '7.4' => true, + ), + 'ibase.default_db' => array( + '7.4' => true, + ), + 'ibase.default_user' => array( + '7.4' => true, + ), + 'ibase.default_password' => array( + '7.4' => true, + ), + 'ibase.default_charset' => array( + '7.4' => true, + ), + 'ibase.timestampformat' => array( + '7.4' => true, + ), + 'ibase.dateformat' => array( + '7.4' => true, + ), + 'ibase.timeformat' => array( + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -243,6 +311,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -292,6 +362,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -305,6 +377,8 @@ public function getItemArray(array $itemInfo) /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -326,6 +400,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -337,10 +413,12 @@ protected function getErrorMsgTemplate() /** * Get the error message template for suggesting an alternative for a specific sniff. * + * @since 7.1.0 + * * @return string */ protected function getAlternativeOptionTemplate() { - return str_replace("%s", "'%s'", parent::getAlternativeOptionTemplate()); + return str_replace('%s', "'%s'", parent::getAlternativeOptionTemplate()); } } diff --git a/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingConstSniff.php b/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingConstSniff.php index 75edee96..c9c2225f 100644 --- a/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingConstSniff.php +++ b/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingConstSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\InitialValue; @@ -15,15 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\InitialValue\NewConstantArraysUsingConstSniff. - * - * Constant arrays using the const keyword in PHP 5.6 + * Detect declaration of constants using the `const` keyword with a (constant) array value + * as supported since PHP 5.6. * * PHP version 5.6 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/const_scalar_exprs + * @link https://www.php.net/manual/en/language.constants.syntax.php + * + * @since 7.1.4 + * @since 9.0.0 Renamed from `ConstantArraysUsingConstSniff` to `NewConstantArraysUsingConstSniff`. */ class NewConstantArraysUsingConstSniff extends Sniff { @@ -31,6 +31,8 @@ class NewConstantArraysUsingConstSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.4 + * * @return array */ public function register() @@ -41,6 +43,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingDefineSniff.php b/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingDefineSniff.php index 9784afae..7b996728 100644 --- a/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingDefineSniff.php +++ b/PHPCompatibility/Sniffs/InitialValue/NewConstantArraysUsingDefineSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\InitialValue; @@ -15,15 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\InitialValue\NewConstantArraysUsingDefineSniff. - * - * Constant arrays using define in PHP 7.0 + * Detect declaration of constants using `define()` with a (constant) array value + * as supported since PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.define-array + * @link https://www.php.net/manual/en/language.constants.syntax.php + * + * @since 7.0.0 + * @since 9.0.0 Renamed from `ConstantArraysUsingDefineSniff` to `NewConstantArraysUsingDefineSniff`. */ class NewConstantArraysUsingDefineSniff extends Sniff { @@ -31,6 +31,8 @@ class NewConstantArraysUsingDefineSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -41,6 +43,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -80,12 +84,12 @@ public function process(File $phpcsFile, $stackPtr) $targetNestingLevel = 0; if (isset($tokens[$secondParam['start']]['nested_parenthesis'])) { - $targetNestingLevel = count($tokens[$secondParam['start']]['nested_parenthesis']); + $targetNestingLevel = \count($tokens[$secondParam['start']]['nested_parenthesis']); } $array = $phpcsFile->findNext(array(\T_ARRAY, \T_OPEN_SHORT_ARRAY), $secondParam['start'], ($secondParam['end'] + 1)); if ($array !== false) { - if ((isset($tokens[$array]['nested_parenthesis']) === false && $targetNestingLevel === 0) || count($tokens[$array]['nested_parenthesis']) === $targetNestingLevel) { + if ((isset($tokens[$array]['nested_parenthesis']) === false && $targetNestingLevel === 0) || \count($tokens[$array]['nested_parenthesis']) === $targetNestingLevel) { $phpcsFile->addError( 'Constant arrays using define are not allowed in PHP 5.6 or earlier', $array, diff --git a/PHPCompatibility/Sniffs/InitialValue/NewConstantScalarExpressionsSniff.php b/PHPCompatibility/Sniffs/InitialValue/NewConstantScalarExpressionsSniff.php index ea39a7bc..8e1cddca 100644 --- a/PHPCompatibility/Sniffs/InitialValue/NewConstantScalarExpressionsSniff.php +++ b/PHPCompatibility/Sniffs/InitialValue/NewConstantScalarExpressionsSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\InitialValue; @@ -17,18 +16,19 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\InitialValue\NewConstantScalarExpressionsSniff. + * Detect constant scalar expressions being used to set an initial value. * * Since PHP 5.6, it is now possible to provide a scalar expression involving * numeric and string literals and/or constants in contexts where PHP previously * expected a static value, such as constant and property declarations and - * default function arguments. + * default values for function parameters. * * PHP version 5.6 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.const-scalar-exprs + * @link https://wiki.php.net/rfc/const_scalar_exprs + * + * @since 8.2.0 */ class NewConstantScalarExpressionsSniff extends Sniff { @@ -36,6 +36,8 @@ class NewConstantScalarExpressionsSniff extends Sniff /** * Error message. * + * @since 8.2.0 + * * @var string */ const ERROR_PHRASE = 'Constant scalar expressions are not allowed %s in PHP 5.5 or earlier.'; @@ -43,6 +45,8 @@ class NewConstantScalarExpressionsSniff extends Sniff /** * Partial error phrases to be used in combination with the error message constant. * + * @since 8.2.0 + * * @var array */ protected $errorPhrases = array( @@ -57,6 +61,8 @@ class NewConstantScalarExpressionsSniff extends Sniff * * This list will be enriched in the setProperties() method. * + * @since 8.2.0 + * * @var array */ protected $safeOperands = array( @@ -89,6 +95,8 @@ class NewConstantScalarExpressionsSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -109,6 +117,8 @@ public function register() /** * Make some adjustments to the $safeOperands property. * + * @since 8.2.0 + * * @return void */ public function setProperties() @@ -121,6 +131,8 @@ public function setProperties() /** * Do a version check to determine if this sniff needs to run at all. * + * @since 8.2.0 + * * @return bool */ protected function bowOutEarly() @@ -132,6 +144,8 @@ protected function bowOutEarly() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -171,7 +185,7 @@ public function process(File $phpcsFile, $stackPtr) // Which nesting level is the one we are interested in ? $nestedParenthesisCount = 1; if (isset($tokens[$opener]['nested_parenthesis'])) { - $nestedParenthesisCount += count($tokens[$opener]['nested_parenthesis']); + $nestedParenthesisCount += \count($tokens[$opener]['nested_parenthesis']); } foreach ($params as $param) { @@ -249,7 +263,7 @@ public function process(File $phpcsFile, $stackPtr) $targetNestingLevel = 0; if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { - $targetNestingLevel = count($tokens[$stackPtr]['nested_parenthesis']); + $targetNestingLevel = \count($tokens[$stackPtr]['nested_parenthesis']); } // Examine each variable/constant in multi-declarations. @@ -296,6 +310,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Is a value declared and is the value declared valid pre-PHP 5.6 ? * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -320,13 +336,15 @@ protected function isValidAssignment(File $phpcsFile, $stackPtr, $end) /** * Is a value declared and is the value declared constant as accepted in PHP 5.5 and lower ? * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param array $tokens The token stack of the current file. * @param int $start The stackPtr from which to start examining. * @param int $end The end of the value definition (inclusive), * i.e. this token will be examined as part of * the snippet. - * @param bool $nestedArrays Optional. Array nesting level when examining + * @param int $nestedArrays Optional. Array nesting level when examining * the content of an array. * * @return bool @@ -464,6 +482,8 @@ protected function isStaticValue(File $phpcsFile, $tokens, $start, $end, $nested /** * Throw an error if a scalar expression is found. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the token to link the error to. * @param string $type Type of usage found. @@ -499,6 +519,8 @@ protected function throwError(File $phpcsFile, $stackPtr, $type, $content = '') * Checks whether a certain part of a declaration needs to be skipped over or * if it is the real end of the declaration. * + * @since 8.2.0 + * * @param array $tokens Token stack of the current file. * @param int $endPtr The token to examine as a candidate end pointer. * @param int $targetLevel Target nesting level. @@ -522,7 +544,7 @@ private function isRealEndOfDeclaration($tokens, $endPtr, $targetLevel) // Check if a comma is at the nesting level we're targetting. $nestingLevel = 0; if (isset($tokens[$endPtr]['nested_parenthesis']) === true) { - $nestingLevel = count($tokens[$endPtr]['nested_parenthesis']); + $nestingLevel = \count($tokens[$endPtr]['nested_parenthesis']); } if ($nestingLevel > $targetLevel) { return $endPtr; diff --git a/PHPCompatibility/Sniffs/InitialValue/NewHeredocSniff.php b/PHPCompatibility/Sniffs/InitialValue/NewHeredocSniff.php index f512bed2..406237a5 100644 --- a/PHPCompatibility/Sniffs/InitialValue/NewHeredocSniff.php +++ b/PHPCompatibility/Sniffs/InitialValue/NewHeredocSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\InitialValue; @@ -16,7 +15,7 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\InitialValue\NewHeredocSniff. + * Detect a heredoc being used to set an initial value. * * As of PHP 5.3.0, it's possible to initialize static variables, class properties * and constants declared using the `const` keyword, using the Heredoc syntax. @@ -28,9 +27,12 @@ * * PHP version 5.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration53.new-features.php + * @link https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc + * + * @since 7.1.4 + * @since 8.2.0 Now extends the NewConstantScalarExpressionsSniff instead of the base Sniff class. + * @since 9.0.0 Renamed from `NewHeredocInitializeSniff` to `NewHeredocSniff`. */ class NewHeredocSniff extends NewConstantScalarExpressionsSniff { @@ -38,6 +40,8 @@ class NewHeredocSniff extends NewConstantScalarExpressionsSniff /** * Error message. * + * @since 8.2.0 + * * @var string */ const ERROR_PHRASE = 'Initializing %s using the Heredoc syntax was not supported in PHP 5.2 or earlier'; @@ -45,6 +49,8 @@ class NewHeredocSniff extends NewConstantScalarExpressionsSniff /** * Partial error phrases to be used in combination with the error message constant. * + * @since 8.2.0 + * * @var array */ protected $errorPhrases = array( @@ -58,6 +64,8 @@ class NewHeredocSniff extends NewConstantScalarExpressionsSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 8.2.0 + * * @return bool */ protected function bowOutEarly() @@ -69,6 +77,8 @@ protected function bowOutEarly() /** * Is a value declared and does the declared value not contain an heredoc ? * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Interfaces/InternalInterfacesSniff.php b/PHPCompatibility/Sniffs/Interfaces/InternalInterfacesSniff.php index 10524471..e154a603 100644 --- a/PHPCompatibility/Sniffs/Interfaces/InternalInterfacesSniff.php +++ b/PHPCompatibility/Sniffs/Interfaces/InternalInterfacesSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Interfaces; @@ -14,11 +15,15 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Interfaces\InternalInterfacesSniff. + * Detect classes which implement PHP native interfaces intended only for PHP internal use. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version 5.0+ + * + * @link https://www.php.net/manual/en/class.traversable.php + * @link https://www.php.net/manual/en/class.throwable.php + * @link https://www.php.net/manual/en/class.datetimeinterface.php + * + * @since 7.0.3 */ class InternalInterfacesSniff extends Sniff { @@ -28,6 +33,8 @@ class InternalInterfacesSniff extends Sniff * * The array lists : the error message to use. * + * @since 7.0.3 + * * @var array(string => string) */ protected $internalInterfaces = array( @@ -40,6 +47,8 @@ class InternalInterfacesSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.3 + * * @return array */ public function register() @@ -49,7 +58,7 @@ public function register() $targets = array(\T_CLASS); - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $targets[] = \T_ANON_CLASS; } @@ -60,6 +69,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -70,7 +81,7 @@ public function process(File $phpcsFile, $stackPtr) { $interfaces = PHPCSHelper::findImplementedInterfaceNames($phpcsFile, $stackPtr); - if (is_array($interfaces) === false || $interfaces === array()) { + if (\is_array($interfaces) === false || $interfaces === array()) { return; } diff --git a/PHPCompatibility/Sniffs/Interfaces/NewInterfacesSniff.php b/PHPCompatibility/Sniffs/Interfaces/NewInterfacesSniff.php index 19276336..35c5939b 100644 --- a/PHPCompatibility/Sniffs/Interfaces/NewInterfacesSniff.php +++ b/PHPCompatibility/Sniffs/Interfaces/NewInterfacesSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Interfaces; @@ -14,11 +15,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Interfaces\NewInterfacesSniff. + * Detect use of new PHP native interfaces and unsupported interface methods. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version 5.0+ + * + * @since 7.0.3 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. + * @since 7.1.4 Now also detects new interfaces when used as parameter type declarations. + * @since 8.2.0 Now also detects new interfaces when used as return type declarations. */ class NewInterfacesSniff extends AbstractNewFeatureSniff { @@ -29,7 +33,9 @@ class NewInterfacesSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the interface appears. * - * @var array(string => array(string => int|string|null)) + * @since 7.0.3 + * + * @var array(string => array(string => bool)) */ protected $newInterfaces = array( 'Traversable' => array( @@ -102,18 +108,22 @@ class NewInterfacesSniff extends AbstractNewFeatureSniff /** * A list of methods which cannot be used in combination with particular interfaces. * + * @since 7.0.3 + * * @var array(string => array(string => string)) */ protected $unsupportedMethods = array( 'Serializable' => array( - '__sleep' => 'http://php.net/serializable', - '__wakeup' => 'http://php.net/serializable', + '__sleep' => 'https://www.php.net/serializable', + '__wakeup' => 'https://www.php.net/serializable', ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.3 + * * @return array */ public function register() @@ -128,11 +138,11 @@ public function register() \T_CLOSURE, ); - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $targets[] = \T_ANON_CLASS; } - if (defined('T_RETURN_TYPE')) { + if (\defined('T_RETURN_TYPE')) { $targets[] = \T_RETURN_TYPE; } @@ -143,6 +153,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -188,6 +200,8 @@ public function process(File $phpcsFile, $stackPtr) * - Detect classes implementing the new interfaces. * - Detect classes implementing the new interfaces with unsupported functions. * + * @since 7.1.4 Split off from the `process()` method. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -198,7 +212,7 @@ private function processClassToken(File $phpcsFile, $stackPtr) { $interfaces = PHPCSHelper::findImplementedInterfaceNames($phpcsFile, $stackPtr); - if (is_array($interfaces) === false || $interfaces === array()) { + if (\is_array($interfaces) === false || $interfaces === array()) { return; } @@ -253,6 +267,8 @@ private function processClassToken(File $phpcsFile, $stackPtr) * * - Detect new interfaces when used as a type hint. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -262,7 +278,7 @@ private function processClassToken(File $phpcsFile, $stackPtr) private function processFunctionToken(File $phpcsFile, $stackPtr) { $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr); - if (empty($typeHints) || is_array($typeHints) === false) { + if (empty($typeHints) || \is_array($typeHints) === false) { return; } @@ -286,6 +302,8 @@ private function processFunctionToken(File $phpcsFile, $stackPtr) * * - Detect new interfaces when used as a return type declaration. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -294,7 +312,11 @@ private function processFunctionToken(File $phpcsFile, $stackPtr) */ private function processReturnTypeToken(File $phpcsFile, $stackPtr) { - $returnTypeHint = $this->getReturnTypeHintName($phpcsFile, $stackPtr); + $returnTypeHint = $this->getReturnTypeHintName($phpcsFile, $stackPtr); + if (empty($returnTypeHint)) { + return; + } + $returnTypeHint = ltrim($returnTypeHint, '\\'); $returnTypeHintLc = strtolower($returnTypeHint); @@ -314,6 +336,8 @@ private function processReturnTypeToken(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -327,6 +351,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/Keywords/CaseSensitiveKeywordsSniff.php b/PHPCompatibility/Sniffs/Keywords/CaseSensitiveKeywordsSniff.php index 8d95b6af..64002b4d 100644 --- a/PHPCompatibility/Sniffs/Keywords/CaseSensitiveKeywordsSniff.php +++ b/PHPCompatibility/Sniffs/Keywords/CaseSensitiveKeywordsSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Keywords; @@ -15,16 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Keywords\CaseSensitiveKeywordsSniff. + * Detect usage of `self`, `parent` and `static` and verify they are lowercase. * - * Prior to PHP 5.5, cases existed where the self, parent, and static keywords + * Prior to PHP 5.5, cases existed where the `self`, `parent`, and `static` keywords * were treated in a case sensitive fashion. * * PHP version 5.5 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration55.incompatible.php#migration55.incompatible.self-parent-static + * + * @since 7.1.4 */ class CaseSensitiveKeywordsSniff extends Sniff { @@ -32,6 +31,8 @@ class CaseSensitiveKeywordsSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.4 + * * @return array */ public function register() @@ -46,6 +47,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsDeclaredSniff.php b/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsDeclaredSniff.php index 5d9e323f..7f4411bc 100644 --- a/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsDeclaredSniff.php +++ b/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsDeclaredSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Keywords; @@ -16,18 +15,18 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesAsDeclaredClassSniff. + * Detects the use of some reserved keywords to name a class, interface, trait or namespace. * - * Prohibits the use of some reserved keywords to name a class, interface, trait or namespace. * Emits errors for reserved words and warnings for soft-reserved words. * - * @see http://php.net/manual/en/reserved.other-reserved-words.php - * * PHP version 7.0+ * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/reserved.other-reserved-words.php + * @link https://wiki.php.net/rfc/reserve_more_types_in_php_7 + * + * @since 7.0.8 + * @since 7.1.4 This sniff now throws a warning (soft reserved) or an error (reserved) depending + * on the `testVersion` set. Previously it would always throw an error. */ class ForbiddenNamesAsDeclaredSniff extends Sniff { @@ -35,6 +34,8 @@ class ForbiddenNamesAsDeclaredSniff extends Sniff /** * List of tokens which can not be used as class, interface, trait names or as part of a namespace. * + * @since 7.0.8 + * * @var array */ protected $forbiddenTokens = array( @@ -46,6 +47,8 @@ class ForbiddenNamesAsDeclaredSniff extends Sniff /** * T_STRING keywords to recognize as forbidden names. * + * @since 7.0.8 + * * @var array */ protected $forbiddenNames = array( @@ -67,6 +70,8 @@ class ForbiddenNamesAsDeclaredSniff extends Sniff * Using any of these keywords to name a class, interface, trait or namespace * is highly discouraged since they may be used in future versions of PHP. * + * @since 7.0.8 + * * @var array */ protected $softReservedNames = array( @@ -83,6 +88,8 @@ class ForbiddenNamesAsDeclaredSniff extends Sniff * word. * Set from the `register()` method. * + * @since 7.0.8 + * * @var array */ private $allForbiddenNames = array(); @@ -91,6 +98,8 @@ class ForbiddenNamesAsDeclaredSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.8 + * * @return array */ public function register() @@ -113,6 +122,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.8 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -137,7 +148,7 @@ public function process(File $phpcsFile, $stackPtr) return; } - if (in_array($tokenType, array('T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) { + if (\in_array($tokenType, array('T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) { // Check for the declared name being a name which is not tokenized as T_STRING. $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); if ($nextNonEmpty !== false && isset($this->forbiddenTokens[$tokens[$nextNonEmpty]['code']]) === true) { @@ -148,7 +159,7 @@ public function process(File $phpcsFile, $stackPtr) } unset($nextNonEmpty); - if (isset($name) === false || is_string($name) === false || $name === '') { + if (isset($name) === false || \is_string($name) === false || $name === '') { return; } diff --git a/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsInvokedFunctionsSniff.php b/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsInvokedFunctionsSniff.php index fce8719f..c1c38281 100644 --- a/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsInvokedFunctionsSniff.php +++ b/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesAsInvokedFunctionsSniff.php @@ -1,11 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Keywords; @@ -15,14 +15,13 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesAsInvokedFunctionsSniff. - * * Prohibits the use of reserved keywords invoked as functions. * - * @category PHP - * @package PHPCompatibility - * @author Jansen Price - * @copyright 2012 Cu.be Solutions bvba + * PHP version All + * + * @link https://www.php.net/manual/en/reserved.keywords.php + * + * @since 5.5 */ class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff { @@ -30,6 +29,8 @@ class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff /** * List of tokens to register. * + * @since 5.5 + * * @var array */ protected $targetedTokens = array( @@ -59,6 +60,8 @@ class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff * as its own token and for PHPCS versions which change the token to * T_STRING when used in a method call. * + * @since 5.5 + * * @var array */ protected $targetedStringTokens = array( @@ -83,6 +86,8 @@ class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -96,6 +101,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesSniff.php b/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesSniff.php index abcdfaef..6be1de4f 100644 --- a/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesSniff.php +++ b/PHPCompatibility/Sniffs/Keywords/ForbiddenNamesSniff.php @@ -1,11 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Keywords; @@ -15,14 +15,13 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesSniff. + * Detects the use of reserved keywords as class, function, namespace or constant names. * - * Prohibits the use of reserved keywords as class, function, namespace or constant names. + * PHP version All * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * @link https://www.php.net/manual/en/reserved.keywords.php + * + * @since 5.5 */ class ForbiddenNamesSniff extends Sniff { @@ -31,6 +30,8 @@ class ForbiddenNamesSniff extends Sniff * A list of keywords that can not be used as function, class and namespace name or constant name. * Mentions since which version it's not allowed. * + * @since 5.5 + * * @var array(string => string) */ protected $invalidNames = array( @@ -48,15 +49,20 @@ class ForbiddenNamesSniff extends Sniff 'continue' => 'all', 'declare' => 'all', 'default' => 'all', + 'die' => 'all', 'do' => 'all', + 'echo' => 'all', 'else' => 'all', 'elseif' => 'all', + 'empty' => 'all', 'enddeclare' => 'all', 'endfor' => 'all', 'endforeach' => 'all', 'endif' => 'all', 'endswitch' => 'all', 'endwhile' => 'all', + 'eval' => 'all', + 'exit' => 'all', 'extends' => 'all', 'final' => '5.0', 'finally' => '5.5', @@ -67,24 +73,34 @@ class ForbiddenNamesSniff extends Sniff 'goto' => '5.3', 'if' => 'all', 'implements' => '5.0', - 'interface' => '5.0', + 'include' => 'all', + 'include_once' => 'all', 'instanceof' => '5.0', 'insteadof' => '5.4', + 'interface' => '5.0', + 'isset' => 'all', + 'list' => 'all', 'namespace' => '5.3', 'new' => 'all', 'or' => 'all', + 'print' => 'all', 'private' => '5.0', 'protected' => '5.0', 'public' => '5.0', + 'require' => 'all', + 'require_once' => 'all', + 'return' => 'all', 'static' => 'all', 'switch' => 'all', 'throw' => '5.0', 'trait' => '5.4', 'try' => '5.0', + 'unset' => 'all', 'use' => 'all', 'var' => 'all', 'while' => 'all', 'xor' => 'all', + 'yield' => '5.5', '__class__' => 'all', '__dir__' => '5.3', '__file__' => 'all', @@ -96,6 +112,8 @@ class ForbiddenNamesSniff extends Sniff /** * A list of keywords that can follow use statements. * + * @since 7.0.1 + * * @var array(string => string) */ protected $validUseNames = array( @@ -106,6 +124,8 @@ class ForbiddenNamesSniff extends Sniff /** * Scope modifiers and other keywords allowed in trait use statements. * + * @since 7.1.4 + * * @var array */ private $allowedModifiers = array(); @@ -113,6 +133,8 @@ class ForbiddenNamesSniff extends Sniff /** * Targeted tokens. * + * @since 5.5 + * * @var array */ protected $targetedTokens = array( @@ -131,6 +153,8 @@ class ForbiddenNamesSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -140,7 +164,7 @@ public function register() $tokens = $this->targetedTokens; - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $tokens[] = \T_ANON_CLASS; } @@ -150,6 +174,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -173,6 +199,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -231,6 +259,23 @@ public function processNonString(File $phpcsFile, $stackPtr, $tokens) $nextNonEmpty = $maybeUseNext; } + /* + * Deal with foreach ( ... as list() ). + */ + elseif ($tokens[$stackPtr]['type'] === 'T_AS' + && isset($tokens[$stackPtr]['nested_parenthesis']) === true + && $tokens[$nextNonEmpty]['code'] === \T_LIST + ) { + $parentheses = array_reverse($tokens[$stackPtr]['nested_parenthesis'], true); + foreach ($parentheses as $open => $close) { + if (isset($tokens[$open]['parenthesis_owner']) + && $tokens[$tokens[$open]['parenthesis_owner']]['code'] === \T_FOREACH + ) { + return; + } + } + } + /* * Deal with functions declared to return by reference. */ @@ -251,7 +296,7 @@ public function processNonString(File $phpcsFile, $stackPtr, $tokens) */ elseif ($tokens[$stackPtr]['type'] === 'T_NAMESPACE') { if ($tokens[$stackPtr + 1]['code'] === \T_NS_SEPARATOR) { - // Not a namespace declaration, but use of, i.e. namespace\someFunction(); + // Not a namespace declaration, but use of, i.e. `namespace\someFunction();`. return; } @@ -311,6 +356,8 @@ public function processNonString(File $phpcsFile, $stackPtr, $tokens) /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -360,6 +407,8 @@ public function processString(File $phpcsFile, $stackPtr, $tokens) /** * Add the error message. * + * @since 7.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -380,12 +429,14 @@ protected function addError(File $phpcsFile, $stackPtr, $content, $data) * Check if the current token code is for a token which can be considered * the end of a (partial) use statement. * + * @since 7.0.8 + * * @param int $token The current token information. * * @return bool */ protected function isEndOfUseStatement($token) { - return in_array($token['code'], array(\T_CLOSE_CURLY_BRACKET, \T_SEMICOLON, \T_COMMA), true); + return \in_array($token['code'], array(\T_CLOSE_CURLY_BRACKET, \T_SEMICOLON, \T_COMMA), true); } } diff --git a/PHPCompatibility/Sniffs/Keywords/NewKeywordsSniff.php b/PHPCompatibility/Sniffs/Keywords/NewKeywordsSniff.php index 0ba83b3f..61f79aa5 100644 --- a/PHPCompatibility/Sniffs/Keywords/NewKeywordsSniff.php +++ b/PHPCompatibility/Sniffs/Keywords/NewKeywordsSniff.php @@ -1,11 +1,11 @@ - * @copyright 2013 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Keywords; @@ -15,12 +15,18 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Keywords\NewKeywordsSniff. + * Detect use of new PHP keywords. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2013 Cu.be Solutions bvba + * PHP version All + * + * @link https://wiki.php.net/rfc/heredoc-with-double-quotes + * @link https://wiki.php.net/rfc/horizontalreuse (traits) + * @link https://wiki.php.net/rfc/generators + * @link https://wiki.php.net/rfc/finally + * @link https://wiki.php.net/rfc/generator-delegation + * + * @since 5.5 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. */ class NewKeywordsSniff extends AbstractNewFeatureSniff { @@ -38,7 +44,10 @@ class NewKeywordsSniff extends AbstractNewFeatureSniff * The callback function should return `true` if the condition is met and the * error should *not* be thrown. * - * @var array(string => array(string => int|string|null)) + * @since 5.5 + * @since 7.0.3 Support for 'condition' has been added. + * + * @var array(string => array(string => bool|string)) */ protected $newKeywords = array( 'T_HALT_COMPILER' => array( @@ -149,6 +158,8 @@ class NewKeywordsSniff extends AbstractNewFeatureSniff * * Will be set up from the register() method. * + * @since 7.0.5 + * * @var array(string => string) */ protected $translateContentToToken = array(); @@ -157,6 +168,8 @@ class NewKeywordsSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -164,7 +177,7 @@ public function register() $tokens = array(); $translate = array(); foreach ($this->newKeywords as $token => $versions) { - if (defined($token)) { + if (\defined($token)) { $tokens[] = constant($token); } if (isset($versions['content'])) { @@ -178,7 +191,7 @@ public function register() */ if (empty($translate) === false) { $this->translateContentToToken = $translate; - $tokens[] = \T_STRING; + $tokens[] = \T_STRING; } return $tokens; @@ -188,6 +201,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -272,7 +287,7 @@ public function process(File $phpcsFile, $stackPtr) ) { // Skip based on token scope condition. if (isset($this->newKeywords[$tokenType]['condition']) - && call_user_func(array($this, $this->newKeywords[$tokenType]['condition']), $phpcsFile, $stackPtr) === true + && \call_user_func(array($this, $this->newKeywords[$tokenType]['condition']), $phpcsFile, $stackPtr) === true ) { return; } @@ -288,6 +303,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -301,6 +318,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -316,6 +335,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -333,6 +354,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Allow for concrete child classes to filter the error data before it's passed to PHPCS. * + * @since 7.1.0 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. @@ -352,6 +375,8 @@ protected function filterErrorData(array $data, array $itemInfo, array $errorInf * A double quoted identifier will have the opening quote on position 3 * in the string: `<<<"ID"`. * + * @since 8.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/LanguageConstructs/NewEmptyNonVariableSniff.php b/PHPCompatibility/Sniffs/LanguageConstructs/NewEmptyNonVariableSniff.php index 14c256b9..ffb9ef28 100644 --- a/PHPCompatibility/Sniffs/LanguageConstructs/NewEmptyNonVariableSniff.php +++ b/PHPCompatibility/Sniffs/LanguageConstructs/NewEmptyNonVariableSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\LanguageConstructs; @@ -16,15 +15,19 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\LanguageConstructs\NewEmptyNonVariableSniff. - * * Verify that nothing but variables are passed to empty(). * + * Prior to PHP 5.5, `empty()` only supported variables; anything else resulted in a parse error. + * * PHP version 5.5 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/empty_isset_exprs + * @link https://www.php.net/manual/en/function.empty.php + * + * @since 7.0.4 + * @since 9.0.0 The "is the parameter a variable" determination has been abstracted out + * and moved to a separate method `Sniff::isVariable()`. + * @since 9.0.0 Renamed from `EmptyNonVariableSniff` to `NewEmptyNonVariableSniff`. */ class NewEmptyNonVariableSniff extends Sniff { @@ -32,6 +35,8 @@ class NewEmptyNonVariableSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.4 + * * @return array */ public function register() @@ -42,6 +47,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -68,7 +75,7 @@ public function process(File $phpcsFile, $stackPtr) $nestingLevel = 0; if ($close !== ($open + 1) && isset($tokens[$open + 1]['nested_parenthesis'])) { - $nestingLevel = count($tokens[$open + 1]['nested_parenthesis']); + $nestingLevel = \count($tokens[$open + 1]['nested_parenthesis']); } if ($this->isVariable($phpcsFile, ($open + 1), $close, $nestingLevel) === true) { diff --git a/PHPCompatibility/Sniffs/LanguageConstructs/NewLanguageConstructsSniff.php b/PHPCompatibility/Sniffs/LanguageConstructs/NewLanguageConstructsSniff.php index 5997b250..44488ed0 100644 --- a/PHPCompatibility/Sniffs/LanguageConstructs/NewLanguageConstructsSniff.php +++ b/PHPCompatibility/Sniffs/LanguageConstructs/NewLanguageConstructsSniff.php @@ -1,11 +1,11 @@ - * @copyright 2013 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\LanguageConstructs; @@ -14,12 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\LanguageConstructs\NewLanguageConstructsSniff. + * Detect use of new PHP language constructs. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2013 Cu.be Solutions bvba + * PHP version All + * + * @link https://wiki.php.net/rfc/namespaceseparator + * @link https://wiki.php.net/rfc/variadics + * @link https://wiki.php.net/rfc/argument_unpacking + * + * @since 5.6 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. + * @since 9.0.0 Detection for new operator tokens has been moved to the `NewOperators` sniff. */ class NewLanguageConstructsSniff extends AbstractNewFeatureSniff { @@ -30,7 +35,9 @@ class NewLanguageConstructsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the keyword appears. * - * @var array(string => array(string => int|string|null)) + * @since 5.6 + * + * @var array(string => array(string => bool|string)) */ protected $newConstructs = array( 'T_NS_SEPARATOR' => array( @@ -41,7 +48,7 @@ class NewLanguageConstructsSniff extends AbstractNewFeatureSniff 'T_ELLIPSIS' => array( '5.5' => false, '5.6' => true, - 'description' => 'variadic functions using ...', + 'description' => 'the ... spread operator', ), ); @@ -49,6 +56,8 @@ class NewLanguageConstructsSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.6 + * * @return array */ public function register() @@ -64,6 +73,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.6 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -85,6 +96,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -98,6 +111,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -109,6 +124,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -126,6 +143,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Allow for concrete child classes to filter the error data before it's passed to PHPCS. * + * @since 7.1.0 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/Sniffs/Lists/AssignmentOrderSniff.php b/PHPCompatibility/Sniffs/Lists/AssignmentOrderSniff.php index b374743e..f5ba4772 100644 --- a/PHPCompatibility/Sniffs/Lists/AssignmentOrderSniff.php +++ b/PHPCompatibility/Sniffs/Lists/AssignmentOrderSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Lists; @@ -16,16 +15,18 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * List assignment order. + * Detect code affected by the changed list assignment order in PHP 7.0+. * - * The list() construct no longer assigns variables in reverse order. + * The `list()` construct no longer assigns variables in reverse order. * This affects all list constructs where non-unique variables are used. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list.order + * @link https://wiki.php.net/rfc/abstract_syntax_tree#changes_to_list + * @link https://www.php.net/manual/en/function.list.php + * + * @since 9.0.0 */ class AssignmentOrderSniff extends Sniff { @@ -33,6 +34,8 @@ class AssignmentOrderSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() @@ -47,6 +50,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -170,7 +175,7 @@ public function process(File $phpcsFile, $stackPtr) } // Verify that all variables used in the list() construct are unique. - if (count($listVars) !== count(array_unique($listVars))) { + if (\count($listVars) !== \count(array_unique($listVars))) { $phpcsFile->addError( 'list() will assign variable from left-to-right since PHP 7.0. Ensure all variables in list() are unique to prevent unexpected results.', $stackPtr, diff --git a/PHPCompatibility/Sniffs/Lists/ForbiddenEmptyListAssignmentSniff.php b/PHPCompatibility/Sniffs/Lists/ForbiddenEmptyListAssignmentSniff.php index 357fe62c..b9d4b7b0 100644 --- a/PHPCompatibility/Sniffs/Lists/ForbiddenEmptyListAssignmentSniff.php +++ b/PHPCompatibility/Sniffs/Lists/ForbiddenEmptyListAssignmentSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Lists; @@ -16,15 +15,15 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Lists\ForbiddenEmptyListAssignmentSniff. - * - * Empty list() assignments have been removed in PHP 7.0 + * Support for empty `list()` expressions has been removed in PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list.empty + * @link https://wiki.php.net/rfc/abstract_syntax_tree#changes_to_list + * @link https://www.php.net/manual/en/function.list.php + * + * @since 7.0.0 */ class ForbiddenEmptyListAssignmentSniff extends Sniff { @@ -32,6 +31,8 @@ class ForbiddenEmptyListAssignmentSniff extends Sniff /** * List of tokens to disregard when determining whether the list() is empty. * + * @since 7.0.3 + * * @var array */ protected $ignoreTokens = array(); @@ -39,6 +40,8 @@ class ForbiddenEmptyListAssignmentSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -59,6 +62,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Lists/NewKeyedListSniff.php b/PHPCompatibility/Sniffs/Lists/NewKeyedListSniff.php index 69f91003..7111b125 100644 --- a/PHPCompatibility/Sniffs/Lists/NewKeyedListSniff.php +++ b/PHPCompatibility/Sniffs/Lists/NewKeyedListSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Lists; @@ -16,21 +15,22 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Lists\NewKeyedListSniff. - * - * "You can now specify keys in list(), or its new shorthand [] syntax. " + * Since PHP 7.1, you can specify keys in `list()`, or its new shorthand `[]` syntax. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/list_keys + * @link https://www.php.net/manual/en/function.list.php + * + * @since 9.0.0 */ class NewKeyedListSniff extends Sniff { /** * Tokens which represent the start of a list construct. * + * @since 9.0.0 + * * @var array */ protected $sniffTargets = array( @@ -41,6 +41,8 @@ class NewKeyedListSniff extends Sniff /** * The token(s) within the list construct which is being targeted. * + * @since 9.0.0 + * * @var array */ protected $targetsInList = array( @@ -53,6 +55,8 @@ class NewKeyedListSniff extends Sniff * * Set by the setUpAllTargets() method which is called from within register(). * + * @since 9.0.0 + * * @var array */ protected $allTargets; @@ -61,6 +65,8 @@ class NewKeyedListSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() @@ -73,7 +79,9 @@ public function register() /** * Prepare the $allTargets array only once. * - * @return array + * @since 9.0.0 + * + * @return void */ public function setUpAllTargets() { @@ -83,6 +91,8 @@ public function setUpAllTargets() /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -93,6 +103,8 @@ protected function bowOutEarly() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -146,6 +158,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Examine the contents of a list construct to determine whether an error needs to be thrown. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $opener The position of the list open token. * @param int $closer The position of the list close token. @@ -170,6 +184,8 @@ protected function examineList(File $phpcsFile, $opener, $closer) * * Skips past nested list constructs, so these can be examined based on their own token. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start The position of the list open token or a token * within the list to start (resume) the examination from. diff --git a/PHPCompatibility/Sniffs/Lists/NewListReferenceAssignmentSniff.php b/PHPCompatibility/Sniffs/Lists/NewListReferenceAssignmentSniff.php index 8f71e96b..ebcd7c8e 100644 --- a/PHPCompatibility/Sniffs/Lists/NewListReferenceAssignmentSniff.php +++ b/PHPCompatibility/Sniffs/Lists/NewListReferenceAssignmentSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Lists; @@ -19,15 +18,19 @@ * * PHP version 7.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.core.destruct-reference + * @link https://wiki.php.net/rfc/list_reference_assignment + * @link https://www.php.net/manual/en/function.list.php + * + * @since 9.0.0 */ class NewListReferenceAssignmentSniff extends NewKeyedListSniff { /** * The token(s) within the list construct which is being targeted. * + * @since 9.0.0 + * * @var array */ protected $targetsInList = array( @@ -37,6 +40,8 @@ class NewListReferenceAssignmentSniff extends NewKeyedListSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -47,6 +52,8 @@ protected function bowOutEarly() /** * Examine the contents of a list construct to determine whether an error needs to be thrown. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $opener The position of the list open token. * @param int $closer The position of the list close token. @@ -55,7 +62,7 @@ protected function bowOutEarly() */ protected function examineList(File $phpcsFile, $opener, $closer) { - $start = $opener; + $start = $opener; while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) { $phpcsFile->addError( 'Reference assignments within list constructs are not supported in PHP 7.2 or earlier.', diff --git a/PHPCompatibility/Sniffs/Lists/NewShortListSniff.php b/PHPCompatibility/Sniffs/Lists/NewShortListSniff.php index 1b6cd27b..ee100fbe 100644 --- a/PHPCompatibility/Sniffs/Lists/NewShortListSniff.php +++ b/PHPCompatibility/Sniffs/Lists/NewShortListSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Lists; @@ -15,17 +14,18 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Lists\NewShortListSniff. + * Detect short list syntax for symmetric array destructuring. * - * "The shorthand array syntax ([]) may now be used to destructure arrays for - * assignments (including within foreach), as an alternative to the existing - * list() syntax, which is still supported." + * "The shorthand array syntax (`[]`) may now be used to destructure arrays for + * assignments (including within `foreach`), as an alternative to the existing + * `list()` syntax, which is still supported." * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.symmetric-array-destructuring + * @link https://wiki.php.net/rfc/short_list_syntax + * + * @since 9.0.0 */ class NewShortListSniff extends Sniff { @@ -33,6 +33,8 @@ class NewShortListSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() @@ -43,11 +45,14 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. * - * @return void + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. */ public function process(File $phpcsFile, $stackPtr) { diff --git a/PHPCompatibility/Sniffs/MethodUse/ForbiddenToStringParametersSniff.php b/PHPCompatibility/Sniffs/MethodUse/ForbiddenToStringParametersSniff.php new file mode 100644 index 00000000..d580bac7 --- /dev/null +++ b/PHPCompatibility/Sniffs/MethodUse/ForbiddenToStringParametersSniff.php @@ -0,0 +1,103 @@ +supportsAbove('5.3') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) { + /* + * Not a method call. + * + * Note: This disregards method calls with the method name in a variable, like: + * $method = '__toString'; + * $obj->$method(); + * However, that would be very hard to examine reliably anyway. + */ + return; + } + + if (strtolower($tokens[$nextNonEmpty]['content']) !== '__tostring') { + // Not a call to the __toString() method. + return; + } + + $openParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true); + if ($openParens === false || $tokens[$openParens]['code'] !== \T_OPEN_PARENTHESIS) { + // Not a method call. + return; + } + + $closeParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($openParens + 1), null, true); + if ($closeParens === false || $tokens[$closeParens]['code'] === \T_CLOSE_PARENTHESIS) { + // Not a method call. + return; + } + + // If we're still here, then this is a call to the __toString() magic method passing parameters. + $phpcsFile->addError( + 'The __toString() magic method will no longer accept passed arguments since PHP 5.3', + $stackPtr, + 'Passed' + ); + } +} diff --git a/PHPCompatibility/Sniffs/MethodUse/NewDirectCallsToCloneSniff.php b/PHPCompatibility/Sniffs/MethodUse/NewDirectCallsToCloneSniff.php index 93f09497..873ef1d2 100644 --- a/PHPCompatibility/Sniffs/MethodUse/NewDirectCallsToCloneSniff.php +++ b/PHPCompatibility/Sniffs/MethodUse/NewDirectCallsToCloneSniff.php @@ -3,7 +3,7 @@ * PHPCompatibility, an external standard for PHP_CodeSniffer. * * @package PHPCompatibility - * @copyright 2012-2018 PHPCompatibility Contributors + * @copyright 2012-2019 PHPCompatibility Contributors * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 * @link https://github.com/PHPCompatibility/PHPCompatibility */ @@ -15,21 +15,35 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * Detect direct calls to the __clone() magic method which is allowed since PHP 7.0. + * Detect direct calls to the `__clone()` magic method, which is allowed since PHP 7.0. * - * "Doing calls like $obj->__clone() is now allowed. This was the only magic method + * "Doing calls like `$obj->__clone()` is now allowed. This was the only magic method * that had a compile-time check preventing some calls to it, which doesn't make sense. * If we allow all other magic methods to be called, there's no reason to forbid this one." * * PHP version 7.0 * * @link https://wiki.php.net/rfc/abstract_syntax_tree#directly_calling_clone_is_allowed + * @link https://www.php.net/manual/en/language.oop5.cloning.php * * @since 9.1.0 */ class NewDirectCallsToCloneSniff extends Sniff { + /** + * Tokens which indicate class internal use. + * + * @since 9.3.2 + * + * @var array + */ + protected $classInternal = array( + \T_PARENT => true, + \T_SELF => true, + \T_STATIC => true, + ); + /** * Returns an array of tokens this test wants to listen for. * @@ -88,6 +102,12 @@ public function process(File $phpcsFile, $stackPtr) return; } + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + if ($prevNonEmpty === false || isset($this->classInternal[$tokens[$prevNonEmpty]['code']])) { + // Class internal call to __clone(). + return; + } + $phpcsFile->addError( 'Direct calls to the __clone() magic method are not allowed in PHP 5.6 or earlier.', $nextNonEmpty, diff --git a/PHPCompatibility/Sniffs/Miscellaneous/NewPHPOpenTagEOFSniff.php b/PHPCompatibility/Sniffs/Miscellaneous/NewPHPOpenTagEOFSniff.php new file mode 100644 index 00000000..85e10bcf --- /dev/null +++ b/PHPCompatibility/Sniffs/Miscellaneous/NewPHPOpenTagEOFSniff.php @@ -0,0 +1,147 @@ + ` interpreted as an opening PHP tag. Previously it was interpreted either as + * > ` interpreted as a literal `shortOpenTags = (bool) ini_get('short_open_tag'); + if ($this->shortOpenTags === false) { + $targets[] = \T_INLINE_HTML; + } else { + $targets[] = \T_STRING; + } + + if (version_compare(\PHP_VERSION_ID, '70399', '>')) { + $targets[] = \T_OPEN_TAG; + } + + return $targets; + } + + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsBelow('7.3') === false) { + return; + } + + if ($stackPtr !== ($phpcsFile->numTokens - 1)) { + // We're only interested in the last token in the file. + return; + } + + $tokens = $phpcsFile->getTokens(); + $contents = $tokens[$stackPtr]['content']; + $error = false; + + switch ($tokens[$stackPtr]['code']) { + case \T_INLINE_HTML: + // PHP < 7.4 with short open tags off. + if ($contents === 'addError( + 'A PHP open tag at the end of a file, without trailing newline, was not supported in PHP 7.3 or earlier and would result in a syntax error or be interpreted as a literal string', + $stackPtr, + 'Found' + ); + } + } +} diff --git a/PHPCompatibility/Sniffs/Miscellaneous/RemovedAlternativePHPTagsSniff.php b/PHPCompatibility/Sniffs/Miscellaneous/RemovedAlternativePHPTagsSniff.php index 1697d4fd..47143a3d 100644 --- a/PHPCompatibility/Sniffs/Miscellaneous/RemovedAlternativePHPTagsSniff.php +++ b/PHPCompatibility/Sniffs/Miscellaneous/RemovedAlternativePHPTagsSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Miscellaneous; @@ -15,18 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Miscellaneous\RemovedAlternativePHPTags. + * Check for use of alternative PHP tags, support for which was removed in PHP 7.0. * - * Check for usage of alternative PHP tags - removed in PHP 7.0. + * {@internal Based on `Generic.PHP.DisallowAlternativePHPTags` by Juliette Reinders Folmer + * (with permission) which was merged into PHPCS 2.7.0.} * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/remove_alternative_php_tags + * @link https://www.php.net/manual/en/language.basic-syntax.phptags.php * - * Based on `Generic_Sniffs_PHP_DisallowAlternativePHPTags` by Juliette Reinders Folmer - * which was merged into PHPCS 2.7.0. + * @since 7.0.4 */ class RemovedAlternativePHPTagsSniff extends Sniff { @@ -34,6 +32,8 @@ class RemovedAlternativePHPTagsSniff extends Sniff /** * Whether ASP tags are enabled or not. * + * @since 7.0.4 + * * @var bool */ private $aspTags = false; @@ -41,6 +41,8 @@ class RemovedAlternativePHPTagsSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.4 + * * @return array */ public function register() @@ -61,6 +63,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -123,7 +127,7 @@ public function process(File $phpcsFile, $stackPtr) return; } - // If we're still here, we can't be sure if what we find was really intended as ASP open tags. + // If we're still here, we can't be sure if what we found was really intended as ASP open tags. if ($openTag['code'] === \T_INLINE_HTML && $this->aspTags === false) { if (strpos($content, '<%') !== false) { $error = 'Possible use of ASP style opening tags detected. ASP style opening tags have been removed in PHP 7.0. Found: %s'; @@ -139,6 +143,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get a snippet from a HTML token. * + * @since 7.0.4 + * * @param string $content The content of the HTML token. * @param string $startAt Partial string to use as a starting point for the snippet. * @param int $length The target length of the snippet to get. Defaults to 25. @@ -152,12 +158,12 @@ protected function getSnippet($content, $startAt = '', $length = 25) if ($startAt !== '') { $startPos = strpos($content, $startAt); if ($startPos !== false) { - $startPos += strlen($startAt); + $startPos += \strlen($startAt); } } $snippet = substr($content, $startPos, $length); - if ((strlen($content) - $startPos) > $length) { + if ((\strlen($content) - $startPos) > $length) { $snippet .= '...'; } diff --git a/PHPCompatibility/Sniffs/Miscellaneous/ValidIntegersSniff.php b/PHPCompatibility/Sniffs/Miscellaneous/ValidIntegersSniff.php index 504554e3..53dce438 100644 --- a/PHPCompatibility/Sniffs/Miscellaneous/ValidIntegersSniff.php +++ b/PHPCompatibility/Sniffs/Miscellaneous/ValidIntegersSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Miscellaneous; @@ -13,11 +14,24 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Miscellaneous\ValidIntegersSniff. + * Check for valid integer types and values. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * Checks: + * - PHP 5.4 introduced binary integers. + * - PHP 7.0 removed tolerance for invalid octals. These were truncated prior to PHP 7 + * and give a parse error since PHP 7. + * - PHP 7.0 removed support for recognizing hexadecimal numeric strings as numeric. + * Type juggling and recognition was inconsistent prior to PHP 7. As of PHP 7, they + * are no longer treated as numeric. + * + * PHP version 5.4+ + * + * @link https://wiki.php.net/rfc/binnotation4ints + * @link https://wiki.php.net/rfc/remove_hex_support_in_numeric_strings + * @link https://www.php.net/manual/en/language.types.integer.php + * + * @since 7.0.3 + * @since 7.0.8 This sniff now throws a warning instead of an error for invalid binary integers. */ class ValidIntegersSniff extends Sniff { @@ -25,6 +39,8 @@ class ValidIntegersSniff extends Sniff /** * Whether PHPCS is run on a PHP < 5.4. * + * @since 7.0.3 + * * @var bool */ protected $isLowPHPVersion = false; @@ -32,6 +48,8 @@ class ValidIntegersSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.3 + * * @return array */ public function register() @@ -48,6 +66,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack. @@ -108,7 +128,9 @@ public function process(File $phpcsFile, $stackPtr) /** - * Could the current token an potentially be a binary integer ? + * Could the current token potentially be a binary integer ? + * + * @since 7.0.3 * * @param array $tokens Token stack. * @param int $stackPtr The current position in the token stack. @@ -124,18 +146,20 @@ private function couldBeBinaryInteger($tokens, $stackPtr) } if ($this->isLowPHPVersion === false) { - return (preg_match('`^0b[0-1]+$`D', $token['content']) === 1); + return (preg_match('`^0b[0-1]+$`iD', $token['content']) === 1); } // Pre-5.4, binary strings are tokenized as T_LNUMBER (0) + T_STRING ("b01010101"). // At this point, we don't yet care whether it's a valid binary int, that's a separate check. else { - return($token['content'] === '0' && $tokens[$stackPtr + 1]['code'] === \T_STRING && preg_match('`^b[0-9]+$`D', $tokens[$stackPtr + 1]['content']) === 1); + return($token['content'] === '0' && $tokens[$stackPtr + 1]['code'] === \T_STRING && preg_match('`^b[0-9]+$`iD', $tokens[$stackPtr + 1]['content']) === 1); } } /** * Is the current token an invalid binary integer ? * + * @since 7.0.3 + * * @param array $tokens Token stack. * @param int $stackPtr The current position in the token stack. * @@ -151,13 +175,15 @@ private function isInvalidBinaryInteger($tokens, $stackPtr) // If it's an invalid binary int, the token will be split into two T_LNUMBER tokens. return ($tokens[$stackPtr + 1]['code'] === \T_LNUMBER); } else { - return (preg_match('`^b[0-1]+$`D', $tokens[$stackPtr + 1]['content']) === 0); + return (preg_match('`^b[0-1]+$`iD', $tokens[$stackPtr + 1]['content']) === 0); } } /** * Retrieve the content of the tokens which together look like a binary integer. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param array $tokens Token stack. * @param int $stackPtr The position of the current token in @@ -183,6 +209,8 @@ private function getBinaryInteger(File $phpcsFile, $tokens, $stackPtr) /** * Is the current token an invalid octal integer ? * + * @since 7.0.3 + * * @param array $tokens Token stack. * @param int $stackPtr The current position in the token stack. * @@ -202,6 +230,8 @@ private function isInvalidOctalInteger($tokens, $stackPtr) /** * Is the current token a hexidecimal numeric string ? * + * @since 7.0.3 + * * @param array $tokens Token stack. * @param int $stackPtr The current position in the token stack. * diff --git a/PHPCompatibility/Sniffs/Operators/ChangedConcatOperatorPrecedenceSniff.php b/PHPCompatibility/Sniffs/Operators/ChangedConcatOperatorPrecedenceSniff.php new file mode 100644 index 00000000..3dfe738c --- /dev/null +++ b/PHPCompatibility/Sniffs/Operators/ChangedConcatOperatorPrecedenceSniff.php @@ -0,0 +1,199 @@ +>` operators. + * + * As of PHP 7.4, a deprecation warning will be thrown upon encountering an + * unparenthesized expression containing an `.` before a `+` or `-`. + * + * PHP version 7.4 + * PHP version 8.0 + * + * @link https://wiki.php.net/rfc/concatenation_precedence + * @link https://www.php.net/manual/en/language.operators.precedence.php + * + * @since 9.2.0 + */ +class ChangedConcatOperatorPrecedenceSniff extends Sniff +{ + + /** + * List of tokens with a lower operator precedence than concatenation in PHP >= 8.0. + * + * @since 9.2.0 + * + * @var array + */ + private $tokensWithLowerPrecedence = array( + 'T_BITWISE_AND' => true, + 'T_BITWISE_XOR' => true, + 'T_BITWISE_OR' => true, + 'T_COALESCE' => true, + 'T_INLINE_THEN' => true, + 'T_INLINE_ELSE' => true, + 'T_YIELD_FROM' => true, + 'T_YIELD' => true, + ); + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + return array( + \T_PLUS, + \T_MINUS, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('7.4') === false) { + return; + } + + if ($this->isUnaryPlusMinus($phpcsFile, $stackPtr) === true) { + return; + } + + $tokens = $phpcsFile->getTokens(); + + for ($i = ($stackPtr - 1); $stackPtr >= 0; $i--) { + if ($tokens[$i]['code'] === \T_STRING_CONCAT) { + // Found one. + break; + } + + if ($tokens[$i]['code'] === \T_SEMICOLON + || $tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET + || $tokens[$i]['code'] === \T_OPEN_TAG + || $tokens[$i]['code'] === \T_OPEN_TAG_WITH_ECHO + || $tokens[$i]['code'] === \T_COMMA + || $tokens[$i]['code'] === \T_COLON + || $tokens[$i]['code'] === \T_CASE + ) { + // If we reached any of the above tokens, we've reached the end of + // the statement without encountering a concatenation operator. + return; + } + + if ($tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET + && isset($tokens[$i]['bracket_closer']) + && $tokens[$i]['bracket_closer'] > $stackPtr + ) { + // No need to look any further, this is plus/minus within curly braces + // and we've reached the open curly. + return; + } + + if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_closer']) + && $tokens[$i]['parenthesis_closer'] > $stackPtr + ) { + // No need to look any further, this is plus/minus within parenthesis + // and we've reached the open parenthesis. + return; + } + + if (($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY + || $tokens[$i]['code'] === \T_OPEN_SQUARE_BRACKET) + && isset($tokens[$i]['bracket_closer']) + && $tokens[$i]['bracket_closer'] > $stackPtr + ) { + // No need to look any further, this is plus/minus within a short array + // or array key square brackets and we've reached the opener. + return; + } + + if ($tokens[$i]['code'] === \T_CLOSE_CURLY_BRACKET) { + if (isset($tokens[$i]['scope_owner'])) { + // Different scope, we've passed the start of the statement. + return; + } + + if (isset($tokens[$i]['bracket_opener'])) { + $i = $tokens[$i]['bracket_opener']; + } + + continue; + } + + if ($tokens[$i]['code'] === \T_CLOSE_PARENTHESIS + && isset($tokens[$i]['parenthesis_opener']) + ) { + // Skip over statements in parenthesis, including long arrays. + $i = $tokens[$i]['parenthesis_opener']; + continue; + } + + if (($tokens[$i]['code'] === \T_CLOSE_SQUARE_BRACKET + || $tokens[$i]['code'] === \T_CLOSE_SHORT_ARRAY) + && isset($tokens[$i]['bracket_opener']) + ) { + // Skip over array keys and short arrays. + $i = $tokens[$i]['bracket_opener']; + continue; + } + + // Check for chain being broken by a token with a lower precedence. + if (isset(Tokens::$booleanOperators[$tokens[$i]['code']]) === true + || isset(Tokens::$assignmentTokens[$tokens[$i]['code']]) === true + ) { + return; + } + + if (isset($this->tokensWithLowerPrecedence[$tokens[$i]['type']]) === true) { + if ($tokens[$i]['code'] === \T_BITWISE_AND + && $phpcsFile->isReference($i) === true + ) { + continue; + } + + return; + } + } + + $message = 'Using an unparenthesized expression containing a "." before a "+" or "-" has been deprecated in PHP 7.4'; + $isError = false; + if ($this->supportsAbove('8.0') === true) { + $message .= ' and removed in PHP 8.0'; + $isError = true; + } + + $this->addMessage($phpcsFile, $message, $i, $isError); + } +} diff --git a/PHPCompatibility/Sniffs/Operators/ForbiddenNegativeBitshiftSniff.php b/PHPCompatibility/Sniffs/Operators/ForbiddenNegativeBitshiftSniff.php index ef55285f..740e7dfd 100644 --- a/PHPCompatibility/Sniffs/Operators/ForbiddenNegativeBitshiftSniff.php +++ b/PHPCompatibility/Sniffs/Operators/ForbiddenNegativeBitshiftSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Operators; @@ -17,15 +16,14 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Operators\ForbiddenNegativeBitshift. - * - * Bitwise shifts by negative number will throw an ArithmeticError in PHP 7.0. + * Bitwise shifts by negative number will throw an ArithmeticError since PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://wiki.php.net/rfc/integer_semantics + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.integers.negative-bitshift + * + * @since 7.0.0 */ class ForbiddenNegativeBitshiftSniff extends Sniff { @@ -34,7 +32,9 @@ class ForbiddenNegativeBitshiftSniff extends Sniff * * {@internal The PHPCS `findEndOfStatement()` method is not completely consistent * in how it returns the statement end. This is just a simple way to bypass - * the inconsistency for our purposes.}} + * the inconsistency for our purposes.} + * + * @since 8.2.0 * * @var array */ @@ -48,6 +48,9 @@ class ForbiddenNegativeBitshiftSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * @since 8.2.0 Now registers all bitshift tokens, not just bitshift right (`T_SR`). + * * @return array */ public function register() @@ -63,6 +66,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Operators/NewOperatorsSniff.php b/PHPCompatibility/Sniffs/Operators/NewOperatorsSniff.php index 85616d0f..7c50ad86 100644 --- a/PHPCompatibility/Sniffs/Operators/NewOperatorsSniff.php +++ b/PHPCompatibility/Sniffs/Operators/NewOperatorsSniff.php @@ -1,11 +1,11 @@ - * @copyright 2013 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Operators; @@ -14,12 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Operators\NewOperatorsSniff. + * Detect use of new PHP operators. * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2013 Cu.be Solutions bvba + * PHP version All + * + * @link https://wiki.php.net/rfc/pow-operator + * @link https://wiki.php.net/rfc/combined-comparison-operator + * @link https://wiki.php.net/rfc/isset_ternary + * @link https://wiki.php.net/rfc/null_coalesce_equal_operator + * + * @since 9.0.0 Detection of new operators was originally included in the + * `NewLanguageConstruct` sniff (since 5.6). */ class NewOperatorsSniff extends AbstractNewFeatureSniff { @@ -28,9 +33,11 @@ class NewOperatorsSniff extends AbstractNewFeatureSniff * A list of new operators, not present in older versions. * * The array lists : version number with false (not present) or true (present). - * If's sufficient to list the first version where the keyword appears. + * If's sufficient to list the first version where the operator appears. + * + * @since 5.6 * - * @var array(string => array(string => int|string|null)) + * @var array(string => array(string => bool|string)) */ protected $newOperators = array( 'T_POW' => array( @@ -53,10 +60,6 @@ class NewOperatorsSniff extends AbstractNewFeatureSniff '7.0' => true, 'description' => 'null coalescing operator (??)', ), // Identified in PHP < 7.0 icw PHPCS < 2.6.2 as T_INLINE_THEN + T_INLINE_THEN. - /* - * Was slated for 7.2, but still not implemented. PHPCS however does already tokenize it. - * @link https://wiki.php.net/rfc/null_coalesce_equal_operator - */ 'T_COALESCE_EQUAL' => array( '7.3' => false, '7.4' => true, @@ -70,6 +73,8 @@ class NewOperatorsSniff extends AbstractNewFeatureSniff * * The array lists an alternative token to listen for. * + * @since 7.0.3 + * * @var array(string => int) */ protected $newOperatorsPHPCSCompat = array( @@ -93,7 +98,9 @@ class NewOperatorsSniff extends AbstractNewFeatureSniff * PHP and PHPCS versions. * * {@internal 'before' was chosen rather than 'after' as that allowed for a 1-on-1 - * translation list with the current tokens.}} + * translation list with the current tokens.} + * + * @since 7.0.3 * * @var array(string => array(string => string)) */ @@ -123,13 +130,15 @@ class NewOperatorsSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.6 + * * @return array */ public function register() { $tokens = array(); foreach ($this->newOperators as $token => $versions) { - if (defined($token)) { + if (\defined($token)) { $tokens[] = constant($token); } elseif (isset($this->newOperatorsPHPCSCompat[$token])) { $tokens[] = $this->newOperatorsPHPCSCompat[$token]; @@ -142,6 +151,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.6 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -159,7 +170,7 @@ public function process(File $phpcsFile, $stackPtr) && ((isset($this->PHPCSCompatTranslate[$tokenType]['before'], $tokens[$stackPtr - 1]) === true && $tokens[$stackPtr - 1]['type'] === $this->PHPCSCompatTranslate[$tokenType]['before']) || (isset($this->PHPCSCompatTranslate[$tokenType]['callback']) === true - && call_user_func(array($this, $this->PHPCSCompatTranslate[$tokenType]['callback']), $tokens, $stackPtr) === true)) + && \call_user_func(array($this, $this->PHPCSCompatTranslate[$tokenType]['callback']), $tokens, $stackPtr) === true)) ) { $tokenType = $this->PHPCSCompatTranslate[$tokenType]['real_token']; } @@ -186,6 +197,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -199,6 +212,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 7.1.0 + * * @return array */ protected function getNonVersionArrayKeys() @@ -210,6 +225,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 7.1.0 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -225,7 +242,9 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** - * Allow for concrete child classes to filter the error data before it's passed to PHPCS. + * Filter the error data before it's passed to PHPCS. + * + * @since 7.1.0 * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. @@ -243,6 +262,8 @@ protected function filterErrorData(array $data, array $itemInfo, array $errorInf /** * Callback function to determine whether a T_EQUAL token is really a T_COALESCE_EQUAL token. * + * @since 7.1.2 + * * @param array $tokens The token stack. * @param int $stackPtr The current position in the token stack. * @@ -251,7 +272,7 @@ protected function filterErrorData(array $data, array $itemInfo, array $errorInf private function isTCoalesceEqual($tokens, $stackPtr) { if ($tokens[$stackPtr]['code'] !== \T_EQUAL || isset($tokens[($stackPtr - 1)]) === false) { - // Function called for wrong token or token has no predecesor. + // Function called for wrong token or token has no predecessor. return false; } @@ -270,6 +291,8 @@ private function isTCoalesceEqual($tokens, $stackPtr) /** * Callback function to determine whether a T_INLINE_THEN token is really a T_COALESCE token. * + * @since 7.1.2 + * * @param array $tokens The token stack. * @param int $stackPtr The current position in the token stack. * @@ -278,7 +301,7 @@ private function isTCoalesceEqual($tokens, $stackPtr) private function isTCoalesce($tokens, $stackPtr) { if ($tokens[$stackPtr]['code'] !== \T_INLINE_THEN || isset($tokens[($stackPtr - 1)]) === false) { - // Function called for wrong token or token has no predecesor. + // Function called for wrong token or token has no predecessor. return false; } diff --git a/PHPCompatibility/Sniffs/Operators/NewShortTernarySniff.php b/PHPCompatibility/Sniffs/Operators/NewShortTernarySniff.php index 9ebbcdc3..b65da597 100644 --- a/PHPCompatibility/Sniffs/Operators/NewShortTernarySniff.php +++ b/PHPCompatibility/Sniffs/Operators/NewShortTernarySniff.php @@ -1,33 +1,32 @@ - * @copyright 2012 Ben Selby + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Operators; use PHPCompatibility\Sniff; use PHP_CodeSniffer_File as File; -use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Operators\NewShortTernarySniff. + * Detect usage of the short ternary (elvis) operator as introduced in PHP 5.3. * * Performs checks on ternary operators, specifically that the middle expression * is not omitted for versions that don't support this. * * PHP version 5.3 * - * @category PHP - * @package PHPCompatibility - * @author Ben Selby - * @copyright 2012 Ben Selby + * @link https://www.php.net/manual/en/migration53.new-features.php + * @link https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary + * + * @since 7.0.0 + * @since 7.0.8 This sniff now throws an error instead of a warning. + * @since 9.0.0 Renamed from `TernaryOperatorsSniff` to `NewShortTernarySniff`. */ class NewShortTernarySniff extends Sniff { @@ -35,6 +34,8 @@ class NewShortTernarySniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -45,6 +46,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -57,18 +60,14 @@ public function process(File $phpcsFile, $stackPtr) return; } - $tokens = $phpcsFile->getTokens(); - - // Get next non-whitespace token, and check it isn't the related inline else - // symbol, which is not allowed prior to PHP 5.3. - $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - - if ($next !== false && $tokens[$next]['code'] === \T_INLINE_ELSE) { - $phpcsFile->addError( - 'Middle may not be omitted from ternary operators in PHP < 5.3', - $stackPtr, - 'MiddleMissing' - ); + if ($this->isShortTernary($phpcsFile, $stackPtr) === false) { + return; } + + $phpcsFile->addError( + 'Middle may not be omitted from ternary operators in PHP < 5.3', + $stackPtr, + 'MiddleMissing' + ); } } diff --git a/PHPCompatibility/Sniffs/Operators/RemovedTernaryAssociativitySniff.php b/PHPCompatibility/Sniffs/Operators/RemovedTernaryAssociativitySniff.php new file mode 100644 index 00000000..21eca02b --- /dev/null +++ b/PHPCompatibility/Sniffs/Operators/RemovedTernaryAssociativitySniff.php @@ -0,0 +1,157 @@ + true, + 'T_YIELD' => true, + 'T_LOGICAL_AND' => true, + 'T_LOGICAL_OR' => true, + 'T_LOGICAL_XOR' => true, + ); + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.2.0 + * + * @return array + */ + public function register() + { + return array(\T_INLINE_THEN); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.2.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('7.4') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $endOfStatement = PHPCSHelper::findEndOfStatement($phpcsFile, $stackPtr); + if ($tokens[$endOfStatement]['code'] !== \T_SEMICOLON + && $tokens[$endOfStatement]['code'] !== \T_COLON + && $tokens[$endOfStatement]['code'] !== \T_COMMA + && $tokens[$endOfStatement]['code'] !== \T_DOUBLE_ARROW + && $tokens[$endOfStatement]['code'] !== \T_OPEN_TAG + && $tokens[$endOfStatement]['code'] !== \T_CLOSE_TAG + ) { + // End of statement is last non-empty before close brace, so make sure we examine that token too. + ++$endOfStatement; + } + + $ternaryCount = 0; + $elseCount = 0; + $shortTernaryCount = 0; + + // Start at $stackPtr so we don't need duplicate code for short ternary determination. + for ($i = $stackPtr; $i < $endOfStatement; $i++) { + if (($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY + || $tokens[$i]['code'] === \T_OPEN_SQUARE_BRACKET + || $tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET) + && isset($tokens[$i]['bracket_closer']) + ) { + // Skip over short arrays, array access keys and curlies. + $i = $tokens[$i]['bracket_closer']; + continue; + } + + if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_closer']) + ) { + // Skip over anything between parentheses. + $i = $tokens[$i]['parenthesis_closer']; + continue; + } + + // Check for operators with lower operator precedence. + if (isset(Tokens::$assignmentTokens[$tokens[$i]['code']]) + || isset($this->tokensWithLowerPrecedence[$tokens[$i]['code']]) + ) { + break; + } + + if ($tokens[$i]['code'] === \T_INLINE_THEN) { + ++$ternaryCount; + + if ($this->isShortTernary($phpcsFile, $i) === true) { + ++$shortTernaryCount; + } + + continue; + } + + if ($tokens[$i]['code'] === \T_INLINE_ELSE) { + if (($ternaryCount - $elseCount) >= 2) { + // This is the `else` for a ternary in the middle part of a previous ternary. + --$ternaryCount; + } else { + ++$elseCount; + } + continue; + } + } + + if ($ternaryCount > 1 && $ternaryCount === $elseCount && $ternaryCount > $shortTernaryCount) { + $message = 'The left-associativity of the ternary operator has been deprecated in PHP 7.4'; + $isError = false; + if ($this->supportsAbove('8.0') === true) { + $message .= ' and removed in PHP 8.0'; + $isError = true; + } + + $message .= '. Multiple consecutive ternaries detected. Use parenthesis to clarify the order in which the operations should be executed'; + + $this->addMessage($phpcsFile, $message, $stackPtr, $isError); + } + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/ForbiddenGetClassNullSniff.php b/PHPCompatibility/Sniffs/ParameterValues/ForbiddenGetClassNullSniff.php index 7e3f9308..30452a09 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/ForbiddenGetClassNullSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/ForbiddenGetClassNullSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -15,16 +14,15 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\ForbiddenGetClassNullSniff. - * - * Detect: Passing `null` to get_class() is no longer allowed as of PHP 7.2. - * This will now result in an E_WARNING being thrown. + * Detect: Passing `null` to `get_class()` is no longer allowed as of PHP 7.2. + * This will now result in an `E_WARNING` being thrown. * * PHP version 7.2 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/get_class_disallow_null_parameter + * @link https://www.php.net/manual/en/function.get-class.php#refsect1-function.get-class-changelog + * + * @since 9.0.0 */ class ForbiddenGetClassNullSniff extends AbstractFunctionCallParameterSniff { @@ -32,6 +30,8 @@ class ForbiddenGetClassNullSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -42,6 +42,8 @@ class ForbiddenGetClassNullSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -53,6 +55,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLSniff.php b/PHPCompatibility/Sniffs/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLSniff.php new file mode 100644 index 00000000..9ecae5c8 --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLSniff.php @@ -0,0 +1,113 @@ + true, + ); + + /** + * Text string tokens to examine. + * + * @since 9.3.0 + * + * @var array + */ + private $textStringTokens = array( + \T_CONSTANT_ENCAPSED_STRING => true, + \T_DOUBLE_QUOTED_STRING => true, + \T_INLINE_HTML => true, + \T_HEREDOC => true, + \T_NOWDOC => true, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return ($this->supportsAbove('5.4') === false); + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + if (isset($parameters[2]) === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $targetParam = $parameters[2]; + for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) { + if ($tokens[$i]['code'] === \T_STRING + || $tokens[$i]['code'] === \T_VARIABLE + ) { + // Variable, constant, function call. Ignore as undetermined. + return; + } + + if (isset($this->textStringTokens[$tokens[$i]['code']]) === true + && strpos($tokens[$i]['content'], '/>') !== false + ) { + + $phpcsFile->addError( + 'Self-closing XHTML tags are ignored. Only non-self-closing tags should be used in the strip_tags() $allowable_tags parameter since PHP 5.3.4. Found: %s', + $i, + 'Found', + array($targetParam['raw']) + ); + + // Only throw one error per function call. + return; + } + } + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewArrayReduceInitialTypeSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewArrayReduceInitialTypeSniff.php index 99ddc301..8045ac8a 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/NewArrayReduceInitialTypeSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/NewArrayReduceInitialTypeSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -13,13 +14,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\NewArrayReduceInitialTypeSniff. + * In PHP 5.2 and lower, the `$initial` parameter for `array_reduce()` had to be an integer. * - * Detect: In PHP 5.2 and lower, the $initial parameter had to be an integer. + * PHP version 5.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration53.other.php#migration53.other + * @link https://www.php.net/manual/en/function.array-reduce.php#refsect1-function.array-reduce-changelog + * + * @since 9.0.0 */ class NewArrayReduceInitialTypeSniff extends AbstractFunctionCallParameterSniff { @@ -27,6 +29,8 @@ class NewArrayReduceInitialTypeSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -37,6 +41,8 @@ class NewArrayReduceInitialTypeSniff extends AbstractFunctionCallParameterSniff * Tokens which, for the purposes of this sniff, indicate that there is * a variable element to the value passed. * + * @since 9.0.0 + * * @var array */ private $variableValueTokens = array( @@ -52,6 +58,8 @@ class NewArrayReduceInitialTypeSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -63,6 +71,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewFopenModesSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewFopenModesSniff.php index 324caec9..83299822 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/NewFopenModesSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/NewFopenModesSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -13,13 +14,13 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\NewFopenModesSniff. + * Check for valid values for the `fopen()` `$mode` parameter. * - * Detect: Changes in allowed values for the fopen() $mode parameter. + * PHP version 5.2+ * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/function.fopen.php#refsect1-function.fopen-changelog + * + * @since 9.0.0 */ class NewFopenModesSniff extends AbstractFunctionCallParameterSniff { @@ -27,6 +28,8 @@ class NewFopenModesSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -37,6 +40,8 @@ class NewFopenModesSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -50,6 +55,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewHTMLEntitiesEncodingDefaultSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewHTMLEntitiesEncodingDefaultSniff.php new file mode 100644 index 00000000..18895565 --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/NewHTMLEntitiesEncodingDefaultSniff.php @@ -0,0 +1,92 @@ + 3, + 'htmlentities' => 3, + 'htmlspecialchars' => 3, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * Note: This sniff should only trigger errors when both PHP 5.3 or lower, + * as well as PHP 5.4 or higher need to be supported within the application. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return ($this->supportsBelow('5.3') === false || $this->supportsAbove('5.4') === false); + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + $functionLC = strtolower($functionName); + if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) { + return; + } + + $phpcsFile->addError( + 'The default value of the $encoding parameter for %s() was changed from ISO-8859-1 to UTF-8 in PHP 5.4. For cross-version compatibility, the $encoding parameter should be explicitly set.', + $stackPtr, + 'NotSet', + array($functionName) + ); + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewHashAlgorithmsSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewHashAlgorithmsSniff.php index e70f8195..297f84ec 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/NewHashAlgorithmsSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/NewHashAlgorithmsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -13,11 +14,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\NewHashAlgorithmsSniff. + * Detect the use of newly introduced hash algorithms. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version 5.2+ + * + * @link https://www.php.net/manual/en/function.hash-algos.php#refsect1-function.hash-algos-changelog + * + * @since 7.0.7 + * @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.. */ class NewHashAlgorithmsSniff extends AbstractNewFeatureSniff { @@ -27,6 +31,8 @@ class NewHashAlgorithmsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the hash algorithm appears. * + * @since 7.0.7 + * * @var array(string => array(string => bool)) */ protected $newAlgorithms = array( @@ -99,12 +105,18 @@ class NewHashAlgorithmsSniff extends AbstractNewFeatureSniff '7.0' => false, '7.1' => true, ), + 'crc32c' => array( + '7.3' => false, + '7.4' => true, + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.7 + * * @return array */ public function register() @@ -116,6 +128,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.7 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -125,7 +139,7 @@ public function register() public function process(File $phpcsFile, $stackPtr) { $algo = $this->getHashAlgorithmParameter($phpcsFile, $stackPtr); - if (empty($algo) || is_string($algo) === false) { + if (empty($algo) || \is_string($algo) === false) { return; } @@ -145,6 +159,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -158,6 +174,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewIDNVariantDefaultSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewIDNVariantDefaultSniff.php new file mode 100644 index 00000000..2c9748ca --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/NewIDNVariantDefaultSniff.php @@ -0,0 +1,91 @@ + 3, + 'idn_to_utf8' => 3, + ); + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * Note: This sniff should only trigger errors when both PHP 7.3 or lower, + * as well as PHP 7.4 or higher need to be supported within the application. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return ($this->supportsBelow('7.3') === false || $this->supportsAbove('7.4') === false); + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + $functionLC = strtolower($functionName); + if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) { + return; + } + + $error = 'The default value of the %s() $variant parameter has changed from INTL_IDNA_VARIANT_2003 to INTL_IDNA_VARIANT_UTS46 in PHP 7.4. For optimal cross-version compatibility, the $variant parameter should be explicitly set.'; + $phpcsFile->addError( + $error, + $stackPtr, + 'NotSet', + array($functionName) + ); + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewIconvMbstringCharsetDefaultSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewIconvMbstringCharsetDefaultSniff.php new file mode 100644 index 00000000..51cc894e --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/NewIconvMbstringCharsetDefaultSniff.php @@ -0,0 +1,231 @@ + 3, + 'iconv_mime_decode' => 3, + 'iconv_mime_encode' => 3, // Special case. + 'iconv_strlen' => 2, + 'iconv_strpos' => 4, + 'iconv_strrpos' => 3, + 'iconv_substr' => 4, + + 'mb_check_encoding' => 2, + 'mb_chr' => 2, + 'mb_convert_case' => 3, + 'mb_convert_encoding' => 3, + 'mb_convert_kana' => 3, + 'mb_decode_numericentity' => 3, + 'mb_encode_numericentity' => 3, + 'mb_ord' => 2, + 'mb_scrub' => 2, + 'mb_strcut' => 4, + 'mb_stripos' => 4, + 'mb_stristr' => 4, + 'mb_strlen' => 2, + 'mb_strpos' => 4, + 'mb_strrchr' => 4, + 'mb_strrichr' => 4, + 'mb_strripos' => 4, + 'mb_strrpos' => 4, + 'mb_strstr' => 4, + 'mb_strtolower' => 2, + 'mb_strtoupper' => 2, + 'mb_strwidth' => 2, + 'mb_substr_count' => 3, + 'mb_substr' => 4, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * Note: This sniff should only trigger errors when both PHP 5.5 or lower, + * as well as PHP 5.6 or higher need to be supported within the application. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return ($this->supportsBelow('5.5') === false || $this->supportsAbove('5.6') === false); + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + $functionLC = strtolower($functionName); + if ($functionLC === 'iconv_mime_encode') { + // Special case the iconv_mime_encode() function. + return $this->processIconvMimeEncode($phpcsFile, $stackPtr, $functionName, $parameters); + } + + if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) { + return; + } + + $paramName = '$encoding'; + if (strpos($functionLC, 'iconv_') === 0) { + $paramName = '$charset'; + } elseif ($functionLC === 'mb_convert_encoding') { + $paramName = '$from_encoding'; + } + + $error = 'The default value of the %1$s parameter for %2$s() was changed from ISO-8859-1 to UTF-8 in PHP 5.6. For cross-version compatibility, the %1$s parameter should be explicitly set.'; + $data = array( + $paramName, + $functionName, + ); + + $phpcsFile->addError($error, $stackPtr, 'NotSet', $data); + } + + /** + * Process the parameters of a matched call to the iconv_mime_encode() function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processIconvMimeEncode(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + $error = 'The default value of the %s parameter index for iconv_mime_encode() was changed from ISO-8859-1 to UTF-8 in PHP 5.6. For cross-version compatibility, the %s should be explicitly set.'; + + $functionLC = strtolower($functionName); + if (isset($parameters[$this->targetFunctions[$functionLC]]) === false) { + $phpcsFile->addError( + $error, + $stackPtr, + 'PreferencesNotSet', + array( + '$preferences[\'input/output-charset\']', + '$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes', + ) + ); + + return; + } + + $tokens = $phpcsFile->getTokens(); + $targetParam = $parameters[$this->targetFunctions[$functionLC]]; + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], ($targetParam['end'] + 1), true); + if ($firstNonEmpty === false) { + // Parse error or live coding. + return; + } + + if ($tokens[$firstNonEmpty]['code'] === \T_ARRAY + || $tokens[$firstNonEmpty]['code'] === \T_OPEN_SHORT_ARRAY + ) { + $hasInputCharset = preg_match('`([\'"])input-charset\1\s*=>`', $targetParam['raw']); + $hasOutputCharset = preg_match('`([\'"])output-charset\1\s*=>`', $targetParam['raw']); + if ($hasInputCharset === 1 && $hasOutputCharset === 1) { + // Both input as well as output charset are set. + return; + } + + if ($hasInputCharset !== 1) { + $phpcsFile->addError( + $error, + $firstNonEmpty, + 'InputPreferenceNotSet', + array( + '$preferences[\'input-charset\']', + '$preferences[\'input-charset\'] index', + ) + ); + } + + if ($hasOutputCharset !== 1) { + $phpcsFile->addError( + $error, + $firstNonEmpty, + 'OutputPreferenceNotSet', + array( + '$preferences[\'output-charset\']', + '$preferences[\'output-charset\'] index', + ) + ); + } + + return; + } + + // The $preferences parameter was passed, but it was a variable/constant/output of a function call. + $phpcsFile->addWarning( + $error, + $firstNonEmpty, + 'Undetermined', + array( + '$preferences[\'input/output-charset\']', + '$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes', + ) + ); + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewNegativeStringOffsetSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewNegativeStringOffsetSniff.php index 8ef932ae..f2aea60e 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/NewNegativeStringOffsetSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/NewNegativeStringOffsetSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -15,16 +14,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\NewNegativeStringOffsetSniff. - * - * Detect: negative string offsets as parameters passed to functions where this + * Detect negative string offsets as parameters passed to functions where this * was not allowed prior to PHP 7.1. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/negative-string-offsets + * + * @since 9.0.0 */ class NewNegativeStringOffsetSniff extends AbstractFunctionCallParameterSniff { @@ -32,6 +29,8 @@ class NewNegativeStringOffsetSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array Function name => 1-based parameter offset of the affected parameters => parameter name. */ protected $targetFunctions = array( @@ -79,6 +78,8 @@ class NewNegativeStringOffsetSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -89,6 +90,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewPCREModifiersSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewPCREModifiersSniff.php index 242286a8..65339e50 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/NewPCREModifiersSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/NewPCREModifiersSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -13,13 +14,17 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\NewPCREModifiers. + * Check for the use of newly added regex modifiers for PCRE functions. * - * Check for usage of newly added regex modifiers for PCRE functions. + * Initially just checks for the PHP 7.2 new `J` modifier. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version 7.2+ + * + * @link https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php + * @link https://www.php.net/manual/en/migration72.new-features.php#migration72.new-features.pcre + * + * @since 8.2.0 + * @since 9.0.0 Renamed from `PCRENewModifiersSniff` to `NewPCREModifiersSniff`. */ class NewPCREModifiersSniff extends RemovedPCREModifiersSniff { @@ -27,10 +32,11 @@ class NewPCREModifiersSniff extends RemovedPCREModifiersSniff /** * Functions to check for. * + * @since 8.2.0 + * * @var array */ protected $targetFunctions = array( - 'preg_replace' => true, 'preg_filter' => true, 'preg_grep' => true, 'preg_match_all' => true, @@ -47,6 +53,8 @@ class NewPCREModifiersSniff extends RemovedPCREModifiersSniff * The key should be the modifier (case-sensitive!). * The value should be the PHP version in which the modifier was introduced. * + * @since 8.2.0 + * * @var array */ protected $newModifiers = array( @@ -60,6 +68,8 @@ class NewPCREModifiersSniff extends RemovedPCREModifiersSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 8.2.0 + * * @return bool */ protected function bowOutEarly() @@ -73,6 +83,8 @@ protected function bowOutEarly() /** * Examine the regex modifier string. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewPackFormatSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewPackFormatSniff.php index 6dd585dd..7e42f3e9 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/NewPackFormatSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/NewPackFormatSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -13,13 +14,13 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\NewPackFormatSniff. + * Check for valid values for the `$format` passed to `pack()`. * - * Detect: Changes in the allowed values for $format passed to pack(). + * PHP version 5.4+ * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/function.pack.php#refsect1-function.pack-changelog + * + * @since 9.0.0 */ class NewPackFormatSniff extends AbstractFunctionCallParameterSniff { @@ -27,6 +28,8 @@ class NewPackFormatSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -36,6 +39,8 @@ class NewPackFormatSniff extends AbstractFunctionCallParameterSniff /** * List of new format character codes added to pack(). * + * @since 9.0.0 + * * @var array Regex pattern => Version array. */ protected $newFormats = array( @@ -57,6 +62,8 @@ class NewPackFormatSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -68,6 +75,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewPasswordAlgoConstantValuesSniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewPasswordAlgoConstantValuesSniff.php new file mode 100644 index 00000000..94a1360d --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/NewPasswordAlgoConstantValuesSniff.php @@ -0,0 +1,125 @@ + 2, + 'password_needs_rehash' => 2, + ); + + /** + * Tokens types which indicate that the parameter passed is not the PHP native constant. + * + * @since 9.3.0 + * + * @var array + */ + private $invalidTokenTypes = array( + \T_NULL => true, + \T_TRUE => true, + \T_FALSE => true, + \T_LNUMBER => true, + \T_DNUMBER => true, + \T_CONSTANT_ENCAPSED_STRING => true, + \T_DOUBLE_QUOTED_STRING => true, + \T_HEREDOC => true, + \T_NOWDOC => true, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return ($this->supportsAbove('7.4') === false); + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + $functionLC = strtolower($functionName); + if (isset($parameters[$this->targetFunctions[$functionLC]]) === false) { + return; + } + + $targetParam = $parameters[$this->targetFunctions[$functionLC]]; + $tokens = $phpcsFile->getTokens(); + + for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) { + if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { + continue; + } + + if (isset($this->invalidTokenTypes[$tokens[$i]['code']]) === true) { + $phpcsFile->addWarning( + 'The value of the password hash algorithm constants has changed in PHP 7.4. Pass a PHP native constant to the %s() function instead of using the value of the constant. Found: %s', + $stackPtr, + 'NotAlgoConstant', + array( + $functionName, + $targetParam['raw'], + ) + ); + + break; + } + } + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewProcOpenCmdArraySniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewProcOpenCmdArraySniff.php new file mode 100644 index 00000000..4e9f1500 --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/NewProcOpenCmdArraySniff.php @@ -0,0 +1,136 @@ + true, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return false; + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + if (isset($parameters[1]) === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $targetParam = $parameters[1]; + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], $targetParam['end'], true); + + if ($nextNonEmpty === false) { + // Shouldn't be possible. + return; + } + + if ($tokens[$nextNonEmpty]['code'] !== \T_ARRAY + && $tokens[$nextNonEmpty]['code'] !== \T_OPEN_SHORT_ARRAY + ) { + // Not passed as an array. + return; + } + + if ($this->supportsBelow('7.3') === true) { + $phpcsFile->addError( + 'The proc_open() function did not accept $cmd to be passed in array format in PHP 7.3 and earlier.', + $nextNonEmpty, + 'Found' + ); + } + + if ($this->supportsAbove('7.4') === true) { + if (strpos($targetParam['raw'], 'escapeshellarg(') === false) { + // Efficiency: prevent needlessly walking the array. + return; + } + + $items = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty); + + if (empty($items)) { + return; + } + + foreach ($items as $item) { + for ($i = $item['start']; $i <= $item['end']; $i++) { + if ($tokens[$i]['code'] !== \T_STRING + || $tokens[$i]['content'] !== 'escapeshellarg' + ) { + continue; + } + + // @todo Potential future enhancement: check if it's a call to the PHP native function. + + $phpcsFile->addWarning( + 'When passing proc_open() the $cmd parameter as an array, PHP will take care of any necessary argument escaping. Found: %s', + $i, + 'Invalid', + array($item['raw']) + ); + + // Only throw one error per array item. + break; + } + } + } + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/NewStripTagsAllowableTagsArraySniff.php b/PHPCompatibility/Sniffs/ParameterValues/NewStripTagsAllowableTagsArraySniff.php new file mode 100644 index 00000000..9d9ede05 --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/NewStripTagsAllowableTagsArraySniff.php @@ -0,0 +1,152 @@ + true, + ); + + /** + * Text string tokens to examine. + * + * @since 9.3.0 + * + * @var array + */ + private $textStringTokens = array( + \T_CONSTANT_ENCAPSED_STRING => true, + \T_DOUBLE_QUOTED_STRING => true, + \T_INLINE_HTML => true, + \T_HEREDOC => true, + \T_NOWDOC => true, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return false; + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + if (isset($parameters[2]) === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $targetParam = $parameters[2]; + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], $targetParam['end'], true); + + if ($nextNonEmpty === false) { + // Shouldn't be possible. + return; + } + + if ($tokens[$nextNonEmpty]['code'] !== \T_ARRAY + && $tokens[$nextNonEmpty]['code'] !== \T_OPEN_SHORT_ARRAY + ) { + // Not passed as a hard-coded array. + return; + } + + if ($this->supportsBelow('7.3') === true) { + $phpcsFile->addError( + 'The strip_tags() function did not accept $allowable_tags to be passed in array format in PHP 7.3 and earlier.', + $nextNonEmpty, + 'Found' + ); + } + + if ($this->supportsAbove('7.4') === true) { + if (strpos($targetParam['raw'], '>') === false) { + // Efficiency: prevent needlessly walking the array. + return; + } + + $items = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty); + + if (empty($items)) { + return; + } + + foreach ($items as $item) { + for ($i = $item['start']; $i <= $item['end']; $i++) { + if ($tokens[$i]['code'] === \T_STRING + || $tokens[$i]['code'] === \T_VARIABLE + ) { + // Variable, constant, function call. Ignore complete item as undetermined. + break; + } + + if (isset($this->textStringTokens[$tokens[$i]['code']]) === true + && strpos($tokens[$i]['content'], '>') !== false + ) { + + $phpcsFile->addWarning( + 'When passing strip_tags() the $allowable_tags parameter as an array, the tags should not be enclosed in <> brackets. Found: %s', + $i, + 'Invalid', + array($item['raw']) + ); + + // Only throw one error per array item. + break; + } + } + } + } + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedHashAlgorithmsSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedHashAlgorithmsSniff.php index 38f81f57..83d4b8b3 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/RemovedHashAlgorithmsSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedHashAlgorithmsSniff.php @@ -1,13 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -16,16 +14,14 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\RemovedHashAlgorithmsSniff. - * - * Discourages the use of deprecated and removed hash algorithms. + * Detect the use of deprecated and removed hash algorithms. * * PHP version 5.4 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * @link https://www.php.net/manual/en/function.hash-algos.php#refsect1-function.hash-algos-changelog + * + * @since 5.5 + * @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class. */ class RemovedHashAlgorithmsSniff extends AbstractRemovedFeatureSniff { @@ -36,6 +32,8 @@ class RemovedHashAlgorithmsSniff extends AbstractRemovedFeatureSniff * The array lists : version number with false (deprecated) and true (removed). * If's sufficient to list the first version where the hash algorithm was deprecated/removed. * + * @since 7.0.7 + * * @var array(string => array(string => bool)) */ protected $removedAlgorithms = array( @@ -50,6 +48,8 @@ class RemovedHashAlgorithmsSniff extends AbstractRemovedFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -61,6 +61,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -70,7 +72,7 @@ public function register() public function process(File $phpcsFile, $stackPtr) { $algo = $this->getHashAlgorithmParameter($phpcsFile, $stackPtr); - if (empty($algo) || is_string($algo) === false) { + if (empty($algo) || \is_string($algo) === false) { return; } @@ -89,6 +91,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -102,6 +106,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedIconvEncodingSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedIconvEncodingSniff.php index f4df0efa..d7c7c663 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/RemovedIconvEncodingSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedIconvEncodingSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -15,19 +14,20 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\RemovedIconvEncodingSniff. + * Detect passing deprecated `$type` values to `iconv_get_encoding()`. * - * Detect: "The iconv and mbstring configuration options related to encoding - * have been deprecated in favour of default_charset." + * "The iconv and mbstring configuration options related to encoding have been + * deprecated in favour of default_charset." * * {@internal It is unclear which mbstring functions should be targetted, so for now, - * only the iconv function is handled.}} + * only the iconv function is handled.} * * PHP version 5.6 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration56.deprecated.php#migration56.deprecated.iconv-mbstring-encoding + * @link https://wiki.php.net/rfc/default_encoding + * + * @since 9.0.0 */ class RemovedIconvEncodingSniff extends AbstractFunctionCallParameterSniff { @@ -35,6 +35,8 @@ class RemovedIconvEncodingSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -45,6 +47,8 @@ class RemovedIconvEncodingSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -56,6 +60,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedImplodeFlexibleParamOrderSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedImplodeFlexibleParamOrderSniff.php new file mode 100644 index 00000000..1ff0f90f --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedImplodeFlexibleParamOrderSniff.php @@ -0,0 +1,323 @@ + true, + 'join' => true, + ); + + /** + * List of PHP native constants which should be recognized as text strings. + * + * @since 9.3.0 + * + * @var array + */ + private $constantStrings = array( + 'DIRECTORY_SEPARATOR' => true, + 'PHP_EOL' => true, + ); + + /** + * List of PHP native functions which should be recognized as returning an array. + * + * Note: The array_*() functions will always be taken into account. + * + * @since 9.3.0 + * + * @var array + */ + private $arrayFunctions = array( + 'compact' => true, + 'explode' => true, + 'range' => true, + ); + + /** + * List of PHP native array functions which should *not* be recognized as returning an array. + * + * @since 9.3.0 + * + * @var array + */ + private $arrayFunctionExceptions = array( + 'array_key_exists' => true, + 'array_key_first' => true, + 'array_key_last' => true, + 'array_multisort' => true, + 'array_pop' => true, + 'array_product' => true, + 'array_push' => true, + 'array_search' => true, + 'array_shift' => true, + 'array_sum' => true, + 'array_unshift' => true, + 'array_walk_recursive' => true, + 'array_walk' => true, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return ($this->supportsAbove('7.4') === false); + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + if (isset($parameters[2]) === false) { + // Only one parameter, this must be $pieces. Bow out. + return; + } + + $tokens = $phpcsFile->getTokens(); + + /* + * Examine the first parameter. + * If there is any indication that this is an array declaration, we have an error. + */ + + $targetParam = $parameters[1]; + $start = $targetParam['start']; + $end = ($targetParam['end'] + 1); + $isOnlyText = true; + + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true); + if ($firstNonEmpty === false) { + // Parse error. Shouldn't be possible. + return; + } + + if ($tokens[$firstNonEmpty]['code'] === \T_OPEN_PARENTHESIS) { + $start = ($firstNonEmpty + 1); + $end = $tokens[$firstNonEmpty]['parenthesis_closer']; + } + + $hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end); + if ($hasTernary !== false + && isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis']) + && count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis']) + ) { + $start = ($hasTernary + 1); + } + + for ($i = $start; $i < $end; $i++) { + $tokenCode = $tokens[$i]['code']; + + if (isset(Tokens::$emptyTokens[$tokenCode])) { + continue; + } + + if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) { + continue; + } + + if ($hasTernary !== false && $tokenCode === \T_INLINE_ELSE) { + continue; + } + + if (isset(Tokens::$stringTokens[$tokenCode]) === false) { + $isOnlyText = false; + } + + if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) { + $this->throwNotice($phpcsFile, $stackPtr, $functionName); + return; + } + + if ($tokenCode === \T_STRING) { + /* + * Check for specific functions which return an array (i.e. $pieces). + */ + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), $end, true); + if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) { + continue; + } + + $nameLc = strtolower($tokens[$i]['content']); + if (isset($this->arrayFunctions[$nameLc]) === false + && (strpos($nameLc, 'array_') !== 0 + || isset($this->arrayFunctionExceptions[$nameLc]) === true) + ) { + continue; + } + + // Now make sure it's the PHP native function being called. + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), $start, true); + if ($tokens[$prevNonEmpty]['code'] === \T_DOUBLE_COLON + || $tokens[$prevNonEmpty]['code'] === \T_OBJECT_OPERATOR + ) { + // Method call, not a call to the PHP native function. + continue; + } + + if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR + && $tokens[$prevNonEmpty - 1]['code'] === \T_STRING + ) { + // Namespaced function. + continue; + } + + // Ok, so we know that there is an array function in the first param. + // 99.9% chance that this is $pieces, not $glue. + $this->throwNotice($phpcsFile, $stackPtr, $functionName); + return; + } + } + + if ($isOnlyText === true) { + // First parameter only contained text string tokens, i.e. glue. + return; + } + + /* + * Examine the second parameter. + */ + + $targetParam = $parameters[2]; + $start = $targetParam['start']; + $end = ($targetParam['end'] + 1); + + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true); + if ($firstNonEmpty === false) { + // Parse error. Shouldn't be possible. + return; + } + + if ($tokens[$firstNonEmpty]['code'] === \T_OPEN_PARENTHESIS) { + $start = ($firstNonEmpty + 1); + $end = $tokens[$firstNonEmpty]['parenthesis_closer']; + } + + $hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end); + if ($hasTernary !== false + && isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis']) + && count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis']) + ) { + $start = ($hasTernary + 1); + } + + for ($i = $start; $i < $end; $i++) { + $tokenCode = $tokens[$i]['code']; + + if (isset(Tokens::$emptyTokens[$tokenCode])) { + continue; + } + + if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) { + // Found an array, $pieces is second. + return; + } + + if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) { + // One of the special cased, PHP native string constants found. + $this->throwNotice($phpcsFile, $stackPtr, $functionName); + return; + } + + if ($tokenCode === \T_STRING || $tokenCode === \T_VARIABLE) { + // Function call, constant or variable encountered. + // No matter what this is combined with, we won't be able to reliably determine the value. + return; + } + + if ($tokenCode === \T_CONSTANT_ENCAPSED_STRING + || $tokenCode === \T_DOUBLE_QUOTED_STRING + || $tokenCode === \T_HEREDOC + || $tokenCode === \T_NOWDOC + ) { + $this->throwNotice($phpcsFile, $stackPtr, $functionName); + return; + } + } + } + + + /** + * Throw the error/warning. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * + * @return void + */ + protected function throwNotice(File $phpcsFile, $stackPtr, $functionName) + { + $message = 'Passing the $glue and $pieces parameters in reverse order to %s has been deprecated since PHP 7.4'; + $isError = false; + $errorCode = 'Deprecated'; + $data = array($functionName); + + /* + Support for the deprecated behaviour is expected to be removed in PHP 8.0. + Once this has been implemented, this section should be uncommented. + if ($this->supportsAbove('8.0') === true) { + $message .= ' and is removed since PHP 8.0'; + $isError = true; + $errorCode = 'Removed'; + } + */ + + $message .= '; $glue should be the first parameter and $pieces the second'; + + $this->addMessage($phpcsFile, $message, $stackPtr, $isError, $errorCode, $data); + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedMbStrrposEncodingThirdParamSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedMbStrrposEncodingThirdParamSniff.php new file mode 100644 index 00000000..7406d9a8 --- /dev/null +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedMbStrrposEncodingThirdParamSniff.php @@ -0,0 +1,149 @@ + true, + ); + + /** + * Tokens which should be recognized as text. + * + * @since 9.3.0 + * + * @var array + */ + private $textStringTokens = array( + \T_CONSTANT_ENCAPSED_STRING, + \T_DOUBLE_QUOTED_STRING, + \T_HEREDOC, + \T_NOWDOC, + ); + + /** + * Tokens which should be recognized as numbers. + * + * @since 9.3.0 + * + * @var array + */ + private $numberTokens = array( + \T_LNUMBER => \T_LNUMBER, + \T_DNUMBER => \T_DNUMBER, + \T_MINUS => \T_MINUS, + \T_PLUS => \T_PLUS, + ); + + + /** + * Do a version check to determine if this sniff needs to run at all. + * + * @since 9.3.0 + * + * @return bool + */ + protected function bowOutEarly() + { + return $this->supportsAbove('5.2') === false; + } + + + /** + * Process the parameters of a matched function. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * @param string $functionName The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) + { + if (isset($parameters[3]) === false) { + // Optional third parameter not set. + return; + } + + if (isset($parameters[4]) === true) { + // Encoding set as fourth parameter. + return; + } + + $targetParam = $parameters[3]; + $targets = $this->numberTokens + Tokens::$emptyTokens; + $nonNumber = $phpcsFile->findNext($targets, $targetParam['start'], ($targetParam['end'] + 1), true); + if ($nonNumber === false) { + return; + } + + if ($this->isNumericCalculation($phpcsFile, $targetParam['start'], $targetParam['end']) === true) { + return; + } + + $hasString = $phpcsFile->findNext($this->textStringTokens, $targetParam['start'], ($targetParam['end'] + 1)); + if ($hasString === false) { + // No text strings found. Undetermined. + return; + } + + $error = 'Passing the encoding to mb_strrpos() as third parameter is soft deprecated since PHP 5.2'; + if ($this->supportsAbove('7.4') === true) { + $error .= ' and hard deprecated since PHP 7.4'; + } + + $error .= '. Use an explicit 0 as the offset in the third parameter.'; + + $phpcsFile->addWarning( + $error, + $targetParam['start'], + 'Deprecated' + ); + } +} diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedMbstringModifiersSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedMbstringModifiersSniff.php index ee6f13a0..83e4de97 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/RemovedMbstringModifiersSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedMbstringModifiersSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -16,13 +15,20 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\ParameterValues\RemovedMbstringModifiersSniff. + * Check for use of deprecated and removed regex modifiers for MbString regex functions. + * + * Initially just checks for the PHP 7.1 deprecated `e` modifier. * * PHP version 7.1 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/deprecate_mb_ereg_replace_eval_option + * @link https://www.php.net/manual/en/function.mb-regex-set-options.php + * + * @since 7.0.5 + * @since 7.0.8 This sniff now throws a warning instead of an error as the functionality is + * only deprecated (for now). + * @since 8.2.0 Now extends the `AbstractFunctionCallParameterSniff` instead of the base `Sniff` class. + * @since 9.0.0 Renamed from `MbstringReplaceEModifierSniff` to `RemovedMbstringModifiersSniff`. */ class RemovedMbstringModifiersSniff extends AbstractFunctionCallParameterSniff { @@ -32,6 +38,9 @@ class RemovedMbstringModifiersSniff extends AbstractFunctionCallParameterSniff * * Key is the function name, value the parameter position of the options parameter. * + * @since 7.0.5 + * @since 8.2.0 Renamed from `$functions` to `$targetFunctions`. + * * @var array */ protected $targetFunctions = array( @@ -46,6 +55,8 @@ class RemovedMbstringModifiersSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 8.2.0 + * * @return bool */ protected function bowOutEarly() @@ -59,7 +70,9 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * - * This method has to be made concrete in child classes. + * @since 7.0.5 + * @since 8.2.0 Renamed from `process()` to `processParameters()` and removed + * logic superfluous now the sniff extends the abstract. * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedNonCryptoHashSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedNonCryptoHashSniff.php index 0cb73496..832a1f62 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/RemovedNonCryptoHashSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedNonCryptoHashSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -15,16 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\RemovedNonCryptoHashSniff. + * Detect usage of non-cryptographic hashes. * - * Detect: "The hash_hmac(), hash_hmac_file(), hash_pbkdf2(), and hash_init() - * (with HASH_HMAC) functions no longer accept non-cryptographic hashes." + * "The `hash_hmac()`, `hash_hmac_file()`, `hash_pbkdf2()`, and `hash_init()` + * (with `HASH_HMAC`) functions no longer accept non-cryptographic hashes." * * PHP version 7.2 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration72.incompatible.php#migration72.incompatible.hash-functions + * + * @since 9.0.0 */ class RemovedNonCryptoHashSniff extends AbstractFunctionCallParameterSniff { @@ -32,6 +31,8 @@ class RemovedNonCryptoHashSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -44,6 +45,8 @@ class RemovedNonCryptoHashSniff extends AbstractFunctionCallParameterSniff /** * List of the non-cryptographic hashes. * + * @since 9.0.0 + * * @var array */ protected $disabledCryptos = array( @@ -61,6 +64,8 @@ class RemovedNonCryptoHashSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -72,6 +77,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedPCREModifiersSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedPCREModifiersSniff.php index d5aa766d..377ed79f 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/RemovedPCREModifiersSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedPCREModifiersSniff.php @@ -1,13 +1,11 @@ - * @copyright 2014 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -17,17 +15,26 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\ParameterValues\RemovedPCREModifiersSniff. + * Check for the use of deprecated and removed regex modifiers for PCRE regex functions. * - * Check for usage of the `e` modifier with PCRE functions which is deprecated since PHP 5.5 + * Initially just checks for the `e` modifier, which was deprecated since PHP 5.5 * and removed as of PHP 7.0. * + * {@internal If and when this sniff would need to start checking for other modifiers, a minor + * refactor will be needed as all references to the `e` modifier are currently hard-coded.} + * * PHP version 5.5 + * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2014 Cu.be Solutions bvba + * @link https://wiki.php.net/rfc/remove_preg_replace_eval_modifier + * @link https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7 + * @link https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php + * + * @since 5.6 + * @since 7.0.8 This sniff now throws a warning (deprecated) or an error (removed) depending + * on the `testVersion` set. Previously it would always throw an error. + * @since 8.2.0 Now extends the `AbstractFunctionCallParameterSniff` instead of the base `Sniff` class. + * @since 9.0.0 Renamed from `PregReplaceEModifierSniff` to `RemovedPCREModifiersSniff`. */ class RemovedPCREModifiersSniff extends AbstractFunctionCallParameterSniff { @@ -35,6 +42,9 @@ class RemovedPCREModifiersSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 7.0.1 + * @since 8.2.0 Renamed from `$functions` to `$targetFunctions`. + * * @var array */ protected $targetFunctions = array( @@ -45,6 +55,8 @@ class RemovedPCREModifiersSniff extends AbstractFunctionCallParameterSniff /** * Regex bracket delimiters. * + * @since 7.0.5 This array was originally contained within the `process()` method. + * * @var array */ protected $doublesSeparators = array( @@ -58,6 +70,10 @@ class RemovedPCREModifiersSniff extends AbstractFunctionCallParameterSniff /** * Process the parameters of a matched function. * + * @since 5.6 + * @since 8.2.0 Renamed from `process()` to `processParameters()` and removed + * logic superfluous now the sniff extends the abstract. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. @@ -116,6 +132,8 @@ public function processParameters(File $phpcsFile, $stackPtr, $functionName, $pa /** * Do a version check to determine if this sniff needs to run at all. * + * @since 8.2.0 + * * @return bool */ protected function bowOutEarly() @@ -125,7 +143,9 @@ protected function bowOutEarly() /** - * Analyse a potential regex pattern for usage of the /e modifier. + * Analyse a potential regex pattern for use of the /e modifier. + * + * @since 7.1.2 This logic was originally contained within the `process()` method. * * @param array $pattern Array containing the start and end token * pointer of the potential regex pattern and @@ -191,6 +211,8 @@ protected function processRegexPattern($pattern, File $phpcsFile, $stackPtr, $fu /** * Examine the regex modifier string. * + * @since 8.2.0 Split off from the `processRegexPattern()` method. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/ParameterValues/RemovedSetlocaleStringSniff.php b/PHPCompatibility/Sniffs/ParameterValues/RemovedSetlocaleStringSniff.php index 5b7b73d3..6f1eced1 100644 --- a/PHPCompatibility/Sniffs/ParameterValues/RemovedSetlocaleStringSniff.php +++ b/PHPCompatibility/Sniffs/ParameterValues/RemovedSetlocaleStringSniff.php @@ -1,13 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\ParameterValues; @@ -16,17 +14,18 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\ParameterValues\RemovedSetlocaleStringSniff. + * Detect passing a string literal as `$category` to `setlocale()`. * - * Detect: Support for the category parameter passed as a string has been removed. - * Only LC_* constants can be used as of this version [7.0.0]. + * Support for the category parameter passed as a string has been removed. + * Only `LC_*` constants can be used as of PHP 7.0.0. * * PHP version 4.2 * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7 + * @link https://www.php.net/manual/en/function.setlocale.php#refsect1-function.setlocale-changelog + * + * @since 9.0.0 */ class RemovedSetlocaleStringSniff extends AbstractFunctionCallParameterSniff { @@ -34,6 +33,8 @@ class RemovedSetlocaleStringSniff extends AbstractFunctionCallParameterSniff /** * Functions to check for. * + * @since 9.0.0 + * * @var array */ protected $targetFunctions = array( @@ -44,6 +45,8 @@ class RemovedSetlocaleStringSniff extends AbstractFunctionCallParameterSniff /** * Do a version check to determine if this sniff needs to run at all. * + * @since 9.0.0 + * * @return bool */ protected function bowOutEarly() @@ -55,6 +58,8 @@ protected function bowOutEarly() /** * Process the parameters of a matched function. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. diff --git a/PHPCompatibility/Sniffs/Syntax/ForbiddenCallTimePassByReferenceSniff.php b/PHPCompatibility/Sniffs/Syntax/ForbiddenCallTimePassByReferenceSniff.php index f0e747c8..96b49827 100644 --- a/PHPCompatibility/Sniffs/Syntax/ForbiddenCallTimePassByReferenceSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/ForbiddenCallTimePassByReferenceSniff.php @@ -1,14 +1,11 @@ - * @author Florian Grandel - * @copyright 2009 Florian Grandel + * @copyright 2009-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -18,17 +15,18 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\ForbiddenCallTimePassByReference. + * Detect the use of call time pass by reference. * - * Discourages the use of call time pass by references + * This behaviour has been deprecated in PHP 5.3 and removed in PHP 5.4. * * PHP version 5.4 * - * @category PHP - * @package PHPCompatibility - * @author Gary Rogers - * @author Florian Grandel - * @copyright 2009 Florian Grandel + * @link https://wiki.php.net/rfc/calltimebyref + * @link https://www.php.net/manual/en/language.references.pass.php + * + * @since 5.5 + * @since 7.0.8 This sniff now throws a warning (deprecated) or an error (removed) depending + * on the `testVersion` set. Previously it would always throw an error. */ class ForbiddenCallTimePassByReferenceSniff extends Sniff { @@ -39,6 +37,8 @@ class ForbiddenCallTimePassByReferenceSniff extends Sniff * Near duplicate of Tokens::$assignmentTokens + Tokens::$equalityTokens. * Copied in for PHPCS cross-version compatibility. * + * @since 8.1.0 + * * @var array */ private $assignOrCompare = array( @@ -72,6 +72,8 @@ class ForbiddenCallTimePassByReferenceSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -85,6 +87,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -113,7 +117,7 @@ public function process(File $phpcsFile, $stackPtr) true ); - if ($prevNonEmpty !== false && in_array($tokens[$prevNonEmpty]['type'], array('T_FUNCTION', 'T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) { + if ($prevNonEmpty !== false && \in_array($tokens[$prevNonEmpty]['type'], array('T_FUNCTION', 'T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) { return; } @@ -129,14 +133,14 @@ public function process(File $phpcsFile, $stackPtr) // Get the function call parameters. $parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr); - if (count($parameters) === 0) { + if (\count($parameters) === 0) { return; } // Which nesting level is the one we are interested in ? $nestedParenthesisCount = 1; if (isset($tokens[$openBracket]['nested_parenthesis'])) { - $nestedParenthesisCount = count($tokens[$openBracket]['nested_parenthesis']) + 1; + $nestedParenthesisCount = \count($tokens[$openBracket]['nested_parenthesis']) + 1; } foreach ($parameters as $parameter) { @@ -161,6 +165,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Determine whether a parameter is passed by reference. * + * @since 7.0.6 Split off from the `process()` method. + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param array $parameter Information on the current parameter * to be examined. @@ -206,7 +212,7 @@ protected function isCallTimePassByReferenceParam(File $phpcsFile, $parameter, $ // Make sure the variable belongs directly to this function call // and is not inside a nested function call or array. if (isset($tokens[$nextVariable]['nested_parenthesis']) === false - || (count($tokens[$nextVariable]['nested_parenthesis']) !== $nestingLevel) + || (\count($tokens[$nextVariable]['nested_parenthesis']) !== $nestingLevel) ) { continue; } diff --git a/PHPCompatibility/Sniffs/Syntax/NewArrayStringDereferencingSniff.php b/PHPCompatibility/Sniffs/Syntax/NewArrayStringDereferencingSniff.php index ba1d130a..2cc1e551 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewArrayStringDereferencingSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewArrayStringDereferencingSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -16,21 +15,36 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff. + * Detect array and string literal dereferencing. + * + * As of PHP 5.5, array and string literals can now be dereferenced directly to + * access individual elements and characters. * - * Array and string literals can now be dereferenced directly to access individual elements and characters. + * As of PHP 7.0, this also works when using curly braces for the dereferencing. + * While unclear, this most likely has to do with the Uniform Variable Syntax changes. * * PHP version 5.5 + * PHP version 7.0 + * + * @link https://www.php.net/manual/en/migration55.new-features.php#migration55.new-features.const-dereferencing + * @link https://wiki.php.net/rfc/constdereference + * @link https://wiki.php.net/rfc/uniform_variable_syntax + * @link https://www.php.net/manual/en/language.types.array.php#example-63 + * + * {@internal The reason for splitting the logic of this sniff into different methods is + * to allow re-use of the logic by the PHP 7.4 `RemovedCurlyBraceArrayAccess` sniff.} * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @since 7.1.4 + * @since 9.3.0 Now also detects dereferencing using curly braces. */ class NewArrayStringDereferencingSniff extends Sniff { + /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.4 + * * @return array */ public function register() @@ -45,6 +59,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -53,10 +69,61 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { - if ($this->supportsBelow('5.4') === false) { + if ($this->supportsBelow('5.6') === false) { + return; + } + + $dereferencing = $this->isArrayStringDereferencing($phpcsFile, $stackPtr); + if (empty($dereferencing)) { return; } + $tokens = $phpcsFile->getTokens(); + $supports54 = $this->supportsBelow('5.4'); + + foreach ($dereferencing['braces'] as $openBrace => $closeBrace) { + if ($supports54 === true + && ($tokens[$openBrace]['type'] === 'T_OPEN_SQUARE_BRACKET' + || $tokens[$openBrace]['type'] === 'T_OPEN_SHORT_ARRAY') // Work around bug #1381 in PHPCS 2.8.1 and lower. + ) { + $phpcsFile->addError( + 'Direct array dereferencing of %s is not present in PHP version 5.4 or earlier', + $openBrace, + 'Found', + array($dereferencing['type']) + ); + + continue; + } + + // PHP 7.0 Array/string dereferencing using curly braces. + if ($tokens[$openBrace]['type'] === 'T_OPEN_CURLY_BRACKET') { + $phpcsFile->addError( + 'Direct array dereferencing of %s using curly braces is not present in PHP version 5.6 or earlier', + $openBrace, + 'FoundUsingCurlies', + array($dereferencing['type']) + ); + } + } + } + + + /** + * Check if this string/array is being dereferenced. + * + * @since 9.3.0 Logic split off from the process method. + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return array Array containing the type of access and stack pointers to the + * open/close braces involved in the array/string dereferencing; + * or an empty array if no array/string dereferencing was detected. + */ + public function isArrayStringDereferencing(File $phpcsFile, $stackPtr) + { $tokens = $phpcsFile->getTokens(); switch ($tokens[$stackPtr]['code']) { @@ -68,7 +135,7 @@ public function process(File $phpcsFile, $stackPtr) case \T_ARRAY: if (isset($tokens[$stackPtr]['parenthesis_closer']) === false) { // Live coding. - return; + return array(); } else { $type = 'arrays'; $end = $tokens[$stackPtr]['parenthesis_closer']; @@ -78,7 +145,7 @@ public function process(File $phpcsFile, $stackPtr) case \T_OPEN_SHORT_ARRAY: if (isset($tokens[$stackPtr]['bracket_closer']) === false) { // Live coding. - return; + return array(); } else { $type = 'arrays'; $end = $tokens[$stackPtr]['bracket_closer']; @@ -88,21 +155,45 @@ public function process(File $phpcsFile, $stackPtr) if (isset($type, $end) === false) { // Shouldn't happen, but for some reason did. - return; + return array(); } - $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true); - - if ($nextNonEmpty !== false - && ($tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET' - || $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SHORT_ARRAY') // Work around bug #1381 in PHPCS 2.8.1 and lower. - ) { - $phpcsFile->addError( - 'Direct array dereferencing of %s is not present in PHP version 5.4 or earlier', - $nextNonEmpty, - 'Found', - array($type) - ); + $braces = array(); + + do { + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true); + if ($nextNonEmpty === false) { + break; + } + + if ($tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET' + || $tokens[$nextNonEmpty]['type'] === 'T_OPEN_CURLY_BRACKET' // PHP 7.0+. + || $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SHORT_ARRAY' // Work around bug #1381 in PHPCS 2.8.1 and lower. + ) { + if (isset($tokens[$nextNonEmpty]['bracket_closer']) === false) { + // Live coding or parse error. + break; + } + + $braces[$nextNonEmpty] = $tokens[$nextNonEmpty]['bracket_closer']; + + // Continue, just in case there is nested array access, i.e. `array(1, 2, 3)[$i][$j];`. + $end = $tokens[$nextNonEmpty]['bracket_closer']; + continue; + } + + // If we're still here, we've reached the end of the variable. + break; + + } while (true); + + if (empty($braces)) { + return array(); } + + return array( + 'type' => $type, + 'braces' => $braces, + ); } } diff --git a/PHPCompatibility/Sniffs/Syntax/NewArrayUnpackingSniff.php b/PHPCompatibility/Sniffs/Syntax/NewArrayUnpackingSniff.php new file mode 100644 index 00000000..f0f66b86 --- /dev/null +++ b/PHPCompatibility/Sniffs/Syntax/NewArrayUnpackingSniff.php @@ -0,0 +1,142 @@ +supportsBelow('7.3') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + + /* + * Determine the array opener & closer. + */ + $closer = $phpcsFile->numTokens; + if ($tokens[$stackPtr]['code'] === \T_ARRAY) { + if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) { + return; + } + + $opener = $tokens[$stackPtr]['parenthesis_opener']; + + if (isset($tokens[$opener]['parenthesis_closer'])) { + $closer = $tokens[$opener]['parenthesis_closer']; + } + } else { + // Short array syntax. + $opener = $stackPtr; + + if (isset($tokens[$stackPtr]['bracket_closer'])) { + $closer = $tokens[$stackPtr]['bracket_closer']; + } + } + + $nestingLevel = 0; + if (isset($tokens[($opener + 1)]['nested_parenthesis'])) { + $nestingLevel = count($tokens[($opener + 1)]['nested_parenthesis']); + } + + for ($i = $opener; $i < $closer;) { + $i = $phpcsFile->findNext(array(\T_ELLIPSIS, \T_OPEN_SHORT_ARRAY, \T_ARRAY), ($i + 1), $closer); + if ($i === false) { + return; + } + + if ($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY) { + if (isset($tokens[$i]['bracket_closer']) === false) { + // Live coding, unfinished nested array, handle this when the array opener + // of the nested array is passed. + return; + } + + // Skip over nested short arrays. These will be handled when the array opener + // of the nested array is passed. + $i = $tokens[$i]['bracket_closer']; + continue; + } + + if ($tokens[$i]['code'] === \T_ARRAY) { + if (isset($tokens[$i]['parenthesis_closer']) === false) { + // Live coding, unfinished nested array, handle this when the array opener + // of the nested array is passed. + return; + } + + // Skip over nested long arrays. These will be handled when the array opener + // of the nested array is passed. + $i = $tokens[$i]['parenthesis_closer']; + continue; + } + + // Ensure this is not function call variable unpacking. + if (isset($tokens[$i]['nested_parenthesis']) + && count($tokens[$i]['nested_parenthesis']) > $nestingLevel + ) { + continue; + } + + // Ok, found one. + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true); + $snippet = trim($phpcsFile->getTokensAsString($i, (($nextNonEmpty - $i) + 1))); + $phpcsFile->addError( + 'Array unpacking within array declarations using the spread operator is not supported in PHP 7.3 or earlier. Found: %s', + $i, + 'Found', + array($snippet) + ); + } + } +} diff --git a/PHPCompatibility/Sniffs/Syntax/NewClassMemberAccessSniff.php b/PHPCompatibility/Sniffs/Syntax/NewClassMemberAccessSniff.php index 12e222f4..218f0c9f 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewClassMemberAccessSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewClassMemberAccessSniff.php @@ -1,13 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -17,17 +15,28 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff. + * Detect class member access on object instantiation/cloning. + * + * PHP 5.4: Class member access on instantiation has been added, e.g. `(new Foo)->bar()`. + * PHP 7.0: Class member access on cloning has been added, e.g. `(clone $foo)->bar()`. * - * PHP 5.4: Class member access on instantiation has been added, e.g. (new Foo)->bar(). - * PHP 7.0: Class member access on cloning has been added, e.g. (clone $foo)->bar(). + * As of PHP 7.0, class member access on instantiation also works when using curly braces. + * While unclear, this most likely has to do with the Uniform Variable Syntax changes. * * PHP version 5.4 * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/language.oop5.basic.php#example-177 + * @link https://www.php.net/manual/en/language.oop5.cloning.php#language.oop5.traits.properties.example + * @link https://www.php.net/manual/en/migration54.new-features.php + * @link https://wiki.php.net/rfc/instance-method-call + * @link https://wiki.php.net/rfc/uniform_variable_syntax + * + * {@internal The reason for splitting the logic of this sniff into different methods is + * to allow re-use of the logic by the PHP 7.4 `RemovedCurlyBraceArrayAccess` sniff.} + * + * @since 8.2.0 + * @since 9.3.0 Now also detects class member access on instantiation using curly braces. */ class NewClassMemberAccessSniff extends Sniff { @@ -35,6 +44,8 @@ class NewClassMemberAccessSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -48,6 +59,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -56,17 +69,73 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { - $tokens = $phpcsFile->getTokens(); - - if ($tokens[$stackPtr]['code'] === \T_NEW && $this->supportsBelow('5.3') !== true) { + if ($this->supportsBelow('5.6') === false) { return; - } elseif ($tokens[$stackPtr]['code'] === \T_CLONE && $this->supportsBelow('5.6') !== true) { + } + + $pointers = $this->isClassMemberAccess($phpcsFile, $stackPtr); + if (empty($pointers)) { return; } + $tokens = $phpcsFile->getTokens(); + $supports53 = $this->supportsBelow('5.3'); + + $error = 'Class member access on object %s was not supported in PHP %s or earlier'; + $data = array('instantiation', '5.3'); + $errorCode = 'OnNewFound'; + + if ($tokens[$stackPtr]['code'] === \T_CLONE) { + $data = array('cloning', '5.6'); + $errorCode = 'OnCloneFound'; + } + + foreach ($pointers as $open => $close) { + $itemData = $data; + $itemErrorCode = $errorCode; + + if ($tokens[$stackPtr]['code'] === \T_NEW + && $tokens[$open]['code'] !== \T_OPEN_CURLY_BRACKET + ) { + if ($supports53 === true) { + $phpcsFile->addError($error, $open, $itemErrorCode, $itemData); + } + continue; + } + + if ($tokens[$stackPtr]['code'] === \T_NEW + && $tokens[$open]['code'] === \T_OPEN_CURLY_BRACKET + ) { + // Non-curlies was already handled above. + $itemData = array('instantiation using curly braces', '5.6'); + $itemErrorCode = 'OnNewFoundUsingCurlies'; + } + + $phpcsFile->addError($error, $open, $itemErrorCode, $itemData); + } + } + + + /** + * Check if the class being instantiated/cloned is being dereferenced. + * + * @since 9.3.0 Logic split off from the process method. + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return array Array containing the stack pointers to the object operator or + * the open/close braces involved in the class member access; + * or an empty array if no class member access was detected. + */ + public function isClassMemberAccess(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['nested_parenthesis']) === false) { // The `new className/clone $a` has to be in parentheses, without is not supported. - return; + return array(); } $parenthesisCloser = end($tokens[$stackPtr]['nested_parenthesis']); @@ -74,40 +143,50 @@ public function process(File $phpcsFile, $stackPtr) if (isset($tokens[$parenthesisOpener]['parenthesis_owner']) === true) { // If there is an owner, these parentheses are for a different purpose. - return; + return array(); } $prevBeforeParenthesis = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($parenthesisOpener - 1), null, true); if ($prevBeforeParenthesis !== false && $tokens[$prevBeforeParenthesis]['code'] === \T_STRING) { // This is most likely a function call with the new/cloned object as a parameter. - return; + return array(); } - $nextAfterParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($parenthesisCloser + 1), null, true); - if ($nextAfterParenthesis === false) { - // Live coding. - return; - } + $braces = array(); + $end = $parenthesisCloser; - if ($tokens[$nextAfterParenthesis]['code'] !== \T_OBJECT_OPERATOR - && $tokens[$nextAfterParenthesis]['code'] !== \T_OPEN_SQUARE_BRACKET - ) { - return; - } + do { + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true); + if ($nextNonEmpty === false) { + break; + } - $data = array('instantiation', '5.3'); - $errorCode = 'OnNewFound'; + if ($tokens[$nextNonEmpty]['code'] === \T_OBJECT_OPERATOR) { + // No need to walk any further if this is object access. + $braces[$nextNonEmpty] = true; + break; + } - if ($tokens[$stackPtr]['code'] === \T_CLONE) { - $data = array('cloning', '5.6'); - $errorCode = 'OnCloneFound'; - } + if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_SQUARE_BRACKET + || $tokens[$nextNonEmpty]['code'] === \T_OPEN_CURLY_BRACKET // PHP 7.0+. + ) { + if (isset($tokens[$nextNonEmpty]['bracket_closer']) === false) { + // Live coding or parse error. + break; + } - $phpcsFile->addError( - 'Class member access on object %s was not supported in PHP %s or earlier', - $parenthesisCloser, - $errorCode, - $data - ); + $braces[$nextNonEmpty] = $tokens[$nextNonEmpty]['bracket_closer']; + + // Continue, just in case there is nested array access, i.e. `(new Foo())[1][0];`. + $end = $tokens[$nextNonEmpty]['bracket_closer']; + continue; + } + + // If we're still here, we've reached the end. + break; + + } while (true); + + return $braces; } } diff --git a/PHPCompatibility/Sniffs/Syntax/NewDynamicAccessToStaticSniff.php b/PHPCompatibility/Sniffs/Syntax/NewDynamicAccessToStaticSniff.php index 3268603b..4fd3b2fa 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewDynamicAccessToStaticSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewDynamicAccessToStaticSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -16,16 +15,17 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\NewDynamicAccessToStaticSniff. + * Detect dynamic access to static methods and properties, as well as class constants. * * As of PHP 5.3, static properties and methods as well as class constants * can be accessed using a dynamic (variable) class name. * * PHP version 5.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration53.new-features.php + * + * @since 8.1.0 + * @since 9.0.0 Renamed from `DynamicAccessToStaticSniff` to `NewDynamicAccessToStaticSniff`. */ class NewDynamicAccessToStaticSniff extends Sniff { @@ -33,6 +33,8 @@ class NewDynamicAccessToStaticSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.1.0 + * * @return array */ public function register() @@ -45,6 +47,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Syntax/NewFlexibleHeredocNowdocSniff.php b/PHPCompatibility/Sniffs/Syntax/NewFlexibleHeredocNowdocSniff.php index bc60f184..b24590a6 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewFlexibleHeredocNowdocSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewFlexibleHeredocNowdocSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -16,19 +15,20 @@ use PHP_CodeSniffer_File as File; /** - * New Flexible Heredoc Nowdoc. + * Detect usage of flexible heredoc/nowdoc and related cross-version incompatibilities. * * As of PHP 7.3: - * - The body and the closing marker of a Heredoc/nowdoc can be indented; + * - The body and the closing marker of a heredoc/nowdoc can be indented; * - The closing marker no longer needs to be on a line by itself; * - The heredoc/nowdoc body may no longer contain the closing marker at the * start of any of its lines. * * PHP version 7.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.core.heredoc + * @link https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes + * + * @since 9.0.0 */ class NewFlexibleHeredocNowdocSniff extends Sniff { @@ -36,6 +36,8 @@ class NewFlexibleHeredocNowdocSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 9.0.0 + * * @return array */ public function register() @@ -57,6 +59,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -84,6 +88,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Detect indented and/or non-stand alone closing markers. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -152,7 +158,7 @@ protected function detectIndentedNonStandAloneClosingMarker(File $phpcsFile, $st * Check for tokens after the closing marker. */ // Remove the identifier. - $afterMarker = substr($trimmed, strlen($identifier)); + $afterMarker = substr($trimmed, \strlen($identifier)); // Remove a potential semi-colon at the beginning of what's left of the string. $afterMarker = ltrim($afterMarker, ';'); // Remove new line characters at the end of the string. @@ -171,6 +177,8 @@ protected function detectIndentedNonStandAloneClosingMarker(File $phpcsFile, $st /** * Detect heredoc/nowdoc identifiers at the start of lines in the heredoc/nowdoc body. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Syntax/NewFunctionArrayDereferencingSniff.php b/PHPCompatibility/Sniffs/Syntax/NewFunctionArrayDereferencingSniff.php index 33edc848..ac5e6b72 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewFunctionArrayDereferencingSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewFunctionArrayDereferencingSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -16,19 +15,34 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff. + * Detect function array dereferencing as introduced in PHP 5.4. + * + * PHP 5.4 supports direct array dereferencing on the return of a method/function call. + * + * As of PHP 7.0, this also works when using curly braces for the dereferencing. + * While unclear, this most likely has to do with the Uniform Variable Syntax changes. * * PHP version 5.4 + * PHP version 7.0 + * + * @link https://www.php.net/manual/en/language.types.array.php#example-63 + * @link https://www.php.net/manual/en/migration54.new-features.php + * @link https://wiki.php.net/rfc/functionarraydereferencing + * @link https://wiki.php.net/rfc/uniform_variable_syntax + * + * {@internal The reason for splitting the logic of this sniff into different methods is + * to allow re-use of the logic by the PHP 7.4 RemovedCurlyBraceArrayAccess sniff.} * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @since 7.0.0 + * @since 9.3.0 Now also detects dereferencing using curly braces. */ class NewFunctionArrayDereferencingSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -39,6 +53,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -47,26 +63,81 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { - if ($this->supportsBelow('5.3') === false) { + if ($this->supportsBelow('5.6') === false) { + return; + } + + $dereferencing = $this->isFunctionArrayDereferencing($phpcsFile, $stackPtr); + if (empty($dereferencing)) { return; } + $tokens = $phpcsFile->getTokens(); + $supports53 = $this->supportsBelow('5.3'); + + foreach ($dereferencing as $openBrace => $closeBrace) { + if ($supports53 === true + && $tokens[$openBrace]['type'] === 'T_OPEN_SQUARE_BRACKET' + ) { + $phpcsFile->addError( + 'Function array dereferencing is not present in PHP version 5.3 or earlier', + $openBrace, + 'Found' + ); + + continue; + } + + // PHP 7.0 function array dereferencing using curly braces. + if ($tokens[$openBrace]['type'] === 'T_OPEN_CURLY_BRACKET') { + $phpcsFile->addError( + 'Function array dereferencing using curly braces is not present in PHP version 5.6 or earlier', + $openBrace, + 'FoundUsingCurlies' + ); + } + } + } + + + /** + * Check if the return of a function/method call is being dereferenced. + * + * @since 9.3.0 Logic split off from the process method. + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return array Array containing stack pointers to the open/close braces + * involved in the function dereferencing; + * or an empty array if no function dereferencing was detected. + */ + public function isFunctionArrayDereferencing(File $phpcsFile, $stackPtr) + { $tokens = $phpcsFile->getTokens(); // Next non-empty token should be the open parenthesis. $openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true); if ($openParenthesis === false || $tokens[$openParenthesis]['code'] !== \T_OPEN_PARENTHESIS) { - return; + return array(); } // Don't throw errors during live coding. if (isset($tokens[$openParenthesis]['parenthesis_closer']) === false) { - return; + return array(); } // Is this T_STRING really a function or method call ? $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); - if ($prevToken !== false && in_array($tokens[$prevToken]['code'], array(\T_DOUBLE_COLON, \T_OBJECT_OPERATOR), true) === false) { + if ($prevToken !== false + && \in_array($tokens[$prevToken]['code'], array(\T_DOUBLE_COLON, \T_OBJECT_OPERATOR), true) === false + ) { + if ($tokens[$prevToken]['code'] === \T_BITWISE_AND) { + // This may be a function declared by reference. + $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevToken - 1), null, true); + } + $ignore = array( \T_FUNCTION => true, \T_CONST => true, @@ -78,18 +149,39 @@ public function process(File $phpcsFile, $stackPtr) if (isset($ignore[$tokens[$prevToken]['code']]) === true) { // Not a call to a PHP function or method. - return; + return array(); } } - $closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer']; - $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParenthesis + 1), null, true, null, true); - if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET') { - $phpcsFile->addError( - 'Function array dereferencing is not present in PHP version 5.3 or earlier', - $nextNonEmpty, - 'Found' - ); - } + $current = $tokens[$openParenthesis]['parenthesis_closer']; + $braces = array(); + + do { + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true, null, true); + if ($nextNonEmpty === false) { + break; + } + + if ($tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET' + || $tokens[$nextNonEmpty]['type'] === 'T_OPEN_CURLY_BRACKET' // PHP 7.0+. + ) { + if (isset($tokens[$nextNonEmpty]['bracket_closer']) === false) { + // Live coding or parse error. + break; + } + + $braces[$nextNonEmpty] = $tokens[$nextNonEmpty]['bracket_closer']; + + // Continue, just in case there is nested array access, i.e. `echo $foo->bar()[0][2];`. + $current = $tokens[$nextNonEmpty]['bracket_closer']; + continue; + } + + // If we're still here, we've reached the end of the function call. + break; + + } while (true); + + return $braces; } } diff --git a/PHPCompatibility/Sniffs/Syntax/NewFunctionCallTrailingCommaSniff.php b/PHPCompatibility/Sniffs/Syntax/NewFunctionCallTrailingCommaSniff.php index 54265e8a..39208b35 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewFunctionCallTrailingCommaSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewFunctionCallTrailingCommaSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -16,19 +15,23 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\NewFunctionCallTrailingCommaSniff. + * Detect trailing comma's in function calls, `isset()` and `unset()` as allowed since PHP 7.3. * * PHP version 7.3 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.core.trailing-commas + * @link https://wiki.php.net/rfc/trailing-comma-function-calls + * + * @since 8.2.0 + * @since 9.0.0 Renamed from `NewTrailingCommaSniff` to `NewFunctionCallTrailingCommaSniff`. */ class NewFunctionCallTrailingCommaSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -45,6 +48,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Syntax/NewShortArraySniff.php b/PHPCompatibility/Sniffs/Syntax/NewShortArraySniff.php index 7389bd78..c60ba5c3 100644 --- a/PHPCompatibility/Sniffs/Syntax/NewShortArraySniff.php +++ b/PHPCompatibility/Sniffs/Syntax/NewShortArraySniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -15,15 +14,15 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Syntax\NewShortArray. - * - * Short array syntax is available since PHP 5.4 + * Detect use of short array syntax which is available since PHP 5.4. * * PHP version 5.4 * - * @category PHP - * @package PHPCompatibility - * @author Alex Miroshnikov + * @link https://wiki.php.net/rfc/shortsyntaxforarrays + * @link https://www.php.net/manual/en/language.types.array.php#language.types.array.syntax + * + * @since 7.0.0 + * @since 9.0.0 Renamed from `ShortArraySniff` to `NewShortArraySniff`. */ class NewShortArraySniff extends Sniff { @@ -31,6 +30,8 @@ class NewShortArraySniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -45,6 +46,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -60,7 +63,7 @@ public function process(File $phpcsFile, $stackPtr) $tokens = $phpcsFile->getTokens(); $token = $tokens[$stackPtr]; - $error = '%s is available since 5.4'; + $error = '%s is not supported in PHP 5.3 or lower'; $data = array(); if ($token['type'] === 'T_OPEN_SHORT_ARRAY') { diff --git a/PHPCompatibility/Sniffs/Syntax/RemovedCurlyBraceArrayAccessSniff.php b/PHPCompatibility/Sniffs/Syntax/RemovedCurlyBraceArrayAccessSniff.php new file mode 100644 index 00000000..b2c29e5f --- /dev/null +++ b/PHPCompatibility/Sniffs/Syntax/RemovedCurlyBraceArrayAccessSniff.php @@ -0,0 +1,362 @@ +newArrayStringDereferencing = new NewArrayStringDereferencingSniff(); + $this->newClassMemberAccess = new NewClassMemberAccessSniff(); + $this->newFunctionArrayDereferencing = new NewFunctionArrayDereferencingSniff(); + } + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 9.3.0 + * + * @return array + */ + public function register() + { + $targets = array( + array( + \T_VARIABLE, + \T_STRING, // Constants. + ), + ); + + // Registers T_ARRAY, T_OPEN_SHORT_ARRAY and T_CONSTANT_ENCAPSED_STRING. + $additionalTargets = $this->newArrayStringDereferencing->register(); + $this->newArrayStringDereferencingTargets = array_flip($additionalTargets); + $targets[] = $additionalTargets; + + // Registers T_NEW and T_CLONE. + $additionalTargets = $this->newClassMemberAccess->register(); + $this->newClassMemberAccessTargets = array_flip($additionalTargets); + $targets[] = $additionalTargets; + + // Registers T_STRING. + $additionalTargets = $this->newFunctionArrayDereferencing->register(); + $this->newFunctionArrayDereferencingTargets = array_flip($additionalTargets); + $targets[] = $additionalTargets; + + return call_user_func_array('array_merge', $targets); + } + + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('7.4') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $braces = array(); + + // Note: Overwriting braces in each `if` is fine as only one will match anyway. + if ($tokens[$stackPtr]['code'] === \T_VARIABLE) { + $braces = $this->isVariableArrayAccess($phpcsFile, $stackPtr); + } + + if (isset($this->newArrayStringDereferencingTargets[$tokens[$stackPtr]['code']])) { + $dereferencing = $this->newArrayStringDereferencing->isArrayStringDereferencing($phpcsFile, $stackPtr); + if (isset($dereferencing['braces'])) { + $braces = $dereferencing['braces']; + } + } + + if (isset($this->newClassMemberAccessTargets[$tokens[$stackPtr]['code']])) { + $braces = $this->newClassMemberAccess->isClassMemberAccess($phpcsFile, $stackPtr); + } + + if (isset($this->newFunctionArrayDereferencingTargets[$tokens[$stackPtr]['code']])) { + $braces = $this->newFunctionArrayDereferencing->isFunctionArrayDereferencing($phpcsFile, $stackPtr); + } + + if (empty($braces) && $tokens[$stackPtr]['code'] === \T_STRING) { + $braces = $this->isConstantArrayAccess($phpcsFile, $stackPtr); + } + + if (empty($braces)) { + return; + } + + foreach ($braces as $open => $close) { + // Some of the functions will sniff for both curlies as well as square braces. + if ($tokens[$open]['code'] !== \T_OPEN_CURLY_BRACKET) { + continue; + } + + // Make sure there is something between the braces, otherwise it's still not curly brace array access. + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($open + 1), $close, true); + if ($nextNonEmpty === false) { + // Nothing between the brackets. Parse error. Ignore. + continue; + } + + // OK, so we've found curly brace array access. + $snippet = $phpcsFile->getTokensAsString($stackPtr, (($close - $stackPtr) + 1)); + $fix = $phpcsFile->addFixableWarning( + 'Curly brace syntax for accessing array elements and string offsets has been deprecated in PHP 7.4. Found: %s', + $open, + 'Found', + array($snippet) + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken($open, '['); + $phpcsFile->fixer->replaceToken($close, ']'); + $phpcsFile->fixer->endChangeset(); + } + } + } + + + /** + * Determine whether a variable is being dereferenced using curly brace syntax. + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return array An array with the stack pointers to the open/close braces of + * the curly brace array access, or an empty array if no curly + * brace array access was detected. + */ + protected function isVariableArrayAccess(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $current = $stackPtr; + $braces = array(); + + do { + $current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true); + if ($current === false) { + break; + } + + // Skip over square bracket array access. Bracket styles can be mixed. + if ($tokens[$current]['code'] === \T_OPEN_SQUARE_BRACKET + && isset($tokens[$current]['bracket_closer']) === true + && $current === $tokens[$current]['bracket_opener'] + ) { + $current = $tokens[$current]['bracket_closer']; + continue; + } + + // Handle property access. + if ($tokens[$current]['code'] === \T_OBJECT_OPERATOR) { + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true); + if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) { + // Live coding or parse error. + break; + } + + $current = $nextNonEmpty; + continue; + } + + if ($tokens[$current]['code'] === \T_OPEN_CURLY_BRACKET) { + if (isset($tokens[$current]['bracket_closer']) === false) { + // Live coding or parse error. + break; + } + + $braces[$current] = $tokens[$current]['bracket_closer']; + + // Continue, just in case there is nested access using curly braces, i.e. `$a{$i}{$j};`. + $current = $tokens[$current]['bracket_closer']; + continue; + } + + // If we're still here, we've reached the end of the variable. + break; + + } while (true); + + return $braces; + } + + + /** + * Determine whether a T_STRING is a constant being dereferenced using curly brace syntax. + * + * {@internal Note: the first braces for array access to a constant, for some unknown reason, + * can never be curlies, but have to be square brackets. + * Subsequent braces can be curlies.} + * + * @since 9.3.0 + * + * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return array An array with the stack pointers to the open/close braces of + * the curly brace array access, or an empty array if no curly + * brace array access was detected. + */ + protected function isConstantArrayAccess(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + + if ($this->isUseOfGlobalConstant($phpcsFile, $stackPtr) === false + && $tokens[$prevNonEmpty]['code'] !== \T_DOUBLE_COLON // Class constant access. + ) { + return array(); + } + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + return array(); + } + + if ($tokens[$nextNonEmpty]['code'] !== \T_OPEN_SQUARE_BRACKET + || isset($tokens[$nextNonEmpty]['bracket_closer']) === false + ) { + // Array access for constants must start with square brackets. + return array(); + } + + $current = $tokens[$nextNonEmpty]['bracket_closer']; + $braces = array(); + + do { + $current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true); + if ($current === false) { + break; + } + + // Skip over square bracket array access. Bracket styles can be mixed. + if ($tokens[$current]['code'] === \T_OPEN_SQUARE_BRACKET + && isset($tokens[$current]['bracket_closer']) === true + && $current === $tokens[$current]['bracket_opener'] + ) { + $current = $tokens[$current]['bracket_closer']; + continue; + } + + if ($tokens[$current]['code'] === \T_OPEN_CURLY_BRACKET) { + if (isset($tokens[$current]['bracket_closer']) === false) { + // Live coding or parse error. + break; + } + + $braces[$current] = $tokens[$current]['bracket_closer']; + + // Continue, just in case there is nested access using curly braces, i.e. `$a{$i}{$j};`. + $current = $tokens[$current]['bracket_closer']; + continue; + } + + // If we're still here, we've reached the end of the variable. + break; + + } while (true); + + return $braces; + } +} diff --git a/PHPCompatibility/Sniffs/Syntax/RemovedNewReferenceSniff.php b/PHPCompatibility/Sniffs/Syntax/RemovedNewReferenceSniff.php index c793ac5a..ad76828c 100644 --- a/PHPCompatibility/Sniffs/Syntax/RemovedNewReferenceSniff.php +++ b/PHPCompatibility/Sniffs/Syntax/RemovedNewReferenceSniff.php @@ -1,13 +1,11 @@ - * @copyright 2012 Cu.be Solutions bvba + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Syntax; @@ -17,16 +15,17 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Syntax\RemovedNewReferenceSniff. + * Detect the use of assigning the return value of `new` by reference. * - * Discourages the use of assigning the return value of new by reference + * This syntax has been deprecated since PHP 5.3 and removed in PHP 7.0. * - * PHP version 5.4 + * PHP version 5.3 + * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden - * @copyright 2012 Cu.be Solutions bvba + * @link https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7 + * + * @since 5.5 + * @since 9.0.0 Renamed from `DeprecatedNewReferenceSniff` to `RemovedNewReferenceSniff`. */ class RemovedNewReferenceSniff extends Sniff { @@ -34,6 +33,8 @@ class RemovedNewReferenceSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * * @return array */ public function register() @@ -44,6 +45,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/TextStrings/NewUnicodeEscapeSequenceSniff.php b/PHPCompatibility/Sniffs/TextStrings/NewUnicodeEscapeSequenceSniff.php new file mode 100644 index 00000000..aa5a39a3 --- /dev/null +++ b/PHPCompatibility/Sniffs/TextStrings/NewUnicodeEscapeSequenceSniff.php @@ -0,0 +1,162 @@ +getTokens(); + + // Check whether this is a single quoted or double quoted string. + if ($tokens[$stackPtr]['code'] === \T_CONSTANT_ENCAPSED_STRING) { + + // Find the start of the - potentially multi-line - text string. + $start = $stackPtr; + for ($i = ($stackPtr - 1); $i >= 0; $i--) { + if ($tokens[$i]['code'] === \T_WHITESPACE) { + continue; + } + + if ($tokens[$i]['code'] === \T_CONSTANT_ENCAPSED_STRING) { + $start = $i; + continue; + } + + break; + } + + try { + $textString = $this->getCompleteTextString($phpcsFile, $start, false); + } catch (PHPCS_Exception $e) { + // Something went wrong determining the start of the text string. + return; + } + + $startQuote = $textString[0]; + $endQuote = substr($textString, -1); + if (($startQuote === "'" && $endQuote === "'") + || $startQuote !== $endQuote + ) { + // Single quoted string, not our concern. + return; + } + } + + $content = $this->stripQuotes($tokens[$stackPtr]['content']); + $count = preg_match_all('`(?isValidUnicodeEscapeSequence($match[1]); + } + + if ($this->supportsBelow('5.6') === true && $valid === true) { + $phpcsFile->addError( + 'Unicode codepoint escape sequences are not supported in PHP 5.6 or earlier. Found: %s', + $stackPtr, + 'Found', + array($match[0]) + ); + } + + if ($this->supportsAbove('7.0') === true && $valid === false) { + $phpcsFile->addError( + 'Strings containing a literal \u{ followed by an invalid unicode codepoint escape sequence will cause a fatal error in PHP 7.0 and above. Escape the leading backslash to prevent this. Found: %s', + $stackPtr, + 'Invalid', + array($match[0]) + ); + } + } + } + + + /** + * Verify if the codepoint in a unicode escape sequence is valid. + * + * @since 9.3.0 + * + * @param string $codepoint The codepoint as a string. + * + * @return bool + */ + protected function isValidUnicodeEscapeSequence($codepoint) + { + if (trim($codepoint) === '') { + return false; + } + + // Check if it's a valid hex codepoint. + if (preg_match('`^[0-9A-F]+$`iD', $codepoint, $match) !== 1) { + return false; + } + + if (hexdec($codepoint) > 1114111) { + // Outside of the maximum permissable range. + return false; + } + + return true; + } +} diff --git a/PHPCompatibility/Sniffs/TypeCasts/NewTypeCastsSniff.php b/PHPCompatibility/Sniffs/TypeCasts/NewTypeCastsSniff.php index a06c5eb0..12e47318 100644 --- a/PHPCompatibility/Sniffs/TypeCasts/NewTypeCastsSniff.php +++ b/PHPCompatibility/Sniffs/TypeCasts/NewTypeCastsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\TypeCasts; @@ -14,11 +15,13 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\TypeCasts\NewTypeCastsSniff. + * Detect use of newly introduced type casts. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version All + * + * @link https://www.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting + * + * @since 8.0.1 */ class NewTypeCastsSniff extends AbstractNewFeatureSniff { @@ -29,7 +32,9 @@ class NewTypeCastsSniff extends AbstractNewFeatureSniff * The array lists : version number with false (not present) or true (present). * If's sufficient to list the first version where the keyword appears. * - * @var array(string => array(string => int|string|null)) + * @since 8.0.1 + * + * @var array(string => array(string => bool|string)) */ protected $newTypeCasts = array( 'T_UNSET_CAST' => array( @@ -48,13 +53,15 @@ class NewTypeCastsSniff extends AbstractNewFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.0.1 + * * @return array */ public function register() { $tokens = array(); foreach ($this->newTypeCasts as $token => $versions) { - if (defined($token)) { + if (\defined($token)) { $tokens[] = constant($token); } } @@ -80,6 +87,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.0.1 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -132,6 +141,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 8.0.1 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -145,6 +156,8 @@ public function getItemArray(array $itemInfo) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 8.0.1 + * * @return array */ protected function getNonVersionArrayKeys() @@ -156,6 +169,8 @@ protected function getNonVersionArrayKeys() /** * Retrieve the relevant detail (version) information for use in an error message. * + * @since 8.0.1 + * * @param array $itemArray Version and other information about the item. * @param array $itemInfo Base information about the item. * @@ -173,6 +188,8 @@ public function getErrorInfo(array $itemArray, array $itemInfo) /** * Filter the error message before it's passed to PHPCS. * + * @since 8.0.1 + * * @param string $error The error message which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. @@ -188,6 +205,8 @@ protected function filterErrorMsg($error, array $itemInfo, array $errorInfo) /** * Filter the error data before it's passed to PHPCS. * + * @since 8.0.1 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/Sniffs/TypeCasts/RemovedTypeCastsSniff.php b/PHPCompatibility/Sniffs/TypeCasts/RemovedTypeCastsSniff.php index 07e74bfa..3791f0b5 100644 --- a/PHPCompatibility/Sniffs/TypeCasts/RemovedTypeCastsSniff.php +++ b/PHPCompatibility/Sniffs/TypeCasts/RemovedTypeCastsSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\TypeCasts; @@ -13,11 +14,16 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\TypeCasts\RemovedTypeCastsSniff. + * Detect use of deprecated/removed type casts. * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * PHP version All + * + * @link https://www.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting + * @link https://wiki.php.net/rfc/deprecations_php_7_2#unset_cast + * @link https://wiki.php.net/rfc/deprecations_php_7_4#the_real_type + * + * @since 8.0.1 + * @since 9.0.0 Renamed from `DeprecatedTypeCastsSniff` to `RemovedTypeCastsSniff`. */ class RemovedTypeCastsSniff extends AbstractRemovedFeatureSniff { @@ -27,7 +33,9 @@ class RemovedTypeCastsSniff extends AbstractRemovedFeatureSniff * The array lists : version number with false (deprecated) or true (removed) and an alternative function. * If no alternative exists, it is NULL, i.e, the function should just not be used. * - * @var array(string => array(string => bool|string|null)) + * @since 8.0.1 + * + * @var array(string => array(string => bool|string)) */ protected $deprecatedTypeCasts = array( 'T_UNSET_CAST' => array( @@ -35,12 +43,19 @@ class RemovedTypeCastsSniff extends AbstractRemovedFeatureSniff 'alternative' => 'unset()', 'description' => 'unset', ), + 'T_DOUBLE_CAST' => array( + '7.4' => false, + 'alternative' => '(float)', + 'description' => 'real', + ), ); /** * Returns an array of tokens this test wants to listen for. * + * @since 8.0.1 + * * @return array */ public function register() @@ -57,6 +72,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.0.1 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -68,6 +85,12 @@ public function process(File $phpcsFile, $stackPtr) $tokens = $phpcsFile->getTokens(); $tokenType = $tokens[$stackPtr]['type']; + // Special case `T_DOUBLE_CAST` as the same token is used for (float) and (double) casts. + if ($tokenType === 'T_DOUBLE_CAST' && strpos($tokens[$stackPtr]['content'], 'real') === false) { + // Float/double casts, not (real) cast. + return; + } + $itemInfo = array( 'name' => $tokenType, 'description' => $this->deprecatedTypeCasts[$tokenType]['description'], @@ -79,6 +102,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get an array of the non-PHP-version array keys used in a sub-array. * + * @since 8.0.1 + * * @return array */ protected function getNonVersionArrayKeys() @@ -89,6 +114,8 @@ protected function getNonVersionArrayKeys() /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 8.0.1 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -102,6 +129,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 8.0.1 + * * @return string */ protected function getErrorMsgTemplate() @@ -113,6 +142,8 @@ protected function getErrorMsgTemplate() /** * Filter the error data before it's passed to PHPCS. * + * @since 8.0.1 + * * @param array $data The error data array which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. diff --git a/PHPCompatibility/Sniffs/Upgrade/LowPHPCSSniff.php b/PHPCompatibility/Sniffs/Upgrade/LowPHPCSSniff.php index 06f81cbf..1e79873c 100644 --- a/PHPCompatibility/Sniffs/Upgrade/LowPHPCSSniff.php +++ b/PHPCompatibility/Sniffs/Upgrade/LowPHPCSSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Upgrade; @@ -14,8 +15,6 @@ use PHP_CodeSniffer_File as File; /** - * \PHPCompatibility\Sniffs\Upgrade\LowPHPCSSniff. - * * Add a notification for users of low PHPCS versions. * * Originally PHPCompatibility supported PHPCS 1.5.x, 2.x and since PHPCompatibility 8.0.0, 3.x. @@ -28,9 +27,10 @@ * This sniff adds an explicit error/warning for users of the standard * using a PHPCS version below the recommended version. * - * @category Upgrade - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/688 + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/835 + * + * @since 8.2.0 */ class LowPHPCSSniff extends Sniff { @@ -39,18 +39,24 @@ class LowPHPCSSniff extends Sniff * * Users on PHPCS versions below this will see an ERROR message. * + * @since 8.2.0 + * @since 9.3.0 Changed from $minSupportedVersion property to a constant. + * * @var string */ - protected $minSupportedVersion = '2.3.0'; + const MIN_SUPPORTED_VERSION = '2.3.0'; /** * The minimum recommended PHPCS version. * * Users on PHPCS versions below this will see a WARNING. * + * @since 8.2.0 + * @since 9.3.0 Changed from $minRecommendedVersion property to a constant. + * * @var string */ - protected $minRecommendedVersion = '2.6.0'; + const MIN_RECOMMENDED_VERSION = '2.6.0'; /** * Keep track of whether this sniff needs to actually run. @@ -59,6 +65,8 @@ class LowPHPCSSniff extends Sniff * version is detected or once the error/warning has been thrown, * to make sure that the notice will only be thrown once per run. * + * @since 8.2.0 + * * @var bool */ private $examine = true; @@ -67,6 +75,8 @@ class LowPHPCSSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 8.2.0 + * * @return array */ public function register() @@ -79,11 +89,14 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. * - * @return void + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. */ public function process(File $phpcsFile, $stackPtr) { @@ -95,29 +108,29 @@ public function process(File $phpcsFile, $stackPtr) $phpcsVersion = PHPCSHelper::getVersion(); // Don't do anything if the PHPCS version used is above the minimum recommended version. - if (version_compare($phpcsVersion, $this->minRecommendedVersion, '>=')) { + if (version_compare($phpcsVersion, self::MIN_RECOMMENDED_VERSION, '>=')) { $this->examine = false; return ($phpcsFile->numTokens + 1); } - if (version_compare($phpcsVersion, $this->minSupportedVersion, '<')) { + if (version_compare($phpcsVersion, self::MIN_SUPPORTED_VERSION, '<')) { $isError = true; - $message = "IMPORTANT: Please be advised that the minimum PHP_CodeSniffer version the PHPCompatibility standard supports is %s. You are currently using PHP_CodeSniffer %s. Please upgrade your PHP_CodeSniffer installation. The recommended version of PHP_CodeSniffer for PHPCompatibility is %s or higher."; - $errorCode = 'Unsupported_' . $this->stringToErrorCode($this->minSupportedVersion); + $message = 'IMPORTANT: Please be advised that the minimum PHP_CodeSniffer version the PHPCompatibility standard supports is %s. You are currently using PHP_CodeSniffer %s. Please upgrade your PHP_CodeSniffer installation. The recommended version of PHP_CodeSniffer for PHPCompatibility is %s or higher.'; + $errorCode = 'Unsupported_' . $this->stringToErrorCode(self::MIN_SUPPORTED_VERSION); $replacements = array( - $this->minSupportedVersion, + self::MIN_SUPPORTED_VERSION, $phpcsVersion, - $this->minRecommendedVersion, + self::MIN_RECOMMENDED_VERSION, $errorCode, ); } else { $isError = false; - $message = "IMPORTANT: Please be advised that for the most reliable PHPCompatibility results, PHP_CodeSniffer %s or higher should be used. Support for lower versions will be dropped in the foreseeable future. You are currently using PHP_CodeSniffer %s. Please upgrade your PHP_CodeSniffer installation to version %s or higher."; - $errorCode = 'BelowRecommended_' . $this->stringToErrorCode($this->minRecommendedVersion); + $message = 'IMPORTANT: Please be advised that for the most reliable PHPCompatibility results, PHP_CodeSniffer %s or higher should be used. Support for lower versions will be dropped in the foreseeable future. You are currently using PHP_CodeSniffer %s. Please upgrade your PHP_CodeSniffer installation to version %s or higher.'; + $errorCode = 'BelowRecommended_' . $this->stringToErrorCode(self::MIN_RECOMMENDED_VERSION); $replacements = array( - $this->minRecommendedVersion, + self::MIN_RECOMMENDED_VERSION, $phpcsVersion, - $this->minRecommendedVersion, + self::MIN_RECOMMENDED_VERSION, $errorCode, ); } @@ -146,11 +159,14 @@ public function process(File $phpcsFile, $stackPtr) * it should be. A patch for that is included in the same upstream PR. * * If/when the upstream PR has been merged and the minimum supported/recommended version - * of PHPCompatibility would go beyond that, the below code should be adjusted.}} + * of PHPCompatibility would go beyond that, the below code should be adjusted.} */ $reportWidth = PHPCSHelper::getCommandLineData($phpcsFile, 'reportWidth'); + if (empty($reportWidth)) { + $reportWidth = 80; + } $showSources = PHPCSHelper::getCommandLineData($phpcsFile, 'showSources'); - if ($showSources === true && version_compare($phpcsVersion, '2.3.0', '>=')) { + if ($showSources === true) { $reportWidth += 6; } diff --git a/PHPCompatibility/Sniffs/Upgrade/LowPHPSniff.php b/PHPCompatibility/Sniffs/Upgrade/LowPHPSniff.php new file mode 100644 index 00000000..7a171633 --- /dev/null +++ b/PHPCompatibility/Sniffs/Upgrade/LowPHPSniff.php @@ -0,0 +1,182 @@ +examine === false) { + return ($phpcsFile->numTokens + 1); + } + + $phpVersion = phpversion(); + + // Don't do anything if the PHPCS version used is above the minimum recommended version. + if (version_compare($phpVersion, self::MIN_RECOMMENDED_VERSION, '>=')) { + $this->examine = false; + return ($phpcsFile->numTokens + 1); + } + + if (version_compare($phpVersion, self::MIN_SUPPORTED_VERSION, '<')) { + $isError = true; + $message = 'IMPORTANT: Please be advised that the minimum PHP version the PHPCompatibility standard supports is %s. You are currently using PHP %s. Please upgrade your PHP installation. The recommended version of PHP for PHPCompatibility is %s or higher.'; + $errorCode = 'Unsupported_' . $this->stringToErrorCode(self::MIN_SUPPORTED_VERSION); + $replacements = array( + self::MIN_SUPPORTED_VERSION, + $phpVersion, + self::MIN_RECOMMENDED_VERSION, + $errorCode, + ); + } else { + $isError = false; + $message = 'IMPORTANT: Please be advised that for the most reliable PHPCompatibility results, PHP %s or higher should be used. Support for lower versions will be dropped in the foreseeable future. You are currently using PHP %s. Please upgrade your PHP installation to version %s or higher.'; + $errorCode = 'BelowRecommended_' . $this->stringToErrorCode(self::MIN_RECOMMENDED_VERSION); + $replacements = array( + self::MIN_RECOMMENDED_VERSION, + $phpVersion, + self::MIN_RECOMMENDED_VERSION, + $errorCode, + ); + } + + /* + * Figure out the report width to determine how long the delimiter lines should be. + * + * This is not an exact calculation as there are a number of unknowns at the time the + * notice is thrown (whether there are other notices for the file, whether those are + * warnings or errors, whether there are auto-fixable issues etc). + * + * In other words, this is just an approximation to get a reasonably stable and + * readable message layout format. + * + * {@internal + * PHPCS has had some changes as to how the messages display over the years. + * Most significantly in 2.4.0 it was attempted to solve an issue with messages + * containing new lines. Unfortunately, that solution is buggy. + * An improved version has been pulled upstream and will hopefully make it + * into PHPCS 3.3.1/3.4.0. + * + * Anyway, this means that instead of new lines, delimiter lines will be used to improved + * the readability of the (long) message. + * + * Also, as of PHPCS 2.2.0, the report width when using the `-s` option is 8 wider than + * it should be. A patch for that is included in the same upstream PR. + * + * If/when the upstream PR has been merged and the minimum supported/recommended version + * of PHPCompatibility would go beyond that, the below code should be adjusted.} + */ + $reportWidth = PHPCSHelper::getCommandLineData($phpcsFile, 'reportWidth'); + if (empty($reportWidth)) { + $reportWidth = 80; + } + $showSources = PHPCSHelper::getCommandLineData($phpcsFile, 'showSources'); + if ($showSources === true) { + $reportWidth += 6; + } + + $messageWidth = ($reportWidth - 15); // 15 is length of " # | WARNING | ". + $delimiterLine = str_repeat('-', ($messageWidth)); + $disableNotice = 'To disable this notice, add --exclude=PHPCompatibility.Upgrade.LowPHP to your command or add to your custom ruleset. '; + $thankYou = 'Thank you for using PHPCompatibility!'; + + $message .= ' ' . $delimiterLine; + $message .= ' ' . $disableNotice; + $message .= ' ' . $delimiterLine; + $message .= ' ' . $thankYou; + + $this->addMessage($phpcsFile, $message, 0, $isError, $errorCode, $replacements); + + $this->examine = false; + } +} diff --git a/PHPCompatibility/Sniffs/UseDeclarations/NewGroupUseDeclarationsSniff.php b/PHPCompatibility/Sniffs/UseDeclarations/NewGroupUseDeclarationsSniff.php index 82dc5e02..a681a4a3 100644 --- a/PHPCompatibility/Sniffs/UseDeclarations/NewGroupUseDeclarationsSniff.php +++ b/PHPCompatibility/Sniffs/UseDeclarations/NewGroupUseDeclarationsSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\UseDeclarations; @@ -16,24 +15,36 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\UseDeclarations\NewGroupUseDeclarationsSniff. + * Detect group use declarations as introduced in PHP 7.0. + * + * Checks for: + * - Group use statements as introduced in PHP 7.0. + * - Trailing comma's in group use statements as allowed since PHP 7.2. * * PHP version 7.0 + * PHP version 7.2 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.group-use-declarations + * @link https://www.php.net/manual/en/migration72.new-features.php#migration72.new-features.trailing-comma-in-grouped-namespaces + * @link https://wiki.php.net/rfc/group_use_declarations + * @link https://wiki.php.net/rfc/list-syntax-trailing-commas + * @link https://www.php.net/manual/en/language.namespaces.importing.php#language.namespaces.importing.group + * + * @since 7.0.0 + * @since 8.0.1 Now also checks for trailing comma's in group `use` declarations. */ class NewGroupUseDeclarationsSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() { - if (defined('T_OPEN_USE_GROUP')) { + if (\defined('T_OPEN_USE_GROUP')) { return array(\T_OPEN_USE_GROUP); } else { return array(\T_USE); @@ -44,6 +55,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. @@ -84,7 +97,7 @@ public function process(File $phpcsFile, $stackPtr) } $closers = array(\T_CLOSE_CURLY_BRACKET); - if (defined('T_CLOSE_USE_GROUP')) { + if (\defined('T_CLOSE_USE_GROUP')) { $closers[] = \T_CLOSE_USE_GROUP; } diff --git a/PHPCompatibility/Sniffs/UseDeclarations/NewUseConstFunctionSniff.php b/PHPCompatibility/Sniffs/UseDeclarations/NewUseConstFunctionSniff.php index 22144272..2d166fb5 100644 --- a/PHPCompatibility/Sniffs/UseDeclarations/NewUseConstFunctionSniff.php +++ b/PHPCompatibility/Sniffs/UseDeclarations/NewUseConstFunctionSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\UseDeclarations; @@ -16,17 +15,19 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\UseDeclarations\NewUseConstFunctionSniff. + * Detect importing constants and functions via a `use` statement. * - * The use operator has been extended to support importing functions and - * constants in addition to classes. This is achieved via the use function - * and use const constructs, respectively. + * The `use` operator has been extended to support importing functions and + * constants in addition to classes. This is achieved via the `use function` + * and `use const` constructs, respectively. * * PHP version 5.6 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.use + * @link https://wiki.php.net/rfc/use_function + * @link https://www.php.net/manual/en/language.namespaces.importing.php + * + * @since 7.1.4 */ class NewUseConstFunctionSniff extends Sniff { @@ -34,6 +35,8 @@ class NewUseConstFunctionSniff extends Sniff /** * A list of keywords that can follow use statements. * + * @since 7.1.4 + * * @var array(string => string) */ protected $validUseNames = array( @@ -44,6 +47,8 @@ class NewUseConstFunctionSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.4 + * * @return array */ public function register() @@ -55,6 +60,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.4 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. diff --git a/PHPCompatibility/Sniffs/Variables/ForbiddenGlobalVariableVariableSniff.php b/PHPCompatibility/Sniffs/Variables/ForbiddenGlobalVariableVariableSniff.php index 0fb6fdde..c7f715d2 100644 --- a/PHPCompatibility/Sniffs/Variables/ForbiddenGlobalVariableVariableSniff.php +++ b/PHPCompatibility/Sniffs/Variables/ForbiddenGlobalVariableVariableSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Variables; @@ -16,15 +15,13 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Variables\ForbiddenGlobalVariableVariableSniff. - * - * Variable variables are forbidden with global + * Detect use of `global` with variable variables, support for which has been removed in PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://wiki.php.net/rfc/uniform_variable_syntax#global_keyword_takes_only_simple_variables + * + * @since 7.0.0 */ class ForbiddenGlobalVariableVariableSniff extends Sniff { @@ -32,6 +29,8 @@ class ForbiddenGlobalVariableVariableSniff extends Sniff /** * Returns an array of tokens this test wants to listen for. * + * @since 7.0.0 + * * @return array */ public function register() @@ -42,6 +41,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -78,7 +79,7 @@ public function process(File $phpcsFile, $stackPtr) $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($variable + 1), $varEnd, true); if ($next !== false - && in_array($tokens[$next]['code'], array(\T_OPEN_SQUARE_BRACKET, \T_OBJECT_OPERATOR, \T_DOUBLE_COLON), true) === true + && \in_array($tokens[$next]['code'], array(\T_OPEN_SQUARE_BRACKET, \T_OBJECT_OPERATOR, \T_DOUBLE_COLON), true) === true ) { $phpcsFile->addError( 'Global with variable variables is not allowed since PHP 7.0. Found %s', diff --git a/PHPCompatibility/Sniffs/Variables/ForbiddenThisUseContextsSniff.php b/PHPCompatibility/Sniffs/Variables/ForbiddenThisUseContextsSniff.php index 081a4c8a..fc0ddc65 100644 --- a/PHPCompatibility/Sniffs/Variables/ForbiddenThisUseContextsSniff.php +++ b/PHPCompatibility/Sniffs/Variables/ForbiddenThisUseContextsSniff.php @@ -3,7 +3,7 @@ * PHPCompatibility, an external standard for PHP_CodeSniffer. * * @package PHPCompatibility - * @copyright 2012-2018 PHPCompatibility Contributors + * @copyright 2012-2019 PHPCompatibility Contributors * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 * @link https://github.com/PHPCompatibility/PHPCompatibility */ @@ -16,11 +16,11 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * Detect using $this in incompatible contexts. + * Detect using `$this` in incompatible contexts. * - * "Whilst $this is considered a special variable in PHP, it lacked proper checks + * "Whilst `$this` is considered a special variable in PHP, it lacked proper checks * to ensure it wasn't used as a variable name or reassigned. This has now been - * rectified to ensure that $this cannot be a user-defined variable, reassigned + * rectified to ensure that `$this` cannot be a user-defined variable, reassigned * to a different value, or be globalised." * * This sniff only addresses those situations which did *not* throw an error prior @@ -29,20 +29,21 @@ * be sniffed for: * - Using $this as static variable. (error _message_ change only). * - * Also, the changes with relation to assigning $this dynamically can not be + * Also, the changes with relation to assigning `$this` dynamically can not be * sniffed for reliably, so are not covered by this sniff. - * - Disable ability to re-assign $this indirectly through $$. - * - Disable ability to re-assign $this indirectly through reference. - * - Disable ability to re-assign $this indirectly through extract() and parse_str(). + * - Disable ability to re-assign `$this` indirectly through `$$`. + * - Disable ability to re-assign `$this` indirectly through reference. + * - Disable ability to re-assign `$this` indirectly through `extract()` and `parse_str()`. * * Other changes not (yet) covered: - * - get_defined_vars() always doesn't show value of variable $this. - * - Always show true $this value in magic method __call(). + * - `get_defined_vars()` always doesn't show value of variable `$this`. + * - Always show true `$this` value in magic method `__call()`. * {@internal This could possibly be covered. Similar logic as "outside object context", - * but with function name check and supportsBelow('7.0').}} + * but with function name check and supportsBelow('7.0').} * * PHP version 7.1 * + * @link https://www.php.net/manual/en/migration71.other-changes.php#migration71.other-changes.inconsistency-fixes-to-this * @link https://wiki.php.net/rfc/this_var * * @since 9.1.0 @@ -98,7 +99,7 @@ class ForbiddenThisUseContextsSniff extends Sniff */ public function register() { - if (defined('T_ANON_CLASS')) { + if (\defined('T_ANON_CLASS')) { $this->ooScopeTokens['T_ANON_CLASS'] = \T_ANON_CLASS; } diff --git a/PHPCompatibility/Sniffs/Variables/NewUniformVariableSyntaxSniff.php b/PHPCompatibility/Sniffs/Variables/NewUniformVariableSyntaxSniff.php index 8ec3fc07..f7948079 100644 --- a/PHPCompatibility/Sniffs/Variables/NewUniformVariableSyntaxSniff.php +++ b/PHPCompatibility/Sniffs/Variables/NewUniformVariableSyntaxSniff.php @@ -1,12 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Variables; @@ -16,21 +15,23 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Variables\NewUniformVariableSyntax. - * * The interpretation of variable variables has changed in PHP 7.0. * * PHP version 7.0 * - * @category PHP - * @package PHPCompatibility - * @author Juliette Reinders Folmer + * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.indirect + * @link https://wiki.php.net/rfc/uniform_variable_syntax + * + * @since 7.1.2 + * @since 9.0.0 Renamed from `VariableVariablesSniff` to `NewUniformVariableSyntaxSniff`. */ class NewUniformVariableSyntaxSniff extends Sniff { /** * Returns an array of tokens this test wants to listen for. * + * @since 7.1.2 + * * @return array */ public function register() @@ -41,6 +42,8 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 7.1.2 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. @@ -64,7 +67,7 @@ public function process(File $phpcsFile, $stackPtr) // The previous non-empty token has to be a $, -> or ::. $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true); - if ($prevToken === false || in_array($tokens[$prevToken]['code'], array(\T_DOLLAR, \T_OBJECT_OPERATOR, \T_DOUBLE_COLON), true) === false) { + if ($prevToken === false || \in_array($tokens[$prevToken]['code'], array(\T_DOLLAR, \T_OBJECT_OPERATOR, \T_DOUBLE_COLON), true) === false) { return; } diff --git a/PHPCompatibility/Sniffs/Variables/RemovedPredefinedGlobalVariablesSniff.php b/PHPCompatibility/Sniffs/Variables/RemovedPredefinedGlobalVariablesSniff.php index 27c99b8c..c814e7dc 100644 --- a/PHPCompatibility/Sniffs/Variables/RemovedPredefinedGlobalVariablesSniff.php +++ b/PHPCompatibility/Sniffs/Variables/RemovedPredefinedGlobalVariablesSniff.php @@ -1,10 +1,11 @@ + * @package PHPCompatibility + * @copyright 2012-2019 PHPCompatibility Contributors + * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 + * @link https://github.com/PHPCompatibility/PHPCompatibility */ namespace PHPCompatibility\Sniffs\Variables; @@ -15,13 +16,20 @@ use PHP_CodeSniffer_Tokens as Tokens; /** - * \PHPCompatibility\Sniffs\Variables\RemovedPredefinedGlobalVariablesSniff. + * Detect the use of removed global variables. Suggests alternatives if available. * - * Discourages the use of removed global variables. Suggests alternative extensions if available + * PHP version 5.3+ * - * @category PHP - * @package PHPCompatibility - * @author Wim Godden + * @link https://wiki.php.net/rfc/deprecations_php_7_2#php_errormsg + * + * @since 5.5 Introduced `LongArrays` sniff. + * @since 7.0 Introduced `RemovedGlobalVariables` sniff. + * @since 7.0.7 The `LongArrays` sniff now throws a warning for deprecated and an error for removed. + * Previously the `LongArrays` sniff would always throw a warning. + * @since 7.1.0 The `RemovedGlobalVariables` sniff now extends the `AbstractNewFeatureSniff` + * instead of the base `Sniff` class. + * @since 7.1.3 Merged the `LongArrays` sniff into the `RemovedGlobalVariables` sniff. + * @since 9.0.0 Renamed from `RemovedGlobalVariablesSniff` to `RemovedPredefinedGlobalVariablesSniff`. */ class RemovedPredefinedGlobalVariablesSniff extends AbstractRemovedFeatureSniff { @@ -32,7 +40,10 @@ class RemovedPredefinedGlobalVariablesSniff extends AbstractRemovedFeatureSniff * The array lists : version number with false (deprecated) and true (removed). * If's sufficient to list the first version where the variable was deprecated/removed. * - * @var array(string|null) + * @since 5.5 + * @since 7.0 + * + * @var array(string => array(string => bool|string)) */ protected $removedGlobalVariables = array( 'HTTP_POST_VARS' => array( @@ -87,6 +98,9 @@ class RemovedPredefinedGlobalVariablesSniff extends AbstractRemovedFeatureSniff /** * Returns an array of tokens this test wants to listen for. * + * @since 5.5 + * @since 7.0 + * * @return array */ public function register() @@ -98,6 +112,9 @@ public function register() /** * Processes this test, when one of its tokens is encountered. * + * @since 5.5 + * @since 7.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -148,6 +165,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Get the relevant sub-array for a specific item from a multi-dimensional array. * + * @since 7.1.0 + * * @param array $itemInfo Base information about the item. * * @return array Version and other information about the item. @@ -161,6 +180,8 @@ public function getItemArray(array $itemInfo) /** * Get the error message template for this sniff. * + * @since 7.1.0 + * * @return string */ protected function getErrorMsgTemplate() @@ -172,6 +193,8 @@ protected function getErrorMsgTemplate() /** * Filter the error message before it's passed to PHPCS. * + * @since 8.1.0 + * * @param string $error The error message which was created. * @param array $itemInfo Base information about the item this error message applies to. * @param array $errorInfo Detail information about an item this error message applies to. @@ -189,6 +212,8 @@ protected function filterErrorMsg($error, array $itemInfo, array $errorInfo) /** * Run some additional checks for the `$php_errormsg` variable. * + * @since 8.1.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. @@ -246,7 +271,7 @@ private function isTargetPHPErrormsgVar(File $phpcsFile, $stackPtr, array $token // Is this a function param shadowing the PHP native one ? if ($function !== false) { $parameters = PHPCSHelper::getMethodParameters($phpcsFile, $function); - if (is_array($parameters) === true && empty($parameters) === false) { + if (\is_array($parameters) === true && empty($parameters) === false) { foreach ($parameters as $param) { if ($param['name'] === '$php_errormsg') { return false; diff --git a/PHPCompatibility/Tests/BaseSniffTest.php b/PHPCompatibility/Tests/BaseSniffTest.php index 1ec8179b..c8dc3cd3 100644 --- a/PHPCompatibility/Tests/BaseSniffTest.php +++ b/PHPCompatibility/Tests/BaseSniffTest.php @@ -1,8 +1,11 @@ + * @since 5.5 + * @since 7.0.4 Caches sniff results per file and testVersion. + * @since 7.1.3 Compatible with PHPUnit 6. + * @since 7.1.3 Limits the sniff run to the actual sniff being tested. + * @since 8.0.0 Compatible with PHP_CodeSniffer 3+. + * @since 8.2.0 Allows for sniffs in multiple categories. + * @since 9.0.0 Dropped support for PHP_CodeSniffer 1.x. */ class BaseSniffTest extends PHPUnit_TestCase { + + /** + * The name of the standard as registered with PHPCS. + * + * @since 7.1.3 + * + * @var string + */ const STANDARD_NAME = 'PHPCompatibility'; /** @@ -30,6 +45,8 @@ class BaseSniffTest extends PHPUnit_TestCase * * Used by PHPCS 2.x. * + * @since 5.5 + * * @var \PHP_CodeSniffer */ protected static $phpcs = null; @@ -37,6 +54,8 @@ class BaseSniffTest extends PHPUnit_TestCase /** * An array of PHPCS results by filename and PHP version. * + * @since 7.0.4 + * * @var array */ public static $sniffFiles = array(); @@ -44,6 +63,8 @@ class BaseSniffTest extends PHPUnit_TestCase /** * Sets up this unit test. * + * @since 7.0.4 + * * @return void */ public static function setUpBeforeClass() @@ -55,6 +76,8 @@ public static function setUpBeforeClass() /** * Sets up this unit test. * + * @since 5.5 + * * @return void */ protected function setUp() @@ -81,6 +104,8 @@ protected function setUp() /** * Tear down after each test. * + * @since 5.5 + * * @return void */ public function tearDown() @@ -92,6 +117,8 @@ public function tearDown() /** * Tear down after each test. * + * @since 7.0.4 + * * @return void */ public static function tearDownAfterClass() @@ -102,11 +129,13 @@ public static function tearDownAfterClass() /** * Get the sniff code for the current sniff being tested. * + * @since 7.1.3 + * * @return string */ protected function getSniffCode() { - $class = get_class($this); + $class = \get_class($this); $parts = explode('\\', $class); $sniff = array_pop($parts); $sniff = str_replace('UnitTest', '', $sniff); @@ -117,6 +146,11 @@ protected function getSniffCode() /** * Sniff a file and return resulting file object. * + * @since 5.5 + * @since 9.0.0 Signature change. The `$filename` parameter was renamed to + * `$pathToFile` and now expects an absolute path instead of + * a relative one. + * * @param string $pathToFile Absolute path to the file to sniff. * Allows for passing __FILE__ from the unit test * file. In that case, the test case file is presumed @@ -169,6 +203,8 @@ public function sniffFile($pathToFile, $targetPhpVersion = 'none') /** * Assert a PHPCS error on a particular line number. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $file Codesniffer file object. * @param int $lineNumber Line number. * @param string $expectedMessage Expected error message (assertContains). @@ -185,6 +221,8 @@ public function assertError(File $file, $lineNumber, $expectedMessage) /** * Assert a PHPCS warning on a particular line number. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $file Codesniffer file object. * @param int $lineNumber Line number. * @param string $expectedMessage Expected message (assertContains). @@ -201,12 +239,17 @@ public function assertWarning(File $file, $lineNumber, $expectedMessage) /** * Assert a PHPCS error or warning on a particular line number. * + * @since 7.0.3 + * * @param array $issues Array of issues of a particular type. * @param string $type The type of issues, either 'error' or 'warning'. * @param int $lineNumber Line number. * @param string $expectedMessage Expected message (assertContains). * * @return bool + * + * @throws \Exception When no issues of a certain type where found on a line + * for which issues of that type where expected. */ private function assertForType($issues, $type, $lineNumber, $expectedMessage) { @@ -232,6 +275,8 @@ private function assertForType($issues, $type, $lineNumber, $expectedMessage) /** * Assert no violation (warning or error) on a given line number. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $file Codesniffer File object. * @param mixed $lineNumber Line number. * @@ -247,7 +292,7 @@ public function assertNoViolation(File $file, $lineNumber = 0) } if ($lineNumber === 0) { - $failMessage = 'Failed asserting no violations in file. Found ' . count($errors) . ' errors and ' . count($warnings) . ' warnings.'; + $failMessage = 'Failed asserting no violations in file. Found ' . \count($errors) . ' errors and ' . \count($warnings) . ' warnings.'; $allMessages = $errors + $warnings; // TODO: Update the fail message to give the tester some // indication of what the errors or warnings were. @@ -277,6 +322,8 @@ public function assertNoViolation(File $file, $lineNumber = 0) * * This is useful for debugging sniffs on a file. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $file Codesniffer file object. * * @return array @@ -294,6 +341,8 @@ public function showViolations(File $file) /** * Gather all error messages by line number from phpcs file result. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $file Codesniffer File object. * * @return array @@ -308,6 +357,8 @@ public function gatherErrors(File $file) /** * Gather all warning messages by line number from phpcs file result. * + * @since 5.5 + * * @param \PHP_CodeSniffer_File $file Codesniffer File object. * * @return array @@ -322,6 +373,8 @@ public function gatherWarnings(File $file) /** * Gather all messages or a particular type by line number. * + * @since 7.0.3 + * * @param array $issuesArray Array of a particular type of issues, * i.e. errors or warnings. * diff --git a/PHPCompatibility/Tests/Classes/ForbiddenAbstractPrivateMethodsUnitTest.inc b/PHPCompatibility/Tests/Classes/ForbiddenAbstractPrivateMethodsUnitTest.inc new file mode 100644 index 00000000..1855f86d --- /dev/null +++ b/PHPCompatibility/Tests/Classes/ForbiddenAbstractPrivateMethodsUnitTest.inc @@ -0,0 +1,41 @@ +markTestSkipped('Traits and abstract classes are not recognized on PHPCS < 2.4.0 in combination with PHP < 5.4'); + return; + } + + parent::setUp(); + } + + + /** + * testForbiddenAbstractPrivateMethods. + * + * @dataProvider dataForbiddenAbstractPrivateMethods + * + * @param int $line The line number where an error is expected. + * + * @return void + */ + public function testForbiddenAbstractPrivateMethods($line) + { + $file = $this->sniffFile(__FILE__, '5.1'); + $this->assertError($file, $line, 'Abstract methods cannot be declared as private since PHP 5.1'); + } + + /** + * Data provider. + * + * @see testForbiddenAbstractPrivateMethods() + * + * @return array + */ + public function dataForbiddenAbstractPrivateMethods() + { + return array( + array(28), + array(29), + array(34), + array(35), + array(39), + array(40), + ); + } + + + /** + * testNoFalsePositives. + * + * @dataProvider dataNoFalsePositives + * + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositives($line) + { + $file = $this->sniffFile(__FILE__, '5.1'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + $cases = array(); + // No errors expected on the first 24 lines. + for ($line = 1; $line <= 24; $line++) { + $cases[] = array($line); + } + + return $cases; + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '5.0'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/Classes/NewAnonymousClassesUnitTest.php b/PHPCompatibility/Tests/Classes/NewAnonymousClassesUnitTest.php index db8cc00d..aaa6ba5d 100644 --- a/PHPCompatibility/Tests/Classes/NewAnonymousClassesUnitTest.php +++ b/PHPCompatibility/Tests/Classes/NewAnonymousClassesUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewAnonymousClassesUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Classes/NewClassesUnitTest.inc b/PHPCompatibility/Tests/Classes/NewClassesUnitTest.inc index 4dcd7a86..ea7d152c 100644 --- a/PHPCompatibility/Tests/Classes/NewClassesUnitTest.inc +++ b/PHPCompatibility/Tests/Classes/NewClassesUnitTest.inc @@ -341,3 +341,9 @@ $test = new SQLiteException(); class MyClosedGeneratorException extends ClosedGeneratorException {} function SodiumExceptionTypeHint( SodiumException $a ) {} $test = new com_exception(); +$test = new ReflectionReference; +function DateTimeReturnTypeHint( $a ) : WeakReference {} +FFI::load(__DIR__ . "/dummy.h"); +function FFITypeHints( FFI\CData $a, FFI\CType $b ); +try { +} catch ( FFI\Exception | FFI\ParserException $e ) {} diff --git a/PHPCompatibility/Tests/Classes/NewClassesUnitTest.php b/PHPCompatibility/Tests/Classes/NewClassesUnitTest.php index c58fa001..61c1b99c 100644 --- a/PHPCompatibility/Tests/Classes/NewClassesUnitTest.php +++ b/PHPCompatibility/Tests/Classes/NewClassesUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class NewClassesUnitTest extends BaseSniffTest { @@ -151,6 +152,11 @@ public function dataNewClass() array('ReflectionType', '5.6', array(308), '7.0'), array('ReflectionGenerator', '5.6', array(309), '7.0'), array('ReflectionClassConstant', '7.0', array(306), '7.1'), + array('FFI', '7.3', array(346), '7.4'), + array('FFI\CData', '7.3', array(347), '7.4'), + array('FFI\CType', '7.3', array(347), '7.4'), + array('ReflectionReference', '7.3', array(344), '7.4'), + array('WeakReference', '7.3', array(345), '7.4'), array('DATETIME', '5.1', array(146), '5.2'), array('datetime', '5.1', array(147, 320), '5.2'), @@ -194,6 +200,8 @@ public function dataNewClass() array('SodiumException', '7.1', array(342), '7.2'), array('CompileError', '7.2', array(249), '7.3'), array('JsonException', '7.2', array(250, 339), '7.3'), + array('FFI\Exception', '7.3', array(349), '7.4'), + array('FFI\ParserException', '7.3', array(349), '7.4'), ); } diff --git a/PHPCompatibility/Tests/Classes/NewConstVisibilityUnitTest.php b/PHPCompatibility/Tests/Classes/NewConstVisibilityUnitTest.php index a1942489..0c8c11bf 100644 --- a/PHPCompatibility/Tests/Classes/NewConstVisibilityUnitTest.php +++ b/PHPCompatibility/Tests/Classes/NewConstVisibilityUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.7 */ class NewConstVisibilityUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Classes/NewLateStaticBindingUnitTest.php b/PHPCompatibility/Tests/Classes/NewLateStaticBindingUnitTest.php index 4f3f900b..de0265d2 100644 --- a/PHPCompatibility/Tests/Classes/NewLateStaticBindingUnitTest.php +++ b/PHPCompatibility/Tests/Classes/NewLateStaticBindingUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class NewLateStaticBindingUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Classes/NewTypedPropertiesUnitTest.inc b/PHPCompatibility/Tests/Classes/NewTypedPropertiesUnitTest.inc new file mode 100644 index 00000000..fc4aa868 --- /dev/null +++ b/PHPCompatibility/Tests/Classes/NewTypedPropertiesUnitTest.inc @@ -0,0 +1,66 @@ +sniffFile(__FILE__, '7.3'); + $this->assertError($file, $line, 'Typed properties are not supported in PHP 7.3 or earlier'); + } + + /** + * Data provider. + * + * @see testNewTypedProperties() + * + * @return array + */ + public function dataNewTypedProperties() + { + return array( + array(23), + array(24), + array(25), + array(28), + array(31), + array(34), + array(35), + array(38), + array(41), + array(49), + array(51), + array(55), + array(58), + array(63), + array(64), + array(65), + ); + } + + + /** + * Verify the sniff doesn't throw false positives for non-typed properties. + * + * @return void + */ + public function testNoFalsePositivesNewTypedProperties() + { + $file = $this->sniffFile(__FILE__, '7.3'); + + for ($line = 1; $line < 19; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * testInvalidPropertyType + * + * @dataProvider dataInvalidPropertyType + * + * @param array $line The line number on which the error should occur. + * @param string $type The invalid type which should be detected. + * + * @return void + */ + public function testInvalidPropertyType($line, $type) + { + $file = $this->sniffFile(__FILE__, '7.4'); + $this->assertError($file, $line, "$type is not supported as a type declaration for properties"); + } + + /** + * Data provider. + * + * @see testInvalidPropertyType() + * + * @return array + */ + public function dataInvalidPropertyType() + { + return array( + array(63, 'void'), + array(64, 'callable'), + array(65, 'callable'), + ); + } + + + /** + * Verify the sniff doesn't throw false positives. + * + * @return void + */ + public function testNoFalsePositivesInvalidPropertyType() + { + $file = $this->sniffFile(__FILE__, '7.4'); + + for ($line = 1; $line < 57; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /* + * `testNoViolationsInFileOnValidVersion` test omitted as this sniff will also throw warnings/errors + * about invalid typed properties. + */ +} diff --git a/PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.inc b/PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.inc new file mode 100644 index 00000000..e50c7dd8 --- /dev/null +++ b/PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.inc @@ -0,0 +1,68 @@ +foo = parent::$foo; + return parent::test(); + } +} + +class PlainClass { + public function create_anon_class() { + $anon = new class() extends ParentClass { + public function test() { + echo parent::class; + $this->foo = parent::$foo; + return parent::test(); + } + }; + } +} + +// PHP 7.4: Deprecated parent in class without parent. +class ParentClass +{ + public function test() { + echo parent::class; + $this->foo = parent::$foo; + return parent::test(); + } +} + +class ImplementedClass implements SomeInterface +{ + public function test() { + echo parent::class; + $this->foo = parent::$foo; + return parent::test(); + } +} + +// Test correct handling of nested classes. +class NestingStuff extends Nested { + public function create_anon_class() { + return new class() { + public function test() { + echo parent::class; + $this->foo = parent::$foo; + return parent::test(); + } + }; + } +} + +// Intentional parse error. This has to be the last test in the file. +class SomeClass extends Something + public function test() { + return parent::test(); + } diff --git a/PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.php b/PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.php new file mode 100644 index 00000000..77a9cd87 --- /dev/null +++ b/PHPCompatibility/Tests/Classes/RemovedOrphanedParentUnitTest.php @@ -0,0 +1,113 @@ +sniffFile(__FILE__, '7.4'); + $this->assertError($file, $line, 'Using "parent" inside a class without parent is deprecated since PHP 7.4'); + } + + /** + * Data provider. + * + * @see testRemovedOrphanedParent() + * + * @return array + */ + public function dataRemovedOrphanedParent() + { + return array( + array(36), + array(37), + array(38), + array(45), + array(46), + array(47), + array(56), + array(57), + array(58), + ); + } + + + /** + * testNoFalsePositives. + * + * @dataProvider dataNoFalsePositives + * + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositives($line) + { + $file = $this->sniffFile(__FILE__, '7.4'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + $cases = array(); + // No errors expected on the first 31 lines. + for ($line = 1; $line <= 31; $line++) { + $cases[] = array($line); + } + + // Add parse error test case. + $cases[] = array(67); + + return $cases; + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.inc b/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.inc index 063f91a4..46a0ad91 100644 --- a/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.inc +++ b/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.inc @@ -821,3 +821,61 @@ echo LDAP_CONTROL_VLVREQUEST; echo LDAP_CONTROL_VLVRESPONSE; echo T_COALESCE; echo T_YIELD_FROM; +echo MB_ONIGURUMA_VERSION; +echo SO_LABEL; +echo SO_PEERLABEL; +echo SO_LISTENQLIMIT; +echo SO_LISTENQLEN; +echo SO_USER_COOKIE; +echo PHP_WINDOWS_EVENT_CTRL_C; +echo PHP_WINDOWS_EVENT_CTRL_BREAK; +echo TIDY_TAG_ARTICLE; +echo TIDY_TAG_ASIDE; +echo TIDY_TAG_AUDIO; +echo TIDY_TAG_BDI; +echo TIDY_TAG_CANVAS; +echo TIDY_TAG_COMMAND; +echo TIDY_TAG_DATALIST; +echo TIDY_TAG_DETAILS; +echo TIDY_TAG_DIALOG; +echo TIDY_TAG_FIGCAPTION; +echo TIDY_TAG_FIGURE; +echo TIDY_TAG_FOOTER; +echo TIDY_TAG_HEADER; +echo TIDY_TAG_HGROUP; +echo TIDY_TAG_MAIN; +echo TIDY_TAG_MARK; +echo TIDY_TAG_MENUITEM; +echo TIDY_TAG_METER; +echo TIDY_TAG_NAV; +echo TIDY_TAG_OUTPUT; +echo TIDY_TAG_PROGRESS; +echo TIDY_TAG_SECTION; +echo TIDY_TAG_SOURCE; +echo TIDY_TAG_SUMMARY; +echo TIDY_TAG_TEMPLATE; +echo TIDY_TAG_TIME; +echo TIDY_TAG_TRACK; +echo TIDY_TAG_VIDEO; +echo CURL_VERSION_ALTSVC; +echo CURL_VERSION_CURLDEBUG; +echo T_BAD_CHARACTER; +echo IMG_FILTER_SCATTER; +echo PASSWORD_ARGON2_PROVIDER; +echo SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13; +echo SODIUM_BASE64_VARIANT_ORIGINAL; +echo SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING; +echo SODIUM_BASE64_VARIANT_URLSAFE; +echo SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING; +echo SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES; +echo SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES; +echo SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES; +echo SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY; +echo SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL; diff --git a/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.php b/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.php index 9c797687..662ad99e 100644 --- a/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.php +++ b/PHPCompatibility/Tests/Constants/NewConstantsUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.1.0 */ class NewConstantsUnitTest extends BaseSniffTest { @@ -690,6 +691,10 @@ public function dataNewConstant() array('SODIUM_LIBRARY_VERSION', '7.1', array(627), '7.2'), array('SODIUM_LIBRARY_MAJOR_VERSION', '7.1', array(628), '7.2'), array('SODIUM_LIBRARY_MINOR_VERSION', '7.1', array(629), '7.2'), + array('SODIUM_BASE64_VARIANT_ORIGINAL', '7.1', array(866), '7.2'), + array('SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING', '7.1', array(867), '7.2'), + array('SODIUM_BASE64_VARIANT_URLSAFE', '7.1', array(868), '7.2'), + array('SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING', '7.1', array(869), '7.2'), array('SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES', '7.1', array(630), '7.2'), array('SODIUM_CRYPTO_AEAD_AES256GCM_NSECBYTES', '7.1', array(631), '7.2'), array('SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES', '7.1', array(632), '7.2'), @@ -702,6 +707,10 @@ public function dataNewConstant() array('SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES', '7.1', array(639), '7.2'), array('SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES', '7.1', array(640), '7.2'), array('SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES', '7.1', array(641), '7.2'), + array('SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES', '7.1', array(870), '7.2'), + array('SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES', '7.1', array(871), '7.2'), + array('SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES', '7.1', array(872), '7.2'), + array('SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES', '7.1', array(873), '7.2'), array('SODIUM_CRYPTO_AUTH_BYTES', '7.1', array(642), '7.2'), array('SODIUM_CRYPTO_AUTH_KEYBYTES', '7.1', array(643), '7.2'), array('SODIUM_CRYPTO_BOX_SEALBYTES', '7.1', array(644), '7.2'), @@ -727,6 +736,7 @@ public function dataNewConstant() array('SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MIN', '7.1', array(664), '7.2'), array('SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MAX', '7.1', array(665), '7.2'), array('SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13', '7.1', array(666), '7.2'), + array('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13', '7.1', array(865), '7.2'), array('SODIUM_CRYPTO_PWHASH_ALG_DEFAULT', '7.1', array(667), '7.2'), array('SODIUM_CRYPTO_PWHASH_SALTBYTES', '7.1', array(668), '7.2'), array('SODIUM_CRYPTO_PWHASH_STRPREFIX', '7.1', array(669), '7.2'), @@ -744,6 +754,14 @@ public function dataNewConstant() array('SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE', '7.1', array(681), '7.2'), array('SODIUM_CRYPTO_SCALARMULT_BYTES', '7.1', array(682), '7.2'), array('SODIUM_CRYPTO_SCALARMULT_SCALARBYTES', '7.1', array(683), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES', '7.1', array(874), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES', '7.1', array(875), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES', '7.1', array(876), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX', '7.1', array(877), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE', '7.1', array(878), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH', '7.1', array(879), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY', '7.1', array(880), '7.2'), + array('SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL', '7.1', array(881), '7.2'), array('SODIUM_CRYPTO_SHORTHASH_BYTES', '7.1', array(684), '7.2'), array('SODIUM_CRYPTO_SHORTHASH_KEYBYTES', '7.1', array(685), '7.2'), array('SODIUM_CRYPTO_SECRETBOX_KEYBYTES', '7.1', array(686), '7.2'), @@ -880,6 +898,49 @@ public function dataNewConstant() array('LDAP_CONTROL_X_EXTENDED_DN', '7.2', array(819), '7.3'), array('LDAP_CONTROL_VLVREQUEST', '7.2', array(820), '7.3'), array('LDAP_CONTROL_VLVRESPONSE', '7.2', array(821), '7.3'), + + array('CURL_VERSION_ALTSVC', '7.3.5', array(860), '7.4', '7.3'), + array('CURL_VERSION_CURLDEBUG', '7.3.5', array(861), '7.4', '7.3'), + + array('IMG_FILTER_SCATTER', '7.3', array(863), '7.4'), + array('MB_ONIGURUMA_VERSION', '7.3', array(824), '7.4'), + array('SO_LABEL', '7.3', array(825), '7.4'), + array('SO_PEERLABEL', '7.3', array(826), '7.4'), + array('SO_LISTENQLIMIT', '7.3', array(827), '7.4'), + array('SO_LISTENQLEN', '7.3', array(828), '7.4'), + array('SO_USER_COOKIE', '7.3', array(829), '7.4'), + array('PASSWORD_ARGON2_PROVIDER', '7.3', array(864), '7.4'), + array('PHP_WINDOWS_EVENT_CTRL_C', '7.3', array(830), '7.4'), + array('PHP_WINDOWS_EVENT_CTRL_BREAK', '7.3', array(831), '7.4'), + array('T_BAD_CHARACTER', '7.3', array(862), '7.4'), + array('TIDY_TAG_ARTICLE', '7.3', array(832), '7.4'), + array('TIDY_TAG_ASIDE', '7.3', array(833), '7.4'), + array('TIDY_TAG_AUDIO', '7.3', array(834), '7.4'), + array('TIDY_TAG_BDI', '7.3', array(835), '7.4'), + array('TIDY_TAG_CANVAS', '7.3', array(836), '7.4'), + array('TIDY_TAG_COMMAND', '7.3', array(837), '7.4'), + array('TIDY_TAG_DATALIST', '7.3', array(838), '7.4'), + array('TIDY_TAG_DETAILS', '7.3', array(839), '7.4'), + array('TIDY_TAG_DIALOG', '7.3', array(840), '7.4'), + array('TIDY_TAG_FIGCAPTION', '7.3', array(841), '7.4'), + array('TIDY_TAG_FIGURE', '7.3', array(842), '7.4'), + array('TIDY_TAG_FOOTER', '7.3', array(843), '7.4'), + array('TIDY_TAG_HEADER', '7.3', array(844), '7.4'), + array('TIDY_TAG_HGROUP', '7.3', array(845), '7.4'), + array('TIDY_TAG_MAIN', '7.3', array(846), '7.4'), + array('TIDY_TAG_MARK', '7.3', array(847), '7.4'), + array('TIDY_TAG_MENUITEM', '7.3', array(848), '7.4'), + array('TIDY_TAG_METER', '7.3', array(849), '7.4'), + array('TIDY_TAG_NAV', '7.3', array(850), '7.4'), + array('TIDY_TAG_OUTPUT', '7.3', array(851), '7.4'), + array('TIDY_TAG_PROGRESS', '7.3', array(852), '7.4'), + array('TIDY_TAG_SECTION', '7.3', array(853), '7.4'), + array('TIDY_TAG_SOURCE', '7.3', array(854), '7.4'), + array('TIDY_TAG_SUMMARY', '7.3', array(855), '7.4'), + array('TIDY_TAG_TEMPLATE', '7.3', array(856), '7.4'), + array('TIDY_TAG_TIME', '7.3', array(857), '7.4'), + array('TIDY_TAG_TRACK', '7.3', array(858), '7.4'), + array('TIDY_TAG_VIDEO', '7.3', array(859), '7.4'), ); } diff --git a/PHPCompatibility/Tests/Constants/NewMagicClassConstantUnitTest.php b/PHPCompatibility/Tests/Constants/NewMagicClassConstantUnitTest.php index e0ddd36e..e7ed722e 100644 --- a/PHPCompatibility/Tests/Constants/NewMagicClassConstantUnitTest.php +++ b/PHPCompatibility/Tests/Constants/NewMagicClassConstantUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class NewMagicClassConstantUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Constants/RemovedConstantsUnitTest.inc b/PHPCompatibility/Tests/Constants/RemovedConstantsUnitTest.inc index 34b49d1e..8c011111 100644 --- a/PHPCompatibility/Tests/Constants/RemovedConstantsUnitTest.inc +++ b/PHPCompatibility/Tests/Constants/RemovedConstantsUnitTest.inc @@ -1,8 +1,8 @@ + * @since 8.1.0 */ class RemovedConstantsUnitTest extends BaseSniffTest { @@ -66,6 +67,52 @@ public function dataDeprecatedConstant() array('INTL_IDNA_VARIANT_2003', '7.2', array(16), '7.1'), array('FILTER_FLAG_SCHEME_REQUIRED', '7.3', array(73), '7.2'), array('FILTER_FLAG_HOST_REQUIRED', '7.3', array(74), '7.2'), + array('CURLPIPE_HTTP1', '7.4', array(138), '7.3'), + ); + } + + + /** + * testDeprecatedConstantWithAlternative + * + * @dataProvider dataDeprecatedConstantWithAlternative + * + * @param string $constantName Name of the PHP constant. + * @param string $deprecatedIn The PHP version in which the constant was deprecated. + * @param string $alternative An alternative constant. + * @param array $lines The line numbers in the test file which apply to this constant. + * @param string $okVersion A PHP version in which the constant was still valid. + * @param string $deprecatedVersion Optional PHP version to test deprecation message with - + * if different from the $deprecatedIn version. + * + * @return void + */ + public function testDeprecatedConstantWithAlternative($constantName, $deprecatedIn, $alternative, $lines, $okVersion, $deprecatedVersion = null) + { + $file = $this->sniffFile(__FILE__, $okVersion); + foreach ($lines as $line) { + $this->assertNoViolation($file, $line); + } + + $errorVersion = (isset($deprecatedVersion)) ? $deprecatedVersion : $deprecatedIn; + $file = $this->sniffFile(__FILE__, $errorVersion); + $error = "The constant \"{$constantName}\" is deprecated since PHP {$deprecatedIn}; Use {$alternative} instead"; + foreach ($lines as $line) { + $this->assertWarning($file, $line, $error); + } + } + + /** + * Data provider. + * + * @see testDeprecatedConstantWithAlternative() + * + * @return array + */ + public function dataDeprecatedConstantWithAlternative() + { + return array( + array('FILTER_SANITIZE_MAGIC_QUOTES', '7.4', 'FILTER_SANITIZE_ADD_SLASHES', array(137), '7.3'), ); } @@ -117,10 +164,75 @@ public function dataRemovedConstant() array('CURLCLOSEPOLICY_CALLBACK', '5.6', array(13), '5.5'), array('CURLCLOSEPOLICY_OLDEST', '5.6', array(14), '5.5'), array('PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT', '7.0', array(15), '5.6'), + array('T_CHARACTER', '7.0', array(139), '5.6'), + array('T_BAD_CHARACTER', '7.0', array(140), '5.6'), array('PHPDBG_FILE', '7.3', array(69), '7.2'), array('PHPDBG_METHOD', '7.3', array(70), '7.2'), array('PHPDBG_LINENO', '7.3', array(71), '7.2'), array('PHPDBG_FUNC', '7.3', array(72), '7.2'), + + array('IBASE_DEFAULT', '7.4', array(75), '7.3'), + array('IBASE_READ', '7.4', array(76), '7.3'), + array('IBASE_WRITE', '7.4', array(77), '7.3'), + array('IBASE_CONSISTENCY', '7.4', array(78), '7.3'), + array('IBASE_CONCURRENCY', '7.4', array(79), '7.3'), + array('IBASE_COMMITTED', '7.4', array(80), '7.3'), + array('IBASE_WAIT', '7.4', array(81), '7.3'), + array('IBASE_NOWAIT', '7.4', array(82), '7.3'), + array('IBASE_FETCH_BLOBS', '7.4', array(83), '7.3'), + array('IBASE_FETCH_ARRAYS', '7.4', array(84), '7.3'), + array('IBASE_UNIXTIME', '7.4', array(85), '7.3'), + array('IBASE_BKP_IGNORE_CHECKSUMS', '7.4', array(86), '7.3'), + array('IBASE_BKP_IGNORE_LIMBO', '7.4', array(87), '7.3'), + array('IBASE_BKP_METADATA_ONLY', '7.4', array(88), '7.3'), + array('IBASE_BKP_NO_GARBAGE_COLLECT', '7.4', array(89), '7.3'), + array('IBASE_BKP_OLD_DESCRIPTIONS', '7.4', array(90), '7.3'), + array('IBASE_BKP_NON_TRANSPORTABLE', '7.4', array(91), '7.3'), + array('IBASE_BKP_CONVERT', '7.4', array(92), '7.3'), + array('IBASE_RES_DEACTIVATE_IDX', '7.4', array(93), '7.3'), + array('IBASE_RES_NO_SHADOW', '7.4', array(94), '7.3'), + array('IBASE_RES_NO_VALIDITY', '7.4', array(95), '7.3'), + array('IBASE_RES_ONE_AT_A_TIME', '7.4', array(96), '7.3'), + array('IBASE_RES_REPLACE', '7.4', array(97), '7.3'), + array('IBASE_RES_CREATE', '7.4', array(98), '7.3'), + array('IBASE_RES_USE_ALL_SPACE', '7.4', array(99), '7.3'), + array('IBASE_PRP_PAGE_BUFFERS', '7.4', array(100), '7.3'), + array('IBASE_PRP_SWEEP_INTERVAL', '7.4', array(101), '7.3'), + array('IBASE_PRP_SHUTDOWN_DB', '7.4', array(102), '7.3'), + array('IBASE_PRP_DENY_NEW_TRANSACTIONS', '7.4', array(103), '7.3'), + array('IBASE_PRP_DENY_NEW_ATTACHMENTS', '7.4', array(104), '7.3'), + array('IBASE_PRP_RESERVE_SPACE', '7.4', array(105), '7.3'), + array('IBASE_PRP_RES_USE_FULL', '7.4', array(106), '7.3'), + array('IBASE_PRP_RES', '7.4', array(107), '7.3'), + array('IBASE_PRP_WRITE_MODE', '7.4', array(108), '7.3'), + array('IBASE_PRP_WM_ASYNC', '7.4', array(109), '7.3'), + array('IBASE_PRP_WM_SYNC', '7.4', array(110), '7.3'), + array('IBASE_PRP_ACCESS_MODE', '7.4', array(111), '7.3'), + array('IBASE_PRP_AM_READONLY', '7.4', array(112), '7.3'), + array('IBASE_PRP_AM_READWRITE', '7.4', array(113), '7.3'), + array('IBASE_PRP_SET_SQL_DIALECT', '7.4', array(114), '7.3'), + array('IBASE_PRP_ACTIVATE', '7.4', array(115), '7.3'), + array('IBASE_PRP_DB_ONLINE', '7.4', array(116), '7.3'), + array('IBASE_RPR_CHECK_DB', '7.4', array(117), '7.3'), + array('IBASE_RPR_IGNORE_CHECKSUM', '7.4', array(118), '7.3'), + array('IBASE_RPR_KILL_SHADOWS', '7.4', array(119), '7.3'), + array('IBASE_RPR_MEND_DB', '7.4', array(120), '7.3'), + array('IBASE_RPR_VALIDATE_DB', '7.4', array(121), '7.3'), + array('IBASE_RPR_FULL', '7.4', array(122), '7.3'), + array('IBASE_RPR_SWEEP_DB', '7.4', array(123), '7.3'), + array('IBASE_STS_DATA_PAGES', '7.4', array(124), '7.3'), + array('IBASE_STS_DB_LOG', '7.4', array(125), '7.3'), + array('IBASE_STS_HDR_PAGES', '7.4', array(126), '7.3'), + array('IBASE_STS_IDX_PAGES', '7.4', array(127), '7.3'), + array('IBASE_STS_SYS_RELATIONS', '7.4', array(128), '7.3'), + array('IBASE_SVC_SERVER_VERSION', '7.4', array(129), '7.3'), + array('IBASE_SVC_IMPLEMENTATION', '7.4', array(130), '7.3'), + array('IBASE_SVC_GET_ENV', '7.4', array(131), '7.3'), + array('IBASE_SVC_GET_ENV_LOCK', '7.4', array(132), '7.3'), + array('IBASE_SVC_GET_ENV_MSG', '7.4', array(133), '7.3'), + array('IBASE_SVC_USER_DBPATH', '7.4', array(134), '7.3'), + array('IBASE_SVC_SVR_DB_INFO', '7.4', array(135), '7.3'), + array('IBASE_SVC_GET_USERS', '7.4', array(136), '7.3'), ); } @@ -231,9 +343,9 @@ public function dataDeprecatedRemovedConstant() /** - * Test functions that shouldn't be flagged by this sniff. + * Test constants that shouldn't be flagged by this sniff. * - * These are either userland methods or namespaced functions. + * These are either userland constants or namespaced constants. * * @dataProvider dataNoFalsePositives * @@ -257,6 +369,7 @@ public function testNoFalsePositives($line) public function dataNoFalsePositives() { return array( + array(3), array(4), array(5), ); diff --git a/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.inc b/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.inc index ac3f73b1..48c325b3 100644 --- a/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.inc +++ b/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.inc @@ -27,7 +27,7 @@ while ($foo) { case 2: continue (1); // Invalid. case 3: - continue 0; // Invalid. The fact that 0 is not the concern of this sniff. + continue 0; // Invalid. The fact that 0 is forbidden, is not the concern of this sniff. case 4: continue $var; // Undetermined. Outside the scope of this sniff. case 5: diff --git a/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.php b/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.php index ea60f254..b40a005a 100644 --- a/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/DiscouragedSwitchContinueUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class DiscouragedSwitchContinueUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueOutsideLoopUnitTest.php b/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueOutsideLoopUnitTest.php index 584dfd9a..eaa2774c 100644 --- a/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueOutsideLoopUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueOutsideLoopUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.7 */ class ForbiddenBreakContinueOutsideLoopUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueVariableArgumentsUnitTest.php b/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueVariableArgumentsUnitTest.php index 733dbd33..eb396ac9 100644 --- a/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueVariableArgumentsUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/ForbiddenBreakContinueVariableArgumentsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class ForbiddenBreakContinueVariableArgumentsUnitTest extends BaseSniffTest { + /** + * Error message snippet for the variable argument error. + * + * @var string + */ const ERROR_TYPE_VARIABLE = 'a variable argument'; + + /** + * Error message snippet for the zero argument error. + * + * @var string + */ const ERROR_TYPE_ZERO = '0 as an argument'; /** diff --git a/PHPCompatibility/Tests/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksUnitTest.php b/PHPCompatibility/Tests/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksUnitTest.php index 00ab9314..cf4eecc4 100644 --- a/PHPCompatibility/Tests/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/ForbiddenSwitchWithMultipleDefaultBlocksUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class ForbiddenSwitchWithMultipleDefaultBlocksUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ControlStructures/NewExecutionDirectivesUnitTest.php b/PHPCompatibility/Tests/ControlStructures/NewExecutionDirectivesUnitTest.php index a1faf59c..57849d9b 100644 --- a/PHPCompatibility/Tests/ControlStructures/NewExecutionDirectivesUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/NewExecutionDirectivesUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class NewExecutionDirectivesUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ControlStructures/NewForeachExpressionReferencingUnitTest.php b/PHPCompatibility/Tests/ControlStructures/NewForeachExpressionReferencingUnitTest.php index 680488d2..e5f42ab6 100644 --- a/PHPCompatibility/Tests/ControlStructures/NewForeachExpressionReferencingUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/NewForeachExpressionReferencingUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewForeachExpressionReferencingUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ControlStructures/NewListInForeachUnitTest.php b/PHPCompatibility/Tests/ControlStructures/NewListInForeachUnitTest.php index 12a1cf6f..6ee7284a 100644 --- a/PHPCompatibility/Tests/ControlStructures/NewListInForeachUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/NewListInForeachUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewListInForeachUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ControlStructures/NewMultiCatchUnitTest.php b/PHPCompatibility/Tests/ControlStructures/NewMultiCatchUnitTest.php index 99602396..2cfccd55 100644 --- a/PHPCompatibility/Tests/ControlStructures/NewMultiCatchUnitTest.php +++ b/PHPCompatibility/Tests/ControlStructures/NewMultiCatchUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.7 */ class NewMultiCatchUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.inc b/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.inc index 86bdf3dc..78439f12 100644 --- a/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.inc +++ b/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.inc @@ -43,7 +43,7 @@ oracle(); ovrimos(); -pfpro(); +pfpro_init(); sqlite(); @@ -75,5 +75,9 @@ ereg_convert(); ereg_translate_to_preg(); ereg_replace(); // Non-whitelisted. +ibase_trans(); +wddx_deserialize(); +recode(); + // Don't throw errors during live code review. ereg($a, $b diff --git a/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.php b/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.php index 61295b08..be2501fd 100644 --- a/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.php +++ b/PHPCompatibility/Tests/Extensions/RemovedExtensionsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class RemovedExtensionsUnitTest extends BaseSniffTest { @@ -74,7 +75,7 @@ public function dataRemovedExtension() array('msql', '5.3', array(36), '5.2'), array('mssql', '7.0', array(63), '5.6'), array('ovrimos', '5.1', array(44), '5.0'), - array('pfpro', '5.3', array(46), '5.2'), + array('pfpro_', '5.1', array(46), '5.0'), array('sqlite', '5.4', array(48), '5.3'), // array('sybase', '7.0', array(xx), '5.6'), sybase_ct ??? array('yp', '5.1', array(54), '5.0'), @@ -126,13 +127,16 @@ public function dataRemovedExtensionWithAlternative() array('dbx', '5.1', 'pecl/dbx', array(12), '5.0'), array('dio', '5.1', 'pecl/dio', array(14), '5.0'), array('fdf', '5.3', 'pecl/fdf', array(20), '5.2'), + array('ibase', '7.4', 'pecl/ibase', array(78), '7.3'), array('ingres', '5.1', 'pecl/ingres', array(26), '5.0'), - array('mcve', '5.1', 'pecl/mvce', array(30), '5.0'), + array('mcve', '5.1', 'pecl/mcve', array(30), '5.0'), array('ming', '5.3', 'pecl/ming', array(32), '5.2'), array('ncurses', '5.3', 'pecl/ncurses', array(40), '5.2'), array('oracle', '5.1', 'oci8 or pdo_oci', array(42), '5.0'), + array('recode', '7.4', 'iconv or mbstring', array(80), '7.3'), array('sybase', '5.3', 'sybase_ct', array(50), '5.2'), array('w32api', '5.1', 'pecl/ffi', array(52), '5.0'), + array('wddx', '7.4', 'pecl/wddx', array(79), '7.3'), ); } @@ -223,7 +227,7 @@ public function dataNoFalsePositives() array(58), // Function declaration. array(59), // Class instantiation. array(60), // Method call. - array(78), // Live coding. + array(82), // Live coding. ); // Inline setting changes in combination with namespaced sniffs is only supported since PHPCS 2.6.0. diff --git a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsUnitTest.php index c7fe161e..ae0dd0b3 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParameterShadowSuperGlobalsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class ForbiddenParameterShadowSuperGlobalsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.inc b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.inc index 2d6020b4..f30ab32a 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.inc @@ -4,7 +4,9 @@ function foo($a, $b, $unused, $unused) { } function foobaz() {} // No parameters = no error. -function ($a, $b, $unused, $unused) {} // Anonymous function with params of same name. +function ($a, $b, $unused, $unused) {}; // Anonymous function with params of same name. + +function varsAreCaseSensitive( $a, $A ) {} // Don't throw errors during live code review. function foobar($a,$a diff --git a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.php index 1c9efb68..fab854c3 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenParametersWithSameNameUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class ForbiddenParametersWithSameNameUnitTest extends BaseSniffTest { @@ -81,6 +82,7 @@ public function dataNoFalsePositives() { return array( array(5), + array(9), array(10), ); } diff --git a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.inc b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.inc new file mode 100644 index 00000000..12e2e980 --- /dev/null +++ b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.inc @@ -0,0 +1,40 @@ +foo; + } +} + +// Irrelevant, not the magic method. +function __toString($param) { + return $foo; +} + +// PHP 5.3: The __toString() magic method can no longer accept arguments. +interface MyInterface { + public function __toString($param); +} + +abstract class AbstractClass { + abstract public function __toString($param); +} + +class MyClass { + public function __toString($param) { + return $this->foo; + } +} + +trait MyTrait { + public function __toString($param) { + return $this->foo; + } +} + +$anon = new class() { + public function __toString($param) { + return $this->foo; + } +}; diff --git a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.php new file mode 100644 index 00000000..8922f05e --- /dev/null +++ b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenToStringParametersUnitTest.php @@ -0,0 +1,137 @@ +markTestSkipped('Traits are not recognized on PHPCS < 2.4.0 in combination with PHP < 5.4'); + return; + } + + $file = $this->sniffFile(__FILE__, '5.3'); + $this->assertError($file, $line, 'The __toString() magic method can no longer accept arguments since PHP 5.3'); + } + + /** + * Data provider. + * + * @see testForbiddenToStringParameters() + * + * @return array + */ + public function dataForbiddenToStringParameters() + { + return array( + array(17), + array(21), + array(25), + array(31, true), + array(37), + ); + } + + + /** + * testNoFalsePositives. + * + * @dataProvider dataNoFalsePositives + * + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositives($line) + { + $file = $this->sniffFile(__FILE__, '5.3'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + $cases = array(); + // No errors expected on the first 15 lines. + for ($line = 1; $line <= 15; $line++) { + $cases[] = array($line); + } + + return $cases; + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '5.2'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenVariableNamesInClosureUseUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenVariableNamesInClosureUseUnitTest.php index 4d73d40e..1aa2dba7 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenVariableNamesInClosureUseUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/ForbiddenVariableNamesInClosureUseUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class ForbiddenVariableNamesInClosureUseUnitTest extends BaseSniffTest { + + /** + * The name of the main test case file. + * + * @var string + */ const TEST_FILE = 'ForbiddenVariableNamesInClosureUseUnitTest.1.inc'; + /** + * The name of a secondary test case file which similates live coding. + * + * @var string + */ const TEST_FILE_LIVE_CODING = 'ForbiddenVariableNamesInClosureUseUnitTest.2.inc'; /** diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewClosureUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/NewClosureUnitTest.php index 5ab3b682..b2938dbf 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NewClosureUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewClosureUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewClosureUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.inc b/PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.inc new file mode 100644 index 00000000..f0951d64 --- /dev/null +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.inc @@ -0,0 +1,159 @@ +foo; + } + + public function someOtherFunction() { + if ( is_int($this->foo) ) { + throw new Exception('Foo is int'); + } + return $this->foo; + } +} + +// Irrelevant, not the magic method. +function __toString() { + if ( is_int($this->foo) ) { + throw new Exception('Foo is int'); + } + return $this->foo; +} + +// Irrelevant, methods in interfaces don't have a body. +interface MyInterface { + public function __toString(); +} + +// Irrelevant, abstract method doesn't have a body. +abstract class AbstractClass { + abstract public function __toString(); +} + +// PHP 7.4: throwing exceptions from __toString(). +class PHP74ValidClass { + public function __toString() { + if ( is_int($this->foo) ) { + throw new Exception('Foo is int'); + } + return $this->foo; + } +} + +trait PHP74ValidTrait { + public function __toString() { + if ( is_int($this->foo) ) { + throw new Exception('Foo is int'); + } + return $this->foo; + } +} + +$anon = new class() { + public function __toString() { + if ( is_int($this->foo) ) { + throw new Exception('Foo is int'); + } + return $this->foo; + } +}; + +// Issue #863 Exception in try/catch. +class CaughtException { + public function __toString() { + try { + throw new \LogicException('should not trigger the sniff'); + } catch (\Exception $e) { + // ... + } + } +} + +class CaughtAndUnCaughtException { + public function __toString() { + try { + throw new \LogicException('should not trigger the sniff'); + } catch (\Exception $e) { + // ... + throw new \RuntimeException('should trigger the sniff'); + } + + throw $obj->methodThrowingException('should trigger the sniff'); + } +} + +function IrrelevantTryCatchOutsideFunctionScope() { + try { + $anon = new class() { + public function __toString() { + if ( is_int($this->foo) ) { + throw new Exception('Foo is int'); + } + return $this->foo; + } + } + } catch (\Exception $e) { + // ... + } +} + +// Issue #863 Uncaught exception thrown from called function/method. Detect via docblock. +class NoDocblock { + public function __toString() { + return $obj->methodThrowingException('should not trigger the sniff'); + } +} + +class DocblockNoThrows { + /** + * Convert to string. + * + * @internal Some internal note. + * @todo Remove exception. + * @random Just testing that other tags are correctly ignored. + * @codeCoverageIgnore + */ + public function __toString() { + return $obj->methodThrowingException('should not trigger the sniff'); + } +} + +class DetectBasedOnDocblock { + /** + * Convert to string. + * + * @todo Remove exception. + * @throws SomeException + */ + public function __toString() { + return $obj->methodThrowingException('should trigger the sniff'); + } +} + +class DetectBasedOnIncorrectDocblock { + /** + * Convert to string. + * + * @throws SomeException + */ + public function __toString() { + return $obj->method_call(); + } +} + +class CaughtExceptionButDocblockStillSaysThrows { + /** + * Convert to string. + * + * @throws SomeException + */ + public function __toString() { + try { + throw new \LogicException(''); + } catch (\Exception $e) { + // ... + } + } +} diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.php new file mode 100644 index 00000000..1af5ec3e --- /dev/null +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewExceptionsFromToStringUnitTest.php @@ -0,0 +1,159 @@ +markTestSkipped('Traits are not recognized on PHPCS < 2.4.0 in combination with PHP < 5.4'); + return; + } + + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertError($file, $line, 'Throwing exceptions from __toString() was not allowed prior to PHP 7.4'); + } + + /** + * Data provider. + * + * @see testNewExceptionsFromToString() + * + * @return array + */ + public function dataNewExceptionsFromToString() + { + return array( + array(39), + array(48, true), + array(57), + array(80), + array(83), + array(92), + array(130), + array(141), + array(152), + ); + } + + + /** + * testNoFalsePositives. + * + * @dataProvider dataNoFalsePositives + * + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositives($line) + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + $cases = array(); + // No errors expected on the first 34 lines. + for ($line = 1; $line <= 34; $line++) { + $cases[] = array($line); + } + + $cases[] = array(37); + $cases[] = array(46); + $cases[] = array(55); + + // No false positive for try/catch. + for ($line = 64; $line <= 79; $line++) { + $cases[] = array($line); + } + + $cases[] = array(90); + + // No false positive for docblock check. + for ($line = 103; $line <= 122; $line++) { + $cases[] = array($line); + } + + $cases[] = array(154); + + return $cases; + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.4'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewNullableTypesUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/NewNullableTypesUnitTest.php index 8d262ae9..9e0e4d97 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NewNullableTypesUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewNullableTypesUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.7 */ class NewNullableTypesUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.inc b/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.inc index fd500f62..c5242237 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.inc @@ -27,7 +27,7 @@ class MyOtherClass extends MyClass { // Class/interface type declaration - PHP 5.0+ function foo(stdClass $a) {} - +function foo(DateInterval ...$a) {} // Combined with splat, PHP 5.6+. // Class/interface type declaration with self keyword - PHP 5.2+ class MyClass { diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.php index c3869c1e..22d8ab95 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewParamTypeDeclarationsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewParamTypeDeclarationsUnitTest extends BaseSniffTest { @@ -198,6 +199,7 @@ public function dataTypeDeclaration() array(24), array(25), array(29, true), + array(30, true), array(34), array(37), array(41), diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NewReturnTypeDeclarationsUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/NewReturnTypeDeclarationsUnitTest.php index 0faaab77..81465338 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NewReturnTypeDeclarationsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/NewReturnTypeDeclarationsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewReturnTypeDeclarationsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.1.inc b/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.1.inc index 6802c7b0..6b5d640c 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.1.inc +++ b/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.1.inc @@ -166,3 +166,49 @@ $wrongStatic = new class function __callStatic($name, $arguments) {} function __set_state($properties) {} } + +// PHP 7.4: new __serialize(), unserialize(). +$normal = new class +{ + public function __serialize() {} + public function __unserialize($data) {} +} + +class PHP74WrongVisibility +{ + protected function __serialize() {} + private function __unserialize($data) {} +} + +interface PHP74WrongStatic +{ + public static function __serialize(); + static public function __unserialize($data); +} + +// More magic methods. +class MoreNormal +{ + public function __construct() {} + public function __destruct() {} + public function __clone() {} + public function __debugInfo() {} + public function __invoke() {} +} + +class MoreWrongVisibility +{ + private function __destruct() {} + protected function __debugInfo() {} + private function __invoke() {} + protected static function __set_state() {} +} + +class MoreWrongStatic +{ + static function __construct() {} + static function __destruct() {} + static function __clone() {} + static function __debugInfo() {} + static function __invoke() {} +} diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.2.inc b/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.2.inc index 2ef42f54..6bdf1625 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.2.inc +++ b/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.2.inc @@ -11,6 +11,8 @@ trait PlainTrait function __sleep() {} function __toString() {} static function __set_state($properties) {} + function __serialize() {} + function __unserialize($data) {} } trait NormalTrait @@ -25,6 +27,8 @@ trait NormalTrait public function __sleep() {} public function __toString() {} public static function __set_state($properties) {} + public function __serialize() {} + public function __unserialize($data) {} } trait WrongVisibilityTrait @@ -37,6 +41,8 @@ trait WrongVisibilityTrait protected static function __callStatic($name, $arguments) {} private function __sleep() {} protected function __toString() {} + private function __serialize() {} + protected function __unserialize($data) {} } trait WrongStaticTrait @@ -48,5 +54,7 @@ trait WrongStaticTrait static function __call($name, $arguments) {} function __callStatic($name, $arguments) {} function __set_state($properties) {} + public static function __serialize() {} + static public function __unserialize($data) {} } diff --git a/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.php b/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.php index 76c15ed7..1ed4e7d5 100644 --- a/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionDeclarations/NonStaticMagicMethodsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class NonStaticMagicMethodsUnitTest extends BaseSniffTest { @@ -61,7 +62,7 @@ public static function setUpBeforeClass() * openers/closers when run on PHP 5.3 or lower. * In a 'normal' situation you won't often find classes, interfaces and traits all * mixed in one file anyway, so this issue for which this is a work-around, - * should not cause real world issues anyway.}} + * should not cause real world issues anyway.} * * @param bool $isTrait Whether to load the class/interface test file or the trait test file. * @param string $testVersion Value of 'testVersion' to set on PHPCS object. @@ -87,7 +88,7 @@ protected function getTestFile($isTrait, $testVersion = null) * @param string $desiredVisibility The visibility the method should have. * @param string $testVisibility The visibility the method actually has in the test. * @param int $line The line number. - * @param bool $isTrait Whether the test relates to method in a trait. + * @param bool $isTrait Whether the test relates to a method in a trait. * * @return void */ @@ -113,7 +114,7 @@ public function dataWrongMethodVisibility() { return array( /* - * nonstatic_magic_methods.php + * File: NonStaticMagicMethodsUnitTest.1.inc. */ // Class. array('__get', 'public', 'private', 32), @@ -150,19 +151,30 @@ public function dataWrongMethodVisibility() array('__sleep', 'public', 'private', 155), array('__toString', 'public', 'protected', 156), + // PHP 7.4: __(un)serialize() + array('__serialize', 'public', 'protected', 179), + array('__unserialize', 'public', 'private', 180), + + // More magic methods. + array('__destruct', 'public', 'private', 201), + array('__debugInfo', 'public', 'protected', 202), + array('__invoke', 'public', 'private', 203), + array('__set_state', 'public', 'protected', 204), + /* - * nonstatic_magic_methods_traits.php + * File: NonStaticMagicMethodsUnitTest.2.inc. */ // Trait. - array('__get', 'public', 'private', 32, true), - array('__set', 'public', 'protected', 33, true), - array('__isset', 'public', 'private', 34, true), - array('__unset', 'public', 'protected', 35, true), - array('__call', 'public', 'private', 36, true), - array('__callStatic', 'public', 'protected', 37, true), - array('__sleep', 'public', 'private', 38, true), - array('__toString', 'public', 'protected', 39, true), - + array('__get', 'public', 'private', 36, true), + array('__set', 'public', 'protected', 37, true), + array('__isset', 'public', 'private', 38, true), + array('__unset', 'public', 'protected', 39, true), + array('__call', 'public', 'private', 40, true), + array('__callStatic', 'public', 'protected', 41, true), + array('__sleep', 'public', 'private', 42, true), + array('__toString', 'public', 'protected', 43, true), + array('__serialize', 'public', 'private', 44, true), + array('__unserialize', 'public', 'protected', 45, true), ); } @@ -200,7 +212,7 @@ public function dataWrongStaticMethod() { return array( /* - * nonstatic_magic_methods.php + * File: NonStaticMagicMethodsUnitTest.1.inc. */ // Class. array('__get', 44), @@ -229,16 +241,27 @@ public function dataWrongStaticMethod() array('__unset', 164), array('__call', 165), + // PHP 7.4: __(un)serialize() + array('__serialize', 185), + array('__unserialize', 186), + + // More magic methods. + array('__construct', 209), + array('__destruct', 210), + array('__clone', 211), + array('__debugInfo', 212), + array('__invoke', 213), /* - * nonstatic_magic_methods_traits.php + * File: NonStaticMagicMethodsUnitTest.2.inc. */ // Trait. - array('__get', 44, true), - array('__set', 45, true), - array('__isset', 46, true), - array('__unset', 47, true), - array('__call', 48, true), - + array('__get', 50, true), + array('__set', 51, true), + array('__isset', 52, true), + array('__unset', 53, true), + array('__call', 54, true), + array('__serialize', 57, true), + array('__unserialize', 58, true), ); } @@ -276,7 +299,7 @@ public function dataWrongNonStaticMethod() { return array( /* - * nonstatic_magic_methods.php + * File: NonStaticMagicMethodsUnitTest.1.inc. */ // Class. array('__callStatic', 49), @@ -291,11 +314,11 @@ public function dataWrongNonStaticMethod() array('__set_state', 167), /* - * nonstatic_magic_methods_traits.php + * File: NonStaticMagicMethodsUnitTest.2.inc. */ // Trait. - array('__callStatic', 49, true), - array('__set_state', 50, true), + array('__callStatic', 55, true), + array('__set_state', 56, true), ); } @@ -333,7 +356,7 @@ public function dataNoFalsePositives() { return array( /* - * nonstatic_magic_methods.php + * File: NonStaticMagicMethodsUnitTest.1.inc. */ // Plain class. array(5), @@ -404,8 +427,19 @@ public function dataNoFalsePositives() array(143), array(144), + // PHP 7.4: __(un)serialize() + array(173), + array(174), + + // More magic methods. + array(192), + array(193), + array(194), + array(195), + array(196), + /* - * nonstatic_magic_methods_traits.php + * File: NonStaticMagicMethodsUnitTest.2.inc. */ // Plain trait. array(5, true), @@ -417,9 +451,9 @@ public function dataNoFalsePositives() array(11, true), array(12, true), array(13, true), + array(14, true), + array(15, true), // Normal trait. - array(18, true), - array(19, true), array(20, true), array(21, true), array(22, true), @@ -428,7 +462,10 @@ public function dataNoFalsePositives() array(25, true), array(26, true), array(27, true), - + array(28, true), + array(29, true), + array(30, true), + array(31, true), ); } @@ -440,11 +477,11 @@ public function dataNoFalsePositives() */ public function testNoViolationsInFileOnValidVersion() { - // File: nonstatic_magic_methods.php. + // File: NonStaticMagicMethodsUnitTest.1.inc. $file = $this->getTestFile(false, '5.2'); $this->assertNoViolation($file); - // File: nonstatic_magic_methods_traits.php. + // File: NonStaticMagicMethodsUnitTest.2.inc. $file = $this->getTestFile(true, '5.2'); $this->assertNoViolation($file); } diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.1.inc b/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.1.inc index 2748ebdf..9d992c16 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.1.inc +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.1.inc @@ -5,8 +5,6 @@ */ class MyOkClass { - public function __construct() {} - public function __destruct() {} public function __call($name, $arguments) {} public function __set($name, $value) {} public function __sleep() {} @@ -19,6 +17,8 @@ class MyOkClass */ class MyClass { + public function __construct() {} + public function __destruct() {} public function __get($name) {} public function __isset($name) {} public function __unset($name) {} @@ -67,3 +67,14 @@ $a = new class public function __invoke($x) {} public function __debugInfo() {} } + +/* + * PHP 7.4: new (un)serialize magic methods. + */ +function __serialize() {} // OK, not in OO scope. +function __unserialize($data) {} // OK, not in OO scope. + +class PHP74NewMagic { + public function __serialize() {} + public function __unserialize($data) {} +} diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.2.inc b/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.2.inc index 75310feb..9ac47345 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.2.inc +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.2.inc @@ -10,4 +10,8 @@ trait MyTrait public static function __callStatic($name, $arguments) {} public function __invoke($x) {} public function __debugInfo() {} + public function __serialize() {} + public function __unserialize($data) {} + public function __construct() {} + public function __destruct() {} } diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.php b/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.php index 1c4cdcf2..102ea278 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/NewMagicMethodsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.4 */ class NewMagicMethodsUnitTest extends BaseSniffTest { @@ -61,7 +62,7 @@ public static function setUpBeforeClass() * openers/closers when run on PHP 5.3 or lower. * In a 'normal' situation you won't often find classes, interfaces and traits all * mixed in one file anyway, so this issue for which this is a work-around, - * should not cause real world issues anyway.}} + * should not cause real world issues anyway.} * * @param bool $isTrait Whether to load the class/interface test file or the trait test file. * @param string $testVersion Value of 'testVersion' to set on PHPCS object. @@ -120,7 +121,9 @@ public function testNewMagicMethod($methodName, $lastVersionBefore, $lines, $okV public function dataNewMagicMethod() { return array( - // File: new_magic_methods.php. + // File: NewMagicMethodsUnitTest.1.inc. + array('__construct', '4.4', array(20), '5.0'), + array('__destruct', '4.4', array(21), '5.0'), array('__get', '4.4', array(22, 34, 61), '5.0'), array('__isset', '5.0', array(23, 35, 62), '5.1'), array('__unset', '5.0', array(24, 36, 63), '5.1'), @@ -128,8 +131,10 @@ public function dataNewMagicMethod() array('__callStatic', '5.2', array(27, 39, 66), '5.3'), array('__invoke', '5.2', array(28, 40, 67), '5.3'), array('__debugInfo', '5.5', array(29, 41, 68), '5.6'), + array('__serialize', '7.3', array(78), '7.4'), + array('__unserialize', '7.3', array(79), '7.4'), - // File: new_magic_methods_traits.php. + // File: NewMagicMethodsUnitTest.2.inc. array('__get', '4.4', array(5), '5.0', true), array('__isset', '5.0', array(6), '5.1', true), array('__unset', '5.0', array(7), '5.1', true), @@ -137,6 +142,10 @@ public function dataNewMagicMethod() array('__callStatic', '5.2', array(10), '5.3', true), array('__invoke', '5.2', array(11), '5.3', true), array('__debugInfo', '5.5', array(12), '5.6', true), + array('__serialize', '7.3', array(13), '7.4', true), + array('__unserialize', '7.3', array(14), '7.4', true), + array('__construct', '4.4', array(15), '5.0', true), + array('__destruct', '4.4', array(16), '5.0', true), ); } @@ -175,12 +184,12 @@ public function testChangedToStringMethod($line, $isTrait = false) public function dataChangedToStringMethod() { return array( - // File: new_magic_methods.php. + // File: NewMagicMethodsUnitTest.1.inc. array(26), array(38), array(65), - // File: new_magic_methods_traits.php. + // File: NewMagicMethodsUnitTest.2.inc. array(9, true), ); } @@ -216,8 +225,6 @@ public function dataMagicMethodsThatShouldntBeFlagged() array(10), array(11), array(12), - array(13), - array(14), ); } @@ -256,6 +263,8 @@ public function dataNoFalsePositives() array(52), array(53), array(54), + array(74), + array(75), ); } @@ -267,11 +276,11 @@ public function dataNoFalsePositives() */ public function testNoViolationsInFileOnValidVersion() { - // File: new_magic_methods.php. + // File: NewMagicMethodsUnitTest.1.inc. $file = $this->getTestFile(false, '99.0'); // High version beyond newest addition. $this->assertNoViolation($file); - // File: new_magic_methods_traits.php. + // File: NewMagicMethodsUnitTest.2.inc. $file = $this->getTestFile(true, '99.0'); // High version beyond newest addition. $this->assertNoViolation($file); } diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedMagicAutoloadUnitTest.php b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedMagicAutoloadUnitTest.php index 3a541648..a1ec679f 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedMagicAutoloadUnitTest.php +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedMagicAutoloadUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.1.0 */ class RemovedMagicAutoloadUnitTest extends BaseSniffTest { - const TEST_FILE = 'RemovedMagicAutoloadUnitTest.1.inc'; + + /** + * The name of the main test case file. + * + * @var string + */ + const TEST_FILE = 'RemovedMagicAutoloadUnitTest.1.inc'; + + /** + * The name of a secondary test case file to test against false positives + * for namespaced function declarations. + * + * @var string + */ const TEST_FILE_NAMESPACED = 'RemovedMagicAutoloadUnitTest.2.inc'; /** @@ -94,7 +108,7 @@ public function dataIsDeprecated() * * @dataProvider dataIsNotAffected * - * @param string $testFile The file to test + * @param string $testFile The file to test. * @param int $line The line number where the error should occur. * @param bool $isTrait Whether the test relates to a method in a trait. * @@ -112,9 +126,9 @@ public function testIsNotAffected($testFile, $line, $isTrait = false) } /** - * dataIsDeprecated + * dataIsNotAffected * - * @see testIsDeprecated() + * @see testIsNotAffected() * * @return array */ diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedNamespacedAssertUnitTest.php b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedNamespacedAssertUnitTest.php index 88f97da4..811525df 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedNamespacedAssertUnitTest.php +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedNamespacedAssertUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class RemovedNamespacedAssertUnitTest extends BaseSniffTest { - const TEST_FILE = 'RemovedNamespacedAssertUnitTest.1.inc'; + + /** + * The name of the primary test case file containing code in the global namespace. + * + * @var string + */ + const TEST_FILE = 'RemovedNamespacedAssertUnitTest.1.inc'; + + /** + * The name of a secondary test case file containing code in a unscoped namespace. + * + * @var string + */ const TEST_FILE_NAMESPACED = 'RemovedNamespacedAssertUnitTest.2.inc'; /** @@ -55,7 +68,7 @@ public static function setUpBeforeClass() * * @dataProvider dataIsDeprecated * - * @param string $testFile The file to test + * @param string $testFile The file to test. * @param int $line The line number where the error should occur. * * @return void @@ -86,7 +99,7 @@ public function dataIsDeprecated() * * @dataProvider dataNoFalsePositives * - * @param string $testFile The file to test + * @param string $testFile The file to test. * @param int $line The line number. * @param bool $maybeSkip Whether this test needs to be skipped on old PHP/PHPCS combis. * diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.inc b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.inc index 5ac7863e..948aecf4 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.inc @@ -62,8 +62,23 @@ class new {} // Testing empty/invalid function name condition. class InvalidSwitchFunctionName { - function switch() {} - function InvalidSwitchFunctionName() {} + function switch() {} + function InvalidSwitchFunctionName() {} +} + +// Simple test of the docblock skipping code. +class bar { + /** + * This + * docblock + * should + * be + * skipped + * over. + */ + function __construct() { + echo 'I am the real constructor'; + } } // Must be last test: testing class without scope closer. diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.php b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.php index af2e2c09..8a8ad219 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/RemovedPHP4StyleConstructorsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class RemovedPHP4StyleConstructorsUnitTest extends BaseSniffTest { @@ -53,7 +54,6 @@ public function dataIsDeprecated() array(3), array(18), array(33), - array(37), array(66), ); } @@ -87,6 +87,7 @@ public function dataNoFalsePositives() array(9), array(12), array(26), + array(37), array(41), array(42), array(47), diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.inc b/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.inc index 86cc9989..1ed07701 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.inc @@ -98,9 +98,9 @@ $a = new class { $b = function ($a) {}; class ClassContainingClosure { - public function methodContainingClosure() { - $a = function($c) {}; - } + public function methodContainingClosure() { + $a = function($c) {}; + } } class Nested { @@ -112,3 +112,25 @@ class Nested { }; } } + +/** + * Function description. + * + * @since 1.2.3 + * @deprecated 2.3.4 + * + * @return void + */ +function __deprecatedFunction() {} + +class Deprecated { + /** + * Function description. + * + * @since 1.2.3 + * @deprecated 2.3.4 + * + * @return void + */ + public static function __deprecatedMethod() {} +} diff --git a/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.php b/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.php index 236df6f3..3cba10ac 100644 --- a/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.php +++ b/PHPCompatibility/Tests/FunctionNameRestrictions/ReservedFunctionNamesUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class ReservedFunctionNamesUnitTest extends BaseSniffTest { @@ -152,6 +153,9 @@ public function dataNoFalsePositives() array(98), array(101), array(102), + + array(124), + array(135), ); } diff --git a/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsReportCurrentValueUnitTest.php b/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsReportCurrentValueUnitTest.php index 450ebc6b..686523c8 100644 --- a/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsReportCurrentValueUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsReportCurrentValueUnitTest.php @@ -3,7 +3,7 @@ * PHPCompatibility, an external standard for PHP_CodeSniffer. * * @package PHPCompatibility - * @copyright 2012-2018 PHPCompatibility Contributors + * @copyright 2012-2019 PHPCompatibility Contributors * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 * @link https://github.com/PHPCompatibility/PHPCompatibility */ @@ -13,7 +13,7 @@ use PHPCompatibility\Tests\BaseSniffTest; /** - * Argument Functions Report Current Value sniff tests. + * Test the ArgumentFunctionsReportCurrentValue sniff. * * @group argumentFunctionsReportCurrentValue * @group functionUse @@ -77,7 +77,7 @@ public function dataValueChanged() * * @dataProvider dataNeedsInspection * - * @param array $line The line number where a warning is expected. + * @param int $line The line number where a warning is expected. * @param string $functionName The name of the function to which the warning applies. * @param string $variableName The variable which was detected as having been used. * @@ -131,7 +131,7 @@ public function testNoFalsePositives($line) public function dataNoFalsePositives() { $cases = array(); - // No errors expected on the first 76 lines. + // No errors expected on the first 81 lines. for ($line = 1; $line <= 81; $line++) { $cases[] = array($line); } diff --git a/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsUsageUnitTest.php b/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsUsageUnitTest.php index 31b6d6c6..3e1f004e 100644 --- a/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsUsageUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/ArgumentFunctionsUsageUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class ArgumentFunctionsUsageUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.inc b/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.inc index e2c2d426..6b16e33e 100644 --- a/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.inc @@ -66,7 +66,7 @@ parse_url($url, PHP_URL_PATH); pg_lo_create($database, $id); pg_lo_import($database, '/tmp/lob.dat', $id); preg_replace( '`[A-Z]([a-z]+)`', '$1', $subject , -1 , $count ); -preg_replace_callback( '`[A-Z]([a-z]+)`' , $callback , $subject , -1 , $count ); +preg_replace_callback( '`[A-Z]([a-z]+)`' , $callback , $subject , -1 , $count, $flags ); round(1.55, 1, PHP_ROUND_HALF_EVEN); sem_acquire( $sem_identifier, true ); session_regenerate_id (true); @@ -115,3 +115,4 @@ ldap_parse_result( $link, $result, &$errcode, &$matcheddn, &$errmsg, &$referrals ldap_read( $link_identifier, $base_dn, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref, $serverctrls ); ldap_rename( $link_identifier, $dn, $newrdn, $newparent, $deleteoldrdn, $serverctrls ); ldap_search( $link_identifier, $base_dn, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref, $serverctrls ); +preg_replace_callback_array($patterns_and_callbacks, $subject, $limit, $count, $flags); diff --git a/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.php b/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.php index 2f91ac12..fcbdfb59 100644 --- a/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/NewFunctionParametersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewFunctionParametersUnitTest extends BaseSniffTest { @@ -164,7 +165,9 @@ public function dataInvalidParameter() array('pg_select', 'result_type', '7.0', array(101), '7.1'), array('php_uname', 'mode', '4.2', array(104), '4.3'), array('preg_replace', 'count', '5.0', array(68), '5.1'), - array('preg_replace_callback', 'count', '5.0', array(69), '5.1'), + array('preg_replace_callback', 'count', '5.0', array(69), '7.4'), // OK version > version in which last parameter was added to the function. + array('preg_replace_callback', 'flags', '7.3', array(69), '7.4'), + array('preg_replace_callback_array', 'flags', '7.3', array(118), '7.4'), array('round', 'mode', '5.2', array(70), '5.3'), array('sem_acquire', 'nowait', '5.6', array(71), '7.0'), array('session_regenerate_id', 'delete_old_session', '5.0', array(72), '5.1'), diff --git a/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.inc b/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.inc index 346ce071..e5f70d06 100644 --- a/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.inc @@ -473,3 +473,22 @@ ldap_mod_add_ext(); ldap_mod_replace_ext(); ldap_mod_del_ext(); ldap_rename_ext(); + +oci_set_call_timeout(); +oci_set_db_operation(); + +mb_str_split(); +get_mangled_object_vars(); +openssl_x509_verify(); +pcntl_unshare(); +sapi_windows_set_ctrl_handler(); +sapi_windows_generate_ctrl_event(); +password_algos(); +imagecreatefromtga(); + +opcache_compile_file(); +opcache_get_configuration(); +opcache_get_status(); +opcache_invalidate(); +opcache_reset(); +opcache_is_script_cached(); diff --git a/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.php b/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.php index 46a1e501..e6e0c734 100644 --- a/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/NewFunctionsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class NewFunctionsUnitTest extends BaseSniffTest { @@ -329,6 +330,13 @@ public function dataNewFunction() array('intlz_to_date_time_zone', '5.4', array(275), '5.5'), array('intlz_get_error_code', '5.4', array(276), '5.5'), array('intlz_get_error_message', '5.4', array(277), '5.5'), + array('opcache_compile_file', '5.4', array(489), '5.5'), + array('opcache_get_configuration', '5.4', array(490), '5.5'), + array('opcache_get_status', '5.4', array(491), '5.5'), + array('opcache_invalidate', '5.4', array(492), '5.5'), + array('opcache_reset', '5.4', array(493), '5.5'), + + array('opcache_is_script_cached', '5.5.10', array(494), '5.6', '5.5'), array('gmp_root', '5.5', array(279), '5.6'), array('gmp_rootrem', '5.5', array(280), '5.6'), @@ -526,6 +534,18 @@ public function dataNewFunction() array('ldap_mod_replace_ext', '7.2', array(473), '7.3'), array('ldap_mod_del_ext', '7.2', array(474), '7.3'), array('ldap_rename_ext', '7.2', array(475), '7.3'), + + array('oci_set_call_timeout', '7.2.13', array(477), '7.3', '7.2'), + array('oci_set_db_operation', '7.2.13', array(478), '7.3', '7.2'), + + array('mb_str_split', '7.3', array(480), '7.4'), + array('get_mangled_object_vars', '7.3', array(481), '7.4'), + array('openssl_x509_verify', '7.3', array(482), '7.4'), + array('pcntl_unshare', '7.3', array(483), '7.4'), + array('sapi_windows_set_ctrl_handler', '7.3', array(484), '7.4'), + array('sapi_windows_generate_ctrl_event', '7.3', array(485), '7.4'), + array('password_algos', '7.3', array(486), '7.4'), + array('imagecreatefromtga', '7.3', array(487), '7.4'), ); } diff --git a/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.inc b/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.inc index a19d1963..b0623533 100644 --- a/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.inc @@ -6,3 +6,6 @@ parse_str($str, $output); // These are not. parse_str($str); crypt( $str ); // Recommended. + +// Prevent false positive on new. Issue #913. +$crypt = new Crypt('password'); diff --git a/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.php b/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.php index 1733c96c..bd8e9776 100644 --- a/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/OptionalToRequiredFunctionParametersUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.1.0 */ class OptionalToRequiredFunctionParametersUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.inc b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.inc index e32555c3..8b61c523 100644 --- a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.inc @@ -13,3 +13,9 @@ ldap_next_attribute( $link_identifier, $result_entry_identifier, $ber_identifier define('CONSTANT', 'foo'); // OK. define( 'CONSTANT', 'foo', true, ); + +curl_version(); // OK. +curl_version( CURLVERSION_NOW ); // OK. +curl_version( 4 ); // OK when on Curl version 4. +curl_version( 10505678 ); +curl_version( $age ); diff --git a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.php b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.php index cbd87798..8f0a614a 100644 --- a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionParametersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class RemovedFunctionParametersUnitTest extends BaseSniffTest { @@ -157,9 +158,17 @@ public function testDeprecatedParameter($functionName, $parameterName, $deprecat */ public function dataDeprecatedParameter() { - return array( + $data = array( array('define', 'case_insensitive', '7.3', array(15), '7.2'), + array('curl_version', 'age', '7.4', array(20), '7.3'), + array('curl_version', 'age', '7.4', array(21), '7.3'), ); + + if (\CURLVERSION_NOW !== 4) { + $data[] = array('curl_version', 'age', '7.4', array(19), '7.3'); + } + + return $data; } @@ -187,11 +196,19 @@ public function testNoFalsePositives($line) */ public function dataNoFalsePositives() { - return array( + $data = array( array(4), array(5), array(14), + array(17), + array(18), ); + + if (\CURLVERSION_NOW === 4) { + $data[] = array(19); + } + + return $data; } diff --git a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.inc b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.inc index 3e543945..5b9715c0 100644 --- a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.inc @@ -166,3 +166,82 @@ mbereg_search_getpos(); mbereg_search_setpos(); fgetss(); gzgetss(); + +// PHP 7.4. +ibase_add_user(); +ibase_affected_rows(); +ibase_backup(); +ibase_blob_add(); +ibase_blob_cancel(); +ibase_blob_close(); +ibase_blob_create(); +ibase_blob_echo(); +ibase_blob_get(); +ibase_blob_import(); +ibase_blob_info(); +ibase_blob_open(); +ibase_close(); +ibase_commit_ret(); +ibase_commit(); +ibase_connect(); +ibase_db_info(); +ibase_delete_user(); +ibase_drop_db(); +ibase_errcode(); +ibase_errmsg(); +ibase_execute(); +ibase_fetch_assoc(); +ibase_fetch_object(); +ibase_fetch_row(); +ibase_field_info(); +ibase_free_event_handler(); +ibase_free_query(); +ibase_free_result(); +ibase_gen_id(); +ibase_maintain_db(); +ibase_modify_user(); +ibase_name_result(); +ibase_num_fields(); +ibase_num_params(); +ibase_param_info(); +ibase_pconnect(); +ibase_prepare(); +ibase_query(); +ibase_restore(); +ibase_rollback_ret(); +ibase_rollback(); +ibase_server_info(); +ibase_service_attach(); +ibase_service_detach(); +ibase_set_event_handler(); +ibase_trans(); +ibase_wait_event(); + +// Pfpro extension - PHP 5.1 +pfpro_cleanup(); +pfpro_init(); +pfpro_process_raw(); +pfpro_process(); +pfpro_version(); + +// PHP 7.4. +wddx_add_vars(); +wddx_deserialize(); +wddx_packet_end(); +wddx_packet_start(); +wddx_serialize_value(); +wddx_serialize_vars(); +ldap_control_paged_result_response(); +ldap_control_paged_result(); +recode_file(); +recode_string(); +recode(); +is_real(); +get_magic_quotes_gpc(); +get_magic_quotes_runtime(); +hebrevc($str)); +convert_cyr_string($str, 'k', 'i'); +money_format(); +ezmlm_hash(); +restore_include_path(); + diff --git a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.php b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.php index 4df3dcfc..6fce14df 100644 --- a/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/RemovedFunctionsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class RemovedFunctionsUnitTest extends BaseSniffTest { @@ -68,6 +69,10 @@ public function dataDeprecatedFunction() array('ldap_sort', '7.0', array(97), '5.6'), array('fgetss', '7.3', array(167), '7.2'), array('gzgetss', '7.3', array(168), '7.2'), + array('ezmlm_hash', '7.4', array(245), '7.3'), + array('get_magic_quotes_gpc', '7.4', array(240), '7.3'), + array('get_magic_quotes_runtime', '7.4', array(241), '7.3'), + array('hebrevc', '7.4', array(242), '7.3'), ); } @@ -183,6 +188,13 @@ public function dataDeprecatedFunctionWithAlternative() array('mbereg_search_getregs', '7.3', 'mb_ereg_search_getregs()', array(164), '7.2'), array('mbereg_search_getpos', '7.3', 'mb_ereg_search_getpos()', array(165), '7.2'), array('mbereg_search_setpos', '7.3', 'mb_ereg_search_setpos()', array(166), '7.2'), + + array('convert_cyr_string', '7.4', 'mb_convert_encoding(), iconv() or UConverter', array(243), '7.3'), + array('is_real', '7.4', 'is_float()', array(239), '7.3'), + array('money_format', '7.4', 'NumberFormatter::formatCurrency()', array(244), '7.3'), + array('restore_include_path', '7.4', "ini_restore('include_path')", array(246), '7.3'), + array('ldap_control_paged_result', '7.4', 'ldap_search()', array(235), '7.3'), + array('ldap_control_paged_result', '7.4', 'ldap_search()', array(235), '7.3'), ); } @@ -239,7 +251,114 @@ public function dataRemovedFunction() array('imagepstext', '7.0', array(96), '5.6'), array('php_check_syntax', '5.0.5', array(98), '5.0', '5.1'), array('mysqli_get_cache_stats', '5.4', array(99), '5.3'), + array('ibase_add_user', '7.4', array(171), '7.3'), + array('ibase_affected_rows', '7.4', array(172), '7.3'), + array('ibase_backup', '7.4', array(173), '7.3'), + array('ibase_blob_add', '7.4', array(174), '7.3'), + array('ibase_blob_cancel', '7.4', array(175), '7.3'), + array('ibase_blob_close', '7.4', array(176), '7.3'), + array('ibase_blob_create', '7.4', array(177), '7.3'), + array('ibase_blob_echo', '7.4', array(178), '7.3'), + array('ibase_blob_get', '7.4', array(179), '7.3'), + array('ibase_blob_import', '7.4', array(180), '7.3'), + array('ibase_blob_info', '7.4', array(181), '7.3'), + array('ibase_blob_open', '7.4', array(182), '7.3'), + array('ibase_close', '7.4', array(183), '7.3'), + array('ibase_commit_ret', '7.4', array(184), '7.3'), + array('ibase_commit', '7.4', array(185), '7.3'), + array('ibase_connect', '7.4', array(186), '7.3'), + array('ibase_db_info', '7.4', array(187), '7.3'), + array('ibase_delete_user', '7.4', array(188), '7.3'), + array('ibase_drop_db', '7.4', array(189), '7.3'), + array('ibase_errcode', '7.4', array(190), '7.3'), + array('ibase_errmsg', '7.4', array(191), '7.3'), + array('ibase_execute', '7.4', array(192), '7.3'), + array('ibase_fetch_assoc', '7.4', array(193), '7.3'), + array('ibase_fetch_object', '7.4', array(194), '7.3'), + array('ibase_fetch_row', '7.4', array(195), '7.3'), + array('ibase_field_info', '7.4', array(196), '7.3'), + array('ibase_free_event_handler', '7.4', array(197), '7.3'), + array('ibase_free_query', '7.4', array(198), '7.3'), + array('ibase_free_result', '7.4', array(199), '7.3'), + array('ibase_gen_id', '7.4', array(200), '7.3'), + array('ibase_maintain_db', '7.4', array(201), '7.3'), + array('ibase_modify_user', '7.4', array(202), '7.3'), + array('ibase_name_result', '7.4', array(203), '7.3'), + array('ibase_num_fields', '7.4', array(204), '7.3'), + array('ibase_num_params', '7.4', array(205), '7.3'), + array('ibase_param_info', '7.4', array(206), '7.3'), + array('ibase_pconnect', '7.4', array(207), '7.3'), + array('ibase_prepare', '7.4', array(208), '7.3'), + array('ibase_query', '7.4', array(209), '7.3'), + array('ibase_restore', '7.4', array(210), '7.3'), + array('ibase_rollback_ret', '7.4', array(211), '7.3'), + array('ibase_rollback', '7.4', array(212), '7.3'), + array('ibase_server_info', '7.4', array(213), '7.3'), + array('ibase_service_attach', '7.4', array(214), '7.3'), + array('ibase_service_detach', '7.4', array(215), '7.3'), + array('ibase_set_event_handler', '7.4', array(216), '7.3'), + array('ibase_trans', '7.4', array(217), '7.3'), + array('ibase_wait_event', '7.4', array(218), '7.3'), + + array('pfpro_cleanup', '5.1', array(221), '5.0'), + array('pfpro_init', '5.1', array(222), '5.0'), + array('pfpro_process_raw', '5.1', array(223), '5.0'), + array('pfpro_process', '5.1', array(224), '5.0'), + array('pfpro_version', '5.1', array(225), '5.0'), + + array('wddx_add_vars', '7.4', array(228), '7.3'), + array('wddx_deserialize', '7.4', array(229), '7.3'), + array('wddx_packet_end', '7.4', array(230), '7.3'), + array('wddx_packet_start', '7.4', array(231), '7.3'), + array('wddx_serialize_value', '7.4', array(232), '7.3'), + array('wddx_serialize_vars', '7.4', array(233), '7.3'), + ); + } + + /** + * testRemovedFunctionWithAlternative + * + * @dataProvider dataRemovedFunctionWithAlternative + * + * @param string $functionName Name of the function. + * @param string $removedIn The PHP version in which the function was removed. + * @param string $alternative An alternative function. + * @param array $lines The line numbers in the test file which apply to this function. + * @param string $okVersion A PHP version in which the function was still valid. + * @param string $removedVersion Optional PHP version to test removed message with - + * if different from the $removedIn version. + * + * @return void + */ + public function testRemovedFunctionWithAlternative($functionName, $removedIn, $alternative, $lines, $okVersion, $removedVersion = null) + { + $file = $this->sniffFile(__FILE__, $okVersion); + foreach ($lines as $line) { + $this->assertNoViolation($file, $line); + } + + $errorVersion = (isset($removedVersion)) ? $removedVersion : $removedIn; + $file = $this->sniffFile(__FILE__, $errorVersion); + $error = "Function {$functionName}() is removed since PHP {$removedIn}; Use {$alternative} instead"; + foreach ($lines as $line) { + $this->assertError($file, $line, $error); + } + } + + /** + * Data provider. + * + * @see testRemovedFunctionWithAlternative() + * + * @return array + */ + public function dataRemovedFunctionWithAlternative() + { + return array( + array('recode_file', '7.4', 'the iconv or mbstring extension', array(236), '7.3'), + array('recode_string', '7.4', 'the iconv or mbstring extension', array(237), '7.3'), + array('recode', '7.4', 'the iconv or mbstring extension', array(238), '7.3'), ); } diff --git a/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.inc b/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.inc index 3867aec1..a9598ad6 100644 --- a/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.inc +++ b/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.inc @@ -30,3 +30,7 @@ ftp_nb_get( $ftp_stream, $local_file, $remote_file); // PHP 7.3+ ftp_nb_put( $ftp_stream, $remote_file,$local_file); // PHP 7.3+ ftp_put( $ftp_stream, $remote_file, $local_file); // PHP 7.3+ ftp_put ( $ftp_stream, $remote_file, $local_file,$mode,$startpos); // OK. + +array_merge($arrays); // OK. +array_merge(); // PHP 7.4+ +array_merge_recursive(); // PHP 7.4+ diff --git a/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.php b/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.php index 44296c89..144a04eb 100644 --- a/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.php +++ b/PHPCompatibility/Tests/FunctionUse/RequiredToOptionalFunctionParametersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class RequiredToOptionalFunctionParametersUnitTest extends BaseSniffTest { @@ -75,6 +76,8 @@ public function dataRequiredOptionalParameter() array('ftp_nb_get', 'mode', '7.2', array(29), '7.3'), array('ftp_nb_put', 'mode', '7.2', array(30), '7.3'), array('ftp_put', 'mode', '7.2', array(31), '7.3'), + array('array_merge', 'array(s) to merge', '7.3', array(35), '7.4'), + array('array_merge_recursive', 'array(s) to merge', '7.3', array(36), '7.4'), ); } @@ -112,6 +115,7 @@ public function dataNoFalsePositives() array(20), array(23), array(32), + array(34), ); } diff --git a/PHPCompatibility/Tests/Generators/NewGeneratorReturnUnitTest.php b/PHPCompatibility/Tests/Generators/NewGeneratorReturnUnitTest.php index d82461eb..0328071e 100644 --- a/PHPCompatibility/Tests/Generators/NewGeneratorReturnUnitTest.php +++ b/PHPCompatibility/Tests/Generators/NewGeneratorReturnUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class NewGeneratorReturnUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.inc b/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.inc index aec4c32f..ba177603 100644 --- a/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.inc +++ b/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.inc @@ -334,3 +334,129 @@ $test = ini_get('session.cookie_samesite'); ini_set('imap.enable_insecure_rsh', 1); $test = ini_get('imap.enable_insecure_rsh'); + +ini_set('zend.exception_ignore_args', 1); +$test = ini_get('zend.exception_ignore_args'); + +ini_set('opcache.cache_id', 1); +$test = ini_get('opcache.cache_id'); + +ini_set('opcache.enable', 1); +$test = ini_get('opcache.enable'); + +ini_set('opcache.enable_cli', 1); +$test = ini_get('opcache.enable_cli'); + +ini_set('opcache.memory_consumption', 1); +$test = ini_get('opcache.memory_consumption'); + +ini_set('opcache.interned_strings_buffer', 1); +$test = ini_get('opcache.interned_strings_buffer'); + +ini_set('opcache.max_accelerated_files', 1); +$test = ini_get('opcache.max_accelerated_files'); + +ini_set('opcache.max_wasted_percentage', 1); +$test = ini_get('opcache.max_wasted_percentage'); + +ini_set('opcache.use_cwd', 1); +$test = ini_get('opcache.use_cwd'); + +ini_set('opcache.validate_timestamps', 1); +$test = ini_get('opcache.validate_timestamps'); + +ini_set('opcache.revalidate_freq', 1); +$test = ini_get('opcache.revalidate_freq'); + +ini_set('opcache.revalidate_path', 1); +$test = ini_get('opcache.revalidate_path'); + +ini_set('opcache.save_comments', 1); +$test = ini_get('opcache.save_comments'); + +ini_set('opcache.load_comments', 1); +$test = ini_get('opcache.load_comments'); + +ini_set('opcache.fast_shutdown', 1); +$test = ini_get('opcache.fast_shutdown'); + +ini_set('opcache.enable_file_override', 1); +$test = ini_get('opcache.enable_file_override'); + +ini_set('opcache.optimization_level', 1); +$test = ini_get('opcache.optimization_level'); + +ini_set('opcache.inherited_hack', 1); +$test = ini_get('opcache.inherited_hack'); + +ini_set('opcache.dups_fix', 1); +$test = ini_get('opcache.dups_fix'); + +ini_set('opcache.blacklist_filename', 1); +$test = ini_get('opcache.blacklist_filename'); + +ini_set('opcache.max_file_size', 1); +$test = ini_get('opcache.max_file_size'); + +ini_set('opcache.consistency_checks', 1); +$test = ini_get('opcache.consistency_checks'); + +ini_set('opcache.force_restart_timeout', 1); +$test = ini_get('opcache.force_restart_timeout'); + +ini_set('opcache.error_log', 1); +$test = ini_get('opcache.error_log'); + +ini_set('opcache.log_verbosity_level', 1); +$test = ini_get('opcache.log_verbosity_level'); + +ini_set('opcache.preferred_memory_model', 1); +$test = ini_get('opcache.preferred_memory_model'); + +ini_set('opcache.protect_memory', 1); +$test = ini_get('opcache.protect_memory'); + +ini_set('opcache.mmap_base', 1); +$test = ini_get('opcache.mmap_base'); + +ini_set('opcache.restrict_api', 1); +$test = ini_get('opcache.restrict_api'); + +ini_set('opcache.file_update_protection', 1); +$test = ini_get('opcache.file_update_protection'); + +ini_set('opcache.huge_code_pages', 1); +$test = ini_get('opcache.huge_code_pages'); + +ini_set('opcache.lockfile_path', 1); +$test = ini_get('opcache.lockfile_path'); + +ini_set('opcache.opt_debug_level', 1); +$test = ini_get('opcache.opt_debug_level'); + +ini_set('opcache.file_cache', 1); +$test = ini_get('opcache.file_cache'); + +ini_set('opcache.file_cache_only', 1); +$test = ini_get('opcache.file_cache_only'); + +ini_set('opcache.file_cache_consistency_checks', 1); +$test = ini_get('opcache.file_cache_consistency_checks'); + +ini_set('opcache.file_cache_fallback', 1); +$test = ini_get('opcache.file_cache_fallback'); + +ini_set('opcache.validate_permission', 1); +$test = ini_get('opcache.validate_permission'); + +ini_set('opcache.validate_root', 1); +$test = ini_get('opcache.validate_root'); + +ini_set('opcache.preload', 1); +$test = ini_get('opcache.preload'); + +ini_set('ffi.enable', 1); +$test = ini_get('ffi.enable'); + +ini_set('ffi.preload', 1); +$test = ini_get('ffi.preload'); diff --git a/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.php b/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.php index ac3cbcf3..9d4751a2 100644 --- a/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.php +++ b/PHPCompatibility/Tests/IniDirectives/NewIniDirectivesUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class NewIniDirectivesUnitTest extends BaseSniffTest { @@ -168,6 +169,37 @@ public function dataNewIniDirectives() array('mysqlnd.trace_alloc', '5.5', array(278, 279), '5.4'), array('sys_temp_dir', '5.5', array(281, 282), '5.4'), array('xsl.security_prefs', '5.5', array(284, 285), '5.4'), + array('opcache.enable', '5.5', array(344, 345), '5.4'), + array('opcache.enable_cli', '5.5', array(347, 348), '5.4'), + array('opcache.memory_consumption', '5.5', array(350, 351), '5.4'), + array('opcache.interned_strings_buffer', '5.5', array(353, 354), '5.4'), + array('opcache.max_accelerated_files', '5.5', array(356, 357), '5.4'), + array('opcache.max_wasted_percentage', '5.5', array(359, 360), '5.4'), + array('opcache.use_cwd', '5.5', array(362, 363), '5.4'), + array('opcache.validate_timestamps', '5.5', array(365, 366), '5.4'), + array('opcache.revalidate_freq', '5.5', array(368, 369), '5.4'), + array('opcache.revalidate_path', '5.5', array(371, 372), '5.4'), + array('opcache.save_comments', '5.5', array(374, 375), '5.4'), + array('opcache.load_comments', '5.5', array(377, 378), '5.4'), + array('opcache.fast_shutdown', '5.5', array(380, 381), '5.4'), + array('opcache.enable_file_override', '5.5', array(383, 384), '5.4'), + array('opcache.optimization_level', '5.5', array(386, 387), '5.4'), + array('opcache.inherited_hack', '5.5', array(389, 390), '5.4'), + array('opcache.dups_fix', '5.5', array(392, 393), '5.4'), + array('opcache.blacklist_filename', '5.5', array(395, 396), '5.4'), + array('opcache.max_file_size', '5.5', array(398, 399), '5.4'), + array('opcache.consistency_checks', '5.5', array(401, 402), '5.4'), + array('opcache.force_restart_timeout', '5.5', array(404, 405), '5.4'), + array('opcache.error_log', '5.5', array(407, 408), '5.4'), + array('opcache.log_verbosity_level', '5.5', array(410, 411), '5.4'), + array('opcache.preferred_memory_model', '5.5', array(413, 414), '5.4'), + array('opcache.protect_memory', '5.5', array(416, 417), '5.4'), + array('opcache.mmap_base', '5.5', array(419, 420), '5.4'), + array('opcache.restrict_api', '5.5', array(422, 423), '5.4'), + array('opcache.file_update_protection', '5.5', array(425, 426), '5.4'), + array('opcache.huge_code_pages', '5.5', array(428, 429), '5.4'), + array('opcache.lockfile_path', '5.5', array(431, 432), '5.4'), + array('opcache.opt_debug_level', '5.5', array(434, 435), '5.4'), array('session.use_strict_mode', '5.6', array(287, 288), '5.5.1', '5.5'), @@ -177,6 +209,13 @@ public function dataNewIniDirectives() array('pcre.jit', '7.0', array(296, 297), '5.6'), array('session.lazy_write', '7.0', array(299, 300), '5.6'), array('zend.assertions', '7.0', array(302, 303), '5.6'), + array('opcache.file_cache', '7.0', array(437, 438), '5.6'), + array('opcache.file_cache_only', '7.0', array(440, 441), '5.6'), + array('opcache.file_cache_consistency_checks', '7.0', array(443, 444), '5.6'), + array('opcache.file_cache_fallback', '7.0', array(446, 447), '5.6'), + + array('opcache.validate_permission', '7.1', array(449, 450), '7.0.13', '7.0'), + array('opcache.validate_root', '7.1', array(452, 453), '7.0.13', '7.0'), array('hard_timeout', '7.1', array(320, 321), '7.0'), array('session.sid_length', '7.1', array(305, 306), '7.0'), @@ -191,6 +230,12 @@ public function dataNewIniDirectives() array('syslog.ident', '7.3', array(314, 315), '7.2'), array('syslog.filter', '7.3', array(317, 318), '7.2'), array('session.cookie_samesite', '7.3', array(332, 333), '7.2'), + + array('ffi.enable', '7.4', array(458, 459), '7.3'), + array('ffi.preload', '7.4', array(461, 462), '7.3'), + array('opcache.cache_id', '7.4', array(341, 342), '7.3'), + array('opcache.preload', '7.4', array(455, 456), '7.3'), + array('zend.exception_ignore_args', '7.4', array(338, 339), '7.3'), ); } diff --git a/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.inc b/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.inc index 423b7316..c5b3643b 100644 --- a/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.inc +++ b/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.inc @@ -156,8 +156,8 @@ myClass::ini_set('ANIMALS', 'dog'); $object->ini_get('ANIMALS', 'dog'); class myClass { - const ini_set = true; - function ini_get() {} + const ini_set = true; + function ini_get() {} } ini_setter('ANIMALS', 'dog'); @@ -183,3 +183,60 @@ $a = ini_get('opcache.inherited_hack'); ini_set('pdo_odbc.db2_instance_name', 'string'); $a = ini_get('pdo_odbc.db2_instance_name'); + +ini_set('ibase.allow_persistent', true); +$a = ini_get('ibase.allow_persistent'); + +ini_set('ibase.max_persistent', '-1'); +$a = ini_get('ibase.max_persistent'); + +ini_set('ibase.max_links', 10); +$a = ini_get('ibase.max_links'); + +ini_set('ibase.default_db', 'string'); +$a = ini_get('ibase.default_db'); + +ini_set('ibase.default_user', 'string'); +$a = ini_get('ibase.default_user'); + +ini_set('ibase.default_password', 'string'); +$a = ini_get('ibase.default_password'); + +ini_set('ibase.default_charset', 'string'); +$a = ini_get('ibase.default_charset'); + +ini_set('ibase.timestampformat', 'string'); +$a = ini_get('ibase.timestampformat'); + +ini_set('ibase.dateformat', '%Y-%m-%d %H:%M:%S'); +$a = ini_get('ibase.dateformat'); + +ini_set('ibase.timeformat', '%Y-%m-%d'); +$a = ini_get('ibase.timeformat'); + +ini_set('pfpro.defaulthost', 'string'); +$a = ini_get('pfpro.defaulthost'); + +ini_set('pfpro.defaultport', 'string'); +$a = ini_get('pfpro.defaultport'); + +ini_set('pfpro.defaulttimeout', 'string'); +$a = ini_get('pfpro.defaulttimeout'); + +ini_set('pfpro.proxyaddress', 'string'); +$a = ini_get('pfpro.proxyaddress'); + +ini_set('pfpro.proxyport', 'string'); +$a = ini_get('pfpro.proxyport'); + +ini_set('pfpro.proxylogon', 'string'); +$a = ini_get('pfpro.proxylogon'); + +ini_set('pfpro.proxypassword', 'string'); +$a = ini_get('pfpro.proxypassword'); + +ini_set('allow_url_include', 'On'); +$a = ini_get('allow_url_include'); + +ini_set('opcache.load_comments', 'On'); +$a = ini_get('opcache.load_comments'); diff --git a/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.php b/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.php index af407b96..da069daa 100644 --- a/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.php +++ b/PHPCompatibility/Tests/IniDirectives/RemovedIniDirectivesUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class RemovedIniDirectivesUnitTest extends BaseSniffTest { @@ -152,6 +153,8 @@ public function dataDeprecatedDirectives() array('track_errors', '7.2', array(172, 173), '7.1'), array('pdo_odbc.db2_instance_name', '7.3', array(184, 185), '7.2'), + + array('allow_url_include', '7.4', array(238, 239), '7.3'), ); } @@ -256,6 +259,7 @@ public function dataRemovedDirectives() array('asp_tags', '7.0', array(83, 84), '5.6'), array('xsl.security_prefs', '7.0', array(86, 87), '5.6'), + array('opcache.load_comments', '7.0', array(241, 242), '5.6'), array('session.entropy_file', '7.1', array(141, 142), '7.0'), array('session.entropy_length', '7.1', array(144, 145), '7.0'), @@ -266,6 +270,25 @@ public function dataRemovedDirectives() array('opcache.fast_shutdown', '7.2', array(175, 176), '7.1'), array('birdstep.max_links', '7.3', array(178, 179), '7.2'), + + array('ibase.allow_persistent', '7.4', array(187, 188), '7.3'), + array('ibase.max_persistent', '7.4', array(190, 191), '7.3'), + array('ibase.max_links', '7.4', array(193, 194), '7.3'), + array('ibase.default_db', '7.4', array(196, 197), '7.3'), + array('ibase.default_user', '7.4', array(199, 200), '7.3'), + array('ibase.default_password', '7.4', array(202, 203), '7.3'), + array('ibase.default_charset', '7.4', array(205, 206), '7.3'), + array('ibase.timestampformat', '7.4', array(208, 209), '7.3'), + array('ibase.dateformat', '7.4', array(211, 212), '7.3'), + array('ibase.timeformat', '7.4', array(214, 215), '7.3'), + + array('pfpro.defaulthost', '5.1', array(217, 218), '5.0'), + array('pfpro.defaultport', '5.1', array(220, 221), '5.0'), + array('pfpro.defaulttimeout', '5.1', array(223, 224), '5.0'), + array('pfpro.proxyaddress', '5.1', array(226, 227), '5.0'), + array('pfpro.proxyport', '5.1', array(229, 230), '5.0'), + array('pfpro.proxylogon', '5.1', array(232, 233), '5.0'), + array('pfpro.proxypassword', '5.1', array(235, 236), '5.0'), ); } diff --git a/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingConstUnitTest.php b/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingConstUnitTest.php index cc62bcb3..84a61c43 100644 --- a/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingConstUnitTest.php +++ b/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingConstUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class NewConstantArraysUsingConstUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingDefineUnitTest.php b/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingDefineUnitTest.php index 1dd54a3a..edc38a47 100644 --- a/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingDefineUnitTest.php +++ b/PHPCompatibility/Tests/InitialValue/NewConstantArraysUsingDefineUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewConstantArraysUsingDefineUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/InitialValue/NewConstantScalarExpressionsUnitTest.php b/PHPCompatibility/Tests/InitialValue/NewConstantScalarExpressionsUnitTest.php index 5b43bb44..6c7c1a66 100644 --- a/PHPCompatibility/Tests/InitialValue/NewConstantScalarExpressionsUnitTest.php +++ b/PHPCompatibility/Tests/InitialValue/NewConstantScalarExpressionsUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class NewConstantScalarExpressionsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/InitialValue/NewHeredocUnitTest.php b/PHPCompatibility/Tests/InitialValue/NewHeredocUnitTest.php index b274684f..e68f7f0c 100644 --- a/PHPCompatibility/Tests/InitialValue/NewHeredocUnitTest.php +++ b/PHPCompatibility/Tests/InitialValue/NewHeredocUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class NewHeredocUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Interfaces/InternalInterfacesUnitTest.php b/PHPCompatibility/Tests/Interfaces/InternalInterfacesUnitTest.php index b83b961f..21e920f5 100644 --- a/PHPCompatibility/Tests/Interfaces/InternalInterfacesUnitTest.php +++ b/PHPCompatibility/Tests/Interfaces/InternalInterfacesUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class InternalInterfacesUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Interfaces/NewInterfacesUnitTest.php b/PHPCompatibility/Tests/Interfaces/NewInterfacesUnitTest.php index 238546c4..c82e7d0d 100644 --- a/PHPCompatibility/Tests/Interfaces/NewInterfacesUnitTest.php +++ b/PHPCompatibility/Tests/Interfaces/NewInterfacesUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class NewInterfacesUnitTest extends BaseSniffTest { @@ -97,7 +98,7 @@ public function dataNewInterface() public function testUnsupportedMethods($line, $methodName) { $file = $this->sniffFile(__FILE__, '5.1'); // Version in which the Serializable interface was introduced. - $this->assertError($file, $line, "Classes that implement interface Serializable do not support the method {$methodName}(). See http://php.net/serializable"); + $this->assertError($file, $line, "Classes that implement interface Serializable do not support the method {$methodName}(). See https://www.php.net/serializable"); } /** diff --git a/PHPCompatibility/Tests/Keywords/CaseSensitiveKeywordsUnitTest.php b/PHPCompatibility/Tests/Keywords/CaseSensitiveKeywordsUnitTest.php index 2dc6a634..327844ed 100644 --- a/PHPCompatibility/Tests/Keywords/CaseSensitiveKeywordsUnitTest.php +++ b/PHPCompatibility/Tests/Keywords/CaseSensitiveKeywordsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class CaseSensitiveKeywordsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-const.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-const.php index e5c4ebe8..d8d447c4 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-const.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-const.php @@ -13,15 +13,20 @@ class Foobar { const CONST = 1; } class Foobar { const CONTINUE = 1; } class Foobar { const DECLARE = 1; } class Foobar { const DEFAULT = 1; } +class Foobar { const DIE = 1; } class Foobar { const DO = 1; } +class Foobar { const ECHO = 1; } class Foobar { const ELSE = 1; } class Foobar { const ELSEIF = 1; } +class Foobar { const EMPTY = 1; } class Foobar { const ENDDECLARE = 1; } class Foobar { const ENDFOR = 1; } class Foobar { const ENDFOREACH = 1; } class Foobar { const ENDIF = 1; } class Foobar { const ENDSWITCH = 1; } class Foobar { const ENDWHILE = 1; } +class Foobar { const EVAL = 1; } +class Foobar { const EXIT = 1; } class Foobar { const EXTENDS = 1; } class Foobar { const FINAL = 1; } class Foobar { const FINALLY = 1; } @@ -32,24 +37,34 @@ class Foobar { const GLOBAL = 1; } class Foobar { const GOTO = 1; } class Foobar { const IF = 1; } class Foobar { const IMPLEMENTS = 1; } -class Foobar { const INTERFACE = 1; } +class Foobar { const INCLUDE = 1; } +class Foobar { const INCLUDE_ONCE = 1; } class Foobar { const INSTANCEOF = 1; } class Foobar { const INSTEADOF = 1; } +class Foobar { const INTERFACE = 1; } +class Foobar { const ISSET = 1; } +class Foobar { const LIST = 1; } class Foobar { const NAMESPACE = 1; } class Foobar { const NEW = 1; } class Foobar { const OR = 1; } +class Foobar { const PRINT = 1; } class Foobar { const PRIVATE = 1; } class Foobar { const PROTECTED = 1; } class Foobar { const PUBLIC = 1; } +class Foobar { const REQUIRE = 1; } +class Foobar { const REQUIRE_ONCE = 1; } +class Foobar { const RETURN = 1; } class Foobar { const STATIC = 1; } class Foobar { const SWITCH = 1; } class Foobar { const THROW = 1; } class Foobar { const TRAIT = 1; } class Foobar { const TRY = 1; } +class Foobar { const UNSET = 1; } class Foobar { const USE = 1; } class Foobar { const VAR = 1; } class Foobar { const WHILE = 1; } class Foobar { const XOR = 1; } +class Foobar { const YIELD = 1; } class Foobar { const __CLASS__ = 1; } class Foobar { const __DIR__ = 1; } class Foobar { const __FILE__ = 1; } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-extends.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-extends.php index db1a596d..d6dda93b 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-extends.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-extends.php @@ -14,15 +14,20 @@ class Foobar extends const {} class Foobar extends continue {} class Foobar extends declare {} class Foobar extends default {} +class Foobar extends die {} class Foobar extends do {} +class Foobar extends echo {} class Foobar extends else {} class Foobar extends elseif {} +class Foobar extends empty {} class Foobar extends enddeclare {} class Foobar extends endfor {} class Foobar extends endforeach {} class Foobar extends endif {} class Foobar extends endswitch {} class Foobar extends endwhile {} +class Foobar extends eval {} +class Foobar extends exit {} class Foobar extends extends {} class Foobar extends final {} class Foobar extends finally {} @@ -33,24 +38,34 @@ class Foobar extends global {} class Foobar extends goto {} class Foobar extends if {} class Foobar extends implements {} -class Foobar extends interface {} +class Foobar extends include {} +class Foobar extends include_once {} class Foobar extends instanceof {} class Foobar extends insteadof {} +class Foobar extends interface {} +class Foobar extends isset {} +class Foobar extends list {} class Foobar extends namespace {} class Foobar extends new {} class Foobar extends or {} +class Foobar extends print {} class Foobar extends private {} class Foobar extends protected {} class Foobar extends public {} +class Foobar extends require {} +class Foobar extends require_once {} +class Foobar extends return {} class Foobar extends static {} class Foobar extends switch {} class Foobar extends throw {} class Foobar extends trait {} class Foobar extends try {} +class Foobar extends unset {} class Foobar extends use {} class Foobar extends var {} class Foobar extends while {} class Foobar extends xor {} +class Foobar extends yield {} class Foobar extends __CLASS__ {} class Foobar extends __DIR__ {} class Foobar extends __FILE__ {} diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-final-method.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-final-method.php index 8174cdc0..86d203c8 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-final-method.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-final-method.php @@ -14,15 +14,20 @@ class Foobar { use BazTrait { oldfunction as final const } } class Foobar { use BazTrait { oldfunction as final continue } } class Foobar { use BazTrait { oldfunction as final declare } } class Foobar { use BazTrait { oldfunction as final default } } +class Foobar { use BazTrait { oldfunction as final die } } class Foobar { use BazTrait { oldfunction as final do } } +class Foobar { use BazTrait { oldfunction as final echo } } class Foobar { use BazTrait { oldfunction as final else } } class Foobar { use BazTrait { oldfunction as final elseif } } +class Foobar { use BazTrait { oldfunction as final empty } } class Foobar { use BazTrait { oldfunction as final enddeclare } } class Foobar { use BazTrait { oldfunction as final endfor } } class Foobar { use BazTrait { oldfunction as final endforeach } } class Foobar { use BazTrait { oldfunction as final endif } } class Foobar { use BazTrait { oldfunction as final endswitch } } class Foobar { use BazTrait { oldfunction as final endwhile } } +class Foobar { use BazTrait { oldfunction as final eval } } +class Foobar { use BazTrait { oldfunction as final exit } } class Foobar { use BazTrait { oldfunction as final extends } } class Foobar { use BazTrait { oldfunction as final final } } class Foobar { use BazTrait { oldfunction as final finally } } @@ -33,24 +38,34 @@ class Foobar { use BazTrait { oldfunction as final global } } class Foobar { use BazTrait { oldfunction as final goto } } class Foobar { use BazTrait { oldfunction as final if } } class Foobar { use BazTrait { oldfunction as final implements } } -class Foobar { use BazTrait { oldfunction as final interface } } +class Foobar { use BazTrait { oldfunction as final include } } +class Foobar { use BazTrait { oldfunction as final include_once } } class Foobar { use BazTrait { oldfunction as final instanceof } } class Foobar { use BazTrait { oldfunction as final insteadof } } +class Foobar { use BazTrait { oldfunction as final interface } } +class Foobar { use BazTrait { oldfunction as final isset } } +class Foobar { use BazTrait { oldfunction as final list } } class Foobar { use BazTrait { oldfunction as final namespace } } class Foobar { use BazTrait { oldfunction as final new } } class Foobar { use BazTrait { oldfunction as final or } } +class Foobar { use BazTrait { oldfunction as final print } } class Foobar { use BazTrait { oldfunction as final private } } class Foobar { use BazTrait { oldfunction as final protected } } class Foobar { use BazTrait { oldfunction as final public } } +class Foobar { use BazTrait { oldfunction as final require } } +class Foobar { use BazTrait { oldfunction as final require_once } } +class Foobar { use BazTrait { oldfunction as final return } } class Foobar { use BazTrait { oldfunction as final static } } class Foobar { use BazTrait { oldfunction as final switch } } class Foobar { use BazTrait { oldfunction as final throw } } class Foobar { use BazTrait { oldfunction as final trait } } class Foobar { use BazTrait { oldfunction as final try } } +class Foobar { use BazTrait { oldfunction as final unset } } class Foobar { use BazTrait { oldfunction as final use } } class Foobar { use BazTrait { oldfunction as final var } } class Foobar { use BazTrait { oldfunction as final while } } class Foobar { use BazTrait { oldfunction as final xor } } +class Foobar { use BazTrait { oldfunction as final yield } } class Foobar { use BazTrait { oldfunction as final __CLASS__ } } class Foobar { use BazTrait { oldfunction as final __DIR__ } } class Foobar { use BazTrait { oldfunction as final __FILE__ } } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-method.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-method.php index 06e65656..52ce25a4 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-method.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-method.php @@ -14,15 +14,20 @@ class Foobar { use BazTrait { oldfunction as const } } class Foobar { use BazTrait { oldfunction as continue } } class Foobar { use BazTrait { oldfunction as declare } } class Foobar { use BazTrait { oldfunction as default } } +class Foobar { use BazTrait { oldfunction as die } } class Foobar { use BazTrait { oldfunction as do } } +class Foobar { use BazTrait { oldfunction as echo } } class Foobar { use BazTrait { oldfunction as else } } class Foobar { use BazTrait { oldfunction as elseif } } +class Foobar { use BazTrait { oldfunction as empty } } class Foobar { use BazTrait { oldfunction as enddeclare } } class Foobar { use BazTrait { oldfunction as endfor } } class Foobar { use BazTrait { oldfunction as endforeach } } class Foobar { use BazTrait { oldfunction as endif } } class Foobar { use BazTrait { oldfunction as endswitch } } class Foobar { use BazTrait { oldfunction as endwhile } } +class Foobar { use BazTrait { oldfunction as eval } } +class Foobar { use BazTrait { oldfunction as exit } } class Foobar { use BazTrait { oldfunction as extends } } class Foobar { use BazTrait { oldfunction as finally } } class Foobar { use BazTrait { oldfunction as for } } @@ -32,21 +37,31 @@ class Foobar { use BazTrait { oldfunction as global } } class Foobar { use BazTrait { oldfunction as goto } } class Foobar { use BazTrait { oldfunction as if } } class Foobar { use BazTrait { oldfunction as implements } } -class Foobar { use BazTrait { oldfunction as interface } } +class Foobar { use BazTrait { oldfunction as include } } +class Foobar { use BazTrait { oldfunction as include_once } } class Foobar { use BazTrait { oldfunction as instanceof } } class Foobar { use BazTrait { oldfunction as insteadof } } +class Foobar { use BazTrait { oldfunction as interface } } +class Foobar { use BazTrait { oldfunction as isset } } +class Foobar { use BazTrait { oldfunction as list } } class Foobar { use BazTrait { oldfunction as namespace } } class Foobar { use BazTrait { oldfunction as new } } class Foobar { use BazTrait { oldfunction as or } } +class Foobar { use BazTrait { oldfunction as print } } +class Foobar { use BazTrait { oldfunction as require } } +class Foobar { use BazTrait { oldfunction as require_once } } +class Foobar { use BazTrait { oldfunction as return } } class Foobar { use BazTrait { oldfunction as static } } class Foobar { use BazTrait { oldfunction as switch } } class Foobar { use BazTrait { oldfunction as throw } } class Foobar { use BazTrait { oldfunction as trait } } class Foobar { use BazTrait { oldfunction as try } } +class Foobar { use BazTrait { oldfunction as unset } } class Foobar { use BazTrait { oldfunction as use } } class Foobar { use BazTrait { oldfunction as var } } class Foobar { use BazTrait { oldfunction as while } } class Foobar { use BazTrait { oldfunction as xor } } +class Foobar { use BazTrait { oldfunction as yield } } class Foobar { use BazTrait { oldfunction as __CLASS__ } } class Foobar { use BazTrait { oldfunction as __DIR__ } } class Foobar { use BazTrait { oldfunction as __FILE__ } } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-private-method.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-private-method.php index 18aec71a..9010f8c2 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-private-method.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-private-method.php @@ -14,15 +14,20 @@ class Foobar { use BazTrait { oldfunction as private const } } class Foobar { use BazTrait { oldfunction as private continue } } class Foobar { use BazTrait { oldfunction as private declare } } class Foobar { use BazTrait { oldfunction as private default } } +class Foobar { use BazTrait { oldfunction as private die } } class Foobar { use BazTrait { oldfunction as private do } } +class Foobar { use BazTrait { oldfunction as private echo } } class Foobar { use BazTrait { oldfunction as private else } } class Foobar { use BazTrait { oldfunction as private elseif } } +class Foobar { use BazTrait { oldfunction as private empty } } class Foobar { use BazTrait { oldfunction as private enddeclare } } class Foobar { use BazTrait { oldfunction as private endfor } } class Foobar { use BazTrait { oldfunction as private endforeach } } class Foobar { use BazTrait { oldfunction as private endif } } class Foobar { use BazTrait { oldfunction as private endswitch } } class Foobar { use BazTrait { oldfunction as private endwhile } } +class Foobar { use BazTrait { oldfunction as private eval } } +class Foobar { use BazTrait { oldfunction as private exit } } class Foobar { use BazTrait { oldfunction as private extends } } class Foobar { use BazTrait { oldfunction as private final } } class Foobar { use BazTrait { oldfunction as private finally } } @@ -33,24 +38,34 @@ class Foobar { use BazTrait { oldfunction as private global } } class Foobar { use BazTrait { oldfunction as private goto } } class Foobar { use BazTrait { oldfunction as private if } } class Foobar { use BazTrait { oldfunction as private implements } } -class Foobar { use BazTrait { oldfunction as private interface } } +class Foobar { use BazTrait { oldfunction as private include } } +class Foobar { use BazTrait { oldfunction as private include_once } } class Foobar { use BazTrait { oldfunction as private instanceof } } class Foobar { use BazTrait { oldfunction as private insteadof } } +class Foobar { use BazTrait { oldfunction as private interface } } +class Foobar { use BazTrait { oldfunction as private isset } } +class Foobar { use BazTrait { oldfunction as private list } } class Foobar { use BazTrait { oldfunction as private namespace } } class Foobar { use BazTrait { oldfunction as private new } } class Foobar { use BazTrait { oldfunction as private or } } +class Foobar { use BazTrait { oldfunction as private print } } class Foobar { use BazTrait { oldfunction as private private } } class Foobar { use BazTrait { oldfunction as private protected } } class Foobar { use BazTrait { oldfunction as private public } } +class Foobar { use BazTrait { oldfunction as private require } } +class Foobar { use BazTrait { oldfunction as private require_once } } +class Foobar { use BazTrait { oldfunction as private return } } class Foobar { use BazTrait { oldfunction as private static } } class Foobar { use BazTrait { oldfunction as private switch } } class Foobar { use BazTrait { oldfunction as private throw } } class Foobar { use BazTrait { oldfunction as private trait } } class Foobar { use BazTrait { oldfunction as private try } } +class Foobar { use BazTrait { oldfunction as private unset } } class Foobar { use BazTrait { oldfunction as private use } } class Foobar { use BazTrait { oldfunction as private var } } class Foobar { use BazTrait { oldfunction as private while } } class Foobar { use BazTrait { oldfunction as private xor } } +class Foobar { use BazTrait { oldfunction as private yield } } class Foobar { use BazTrait { oldfunction as private __CLASS__ } } class Foobar { use BazTrait { oldfunction as private __DIR__ } } class Foobar { use BazTrait { oldfunction as private __FILE__ } } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-protected-method.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-protected-method.php index dde74191..eb087493 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-protected-method.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-protected-method.php @@ -14,15 +14,20 @@ class Foobar { use BazTrait { oldfunction as protected const } } class Foobar { use BazTrait { oldfunction as protected continue } } class Foobar { use BazTrait { oldfunction as protected declare } } class Foobar { use BazTrait { oldfunction as protected default } } +class Foobar { use BazTrait { oldfunction as protected die } } class Foobar { use BazTrait { oldfunction as protected do } } +class Foobar { use BazTrait { oldfunction as protected echo } } class Foobar { use BazTrait { oldfunction as protected else } } class Foobar { use BazTrait { oldfunction as protected elseif } } +class Foobar { use BazTrait { oldfunction as protected empty } } class Foobar { use BazTrait { oldfunction as protected enddeclare } } class Foobar { use BazTrait { oldfunction as protected endfor } } class Foobar { use BazTrait { oldfunction as protected endforeach } } class Foobar { use BazTrait { oldfunction as protected endif } } class Foobar { use BazTrait { oldfunction as protected endswitch } } class Foobar { use BazTrait { oldfunction as protected endwhile } } +class Foobar { use BazTrait { oldfunction as protected eval } } +class Foobar { use BazTrait { oldfunction as protected exit } } class Foobar { use BazTrait { oldfunction as protected extends } } class Foobar { use BazTrait { oldfunction as protected final } } class Foobar { use BazTrait { oldfunction as protected finally } } @@ -33,24 +38,34 @@ class Foobar { use BazTrait { oldfunction as protected global } } class Foobar { use BazTrait { oldfunction as protected goto } } class Foobar { use BazTrait { oldfunction as protected if } } class Foobar { use BazTrait { oldfunction as protected implements } } -class Foobar { use BazTrait { oldfunction as protected interface } } +class Foobar { use BazTrait { oldfunction as protected include } } +class Foobar { use BazTrait { oldfunction as protected include_once } } class Foobar { use BazTrait { oldfunction as protected instanceof } } class Foobar { use BazTrait { oldfunction as protected insteadof } } +class Foobar { use BazTrait { oldfunction as protected interface } } +class Foobar { use BazTrait { oldfunction as protected isset } } +class Foobar { use BazTrait { oldfunction as protected list } } class Foobar { use BazTrait { oldfunction as protected namespace } } class Foobar { use BazTrait { oldfunction as protected new } } class Foobar { use BazTrait { oldfunction as protected or } } +class Foobar { use BazTrait { oldfunction as protected print } } class Foobar { use BazTrait { oldfunction as protected private } } class Foobar { use BazTrait { oldfunction as protected protected } } class Foobar { use BazTrait { oldfunction as protected public } } +class Foobar { use BazTrait { oldfunction as protected require } } +class Foobar { use BazTrait { oldfunction as protected require_once } } +class Foobar { use BazTrait { oldfunction as protected return } } class Foobar { use BazTrait { oldfunction as protected static } } class Foobar { use BazTrait { oldfunction as protected switch } } class Foobar { use BazTrait { oldfunction as protected throw } } class Foobar { use BazTrait { oldfunction as protected trait } } class Foobar { use BazTrait { oldfunction as protected try } } +class Foobar { use BazTrait { oldfunction as protected unset } } class Foobar { use BazTrait { oldfunction as protected use } } class Foobar { use BazTrait { oldfunction as protected var } } class Foobar { use BazTrait { oldfunction as protected while } } class Foobar { use BazTrait { oldfunction as protected xor } } +class Foobar { use BazTrait { oldfunction as protected yield } } class Foobar { use BazTrait { oldfunction as protected __CLASS__ } } class Foobar { use BazTrait { oldfunction as protected __DIR__ } } class Foobar { use BazTrait { oldfunction as protected __FILE__ } } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-public-method.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-public-method.php index d8e9bc39..1ed4f245 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-public-method.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-alias-public-method.php @@ -14,15 +14,20 @@ class Foobar { use BazTrait { oldfunction as public const } } class Foobar { use BazTrait { oldfunction as public continue } } class Foobar { use BazTrait { oldfunction as public declare } } class Foobar { use BazTrait { oldfunction as public default } } +class Foobar { use BazTrait { oldfunction as public die } } class Foobar { use BazTrait { oldfunction as public do } } +class Foobar { use BazTrait { oldfunction as public echo } } class Foobar { use BazTrait { oldfunction as public else } } class Foobar { use BazTrait { oldfunction as public elseif } } +class Foobar { use BazTrait { oldfunction as public empty } } class Foobar { use BazTrait { oldfunction as public enddeclare } } class Foobar { use BazTrait { oldfunction as public endfor } } class Foobar { use BazTrait { oldfunction as public endforeach } } class Foobar { use BazTrait { oldfunction as public endif } } class Foobar { use BazTrait { oldfunction as public endswitch } } class Foobar { use BazTrait { oldfunction as public endwhile } } +class Foobar { use BazTrait { oldfunction as public eval } } +class Foobar { use BazTrait { oldfunction as public exit } } class Foobar { use BazTrait { oldfunction as public extends } } class Foobar { use BazTrait { oldfunction as public final } } class Foobar { use BazTrait { oldfunction as public finally } } @@ -33,24 +38,34 @@ class Foobar { use BazTrait { oldfunction as public global } } class Foobar { use BazTrait { oldfunction as public goto } } class Foobar { use BazTrait { oldfunction as public if } } class Foobar { use BazTrait { oldfunction as public implements } } -class Foobar { use BazTrait { oldfunction as public interface } } +class Foobar { use BazTrait { oldfunction as public include } } +class Foobar { use BazTrait { oldfunction as public include_once } } class Foobar { use BazTrait { oldfunction as public instanceof } } class Foobar { use BazTrait { oldfunction as public insteadof } } +class Foobar { use BazTrait { oldfunction as public interface } } +class Foobar { use BazTrait { oldfunction as public isset } } +class Foobar { use BazTrait { oldfunction as public list } } class Foobar { use BazTrait { oldfunction as public namespace } } class Foobar { use BazTrait { oldfunction as public new } } class Foobar { use BazTrait { oldfunction as public or } } +class Foobar { use BazTrait { oldfunction as public print } } class Foobar { use BazTrait { oldfunction as public private } } class Foobar { use BazTrait { oldfunction as public protected } } class Foobar { use BazTrait { oldfunction as public public } } +class Foobar { use BazTrait { oldfunction as public require } } +class Foobar { use BazTrait { oldfunction as public require_once } } +class Foobar { use BazTrait { oldfunction as public return } } class Foobar { use BazTrait { oldfunction as public static } } class Foobar { use BazTrait { oldfunction as public switch } } class Foobar { use BazTrait { oldfunction as public throw } } class Foobar { use BazTrait { oldfunction as public trait } } class Foobar { use BazTrait { oldfunction as public try } } +class Foobar { use BazTrait { oldfunction as public unset } } class Foobar { use BazTrait { oldfunction as public use } } class Foobar { use BazTrait { oldfunction as public var } } class Foobar { use BazTrait { oldfunction as public while } } class Foobar { use BazTrait { oldfunction as public xor } } +class Foobar { use BazTrait { oldfunction as public yield } } class Foobar { use BazTrait { oldfunction as public __CLASS__ } } class Foobar { use BazTrait { oldfunction as public __DIR__ } } class Foobar { use BazTrait { oldfunction as public __FILE__ } } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-const.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-const.php index 11ca2bdc..438805d7 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-const.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-const.php @@ -14,15 +14,20 @@ class Foobar { use const const } class Foobar { use const continue } class Foobar { use const declare } class Foobar { use const default } +class Foobar { use const die } class Foobar { use const do } +class Foobar { use const echo } class Foobar { use const else } class Foobar { use const elseif } +class Foobar { use const empty } class Foobar { use const enddeclare } class Foobar { use const endfor } class Foobar { use const endforeach } class Foobar { use const endif } class Foobar { use const endswitch } class Foobar { use const endwhile } +class Foobar { use const eval } +class Foobar { use const exit } class Foobar { use const extends } class Foobar { use const final } class Foobar { use const finally } @@ -33,24 +38,34 @@ class Foobar { use const global } class Foobar { use const goto } class Foobar { use const if } class Foobar { use const implements } -class Foobar { use const interface } +class Foobar { use const include } +class Foobar { use const include_once } class Foobar { use const instanceof } class Foobar { use const insteadof } +class Foobar { use const interface } +class Foobar { use const isset } +class Foobar { use const list } class Foobar { use const namespace } class Foobar { use const new } class Foobar { use const or } +class Foobar { use const print } class Foobar { use const private } class Foobar { use const protected } class Foobar { use const public } +class Foobar { use const require } +class Foobar { use const require_once } +class Foobar { use const return } class Foobar { use const static } class Foobar { use const switch } class Foobar { use const throw } class Foobar { use const trait } class Foobar { use const try } +class Foobar { use const unset } class Foobar { use const use } class Foobar { use const var } class Foobar { use const while } class Foobar { use const xor } +class Foobar { use const yield } class Foobar { use const __CLASS__ } class Foobar { use const __DIR__ } class Foobar { use const __FILE__ } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-function.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-function.php index e868915d..10ffa0e1 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-function.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait-function.php @@ -14,15 +14,20 @@ class Foobar { use function const } class Foobar { use function continue } class Foobar { use function declare } class Foobar { use function default } +class Foobar { use function die } class Foobar { use function do } +class Foobar { use function echo } class Foobar { use function else } class Foobar { use function elseif } +class Foobar { use function empty } class Foobar { use function enddeclare } class Foobar { use function endfor } class Foobar { use function endforeach } class Foobar { use function endif } class Foobar { use function endswitch } class Foobar { use function endwhile } +class Foobar { use function eval } +class Foobar { use function exit } class Foobar { use function extends } class Foobar { use function final } class Foobar { use function finally } @@ -33,24 +38,34 @@ class Foobar { use function global } class Foobar { use function goto } class Foobar { use function if } class Foobar { use function implements } -class Foobar { use function interface } +class Foobar { use function include } +class Foobar { use function include_once } class Foobar { use function instanceof } class Foobar { use function insteadof } +class Foobar { use function interface } +class Foobar { use function isset } +class Foobar { use function list } class Foobar { use function namespace } class Foobar { use function new } class Foobar { use function or } +class Foobar { use function print } class Foobar { use function private } class Foobar { use function protected } class Foobar { use function public } +class Foobar { use function require } +class Foobar { use function require_once } +class Foobar { use function return } class Foobar { use function static } class Foobar { use function switch } class Foobar { use function throw } class Foobar { use function trait } class Foobar { use function try } +class Foobar { use function unset } class Foobar { use function use } class Foobar { use function var } class Foobar { use function while } class Foobar { use function xor } +class Foobar { use function yield } class Foobar { use function __CLASS__ } class Foobar { use function __DIR__ } class Foobar { use function __FILE__ } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait.php index 56a98455..9bcdfc09 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class-use-trait.php @@ -14,15 +14,20 @@ class Foobar { use const } class Foobar { use continue } class Foobar { use declare } class Foobar { use default } +class Foobar { use die } class Foobar { use do } +class Foobar { use echo } class Foobar { use else } class Foobar { use elseif } +class Foobar { use empty } class Foobar { use enddeclare } class Foobar { use endfor } class Foobar { use endforeach } class Foobar { use endif } class Foobar { use endswitch } class Foobar { use endwhile } +class Foobar { use eval } +class Foobar { use exit } class Foobar { use extends } class Foobar { use final } class Foobar { use finally } @@ -33,24 +38,34 @@ class Foobar { use global } class Foobar { use goto } class Foobar { use if } class Foobar { use implements } -class Foobar { use interface } +class Foobar { use include } +class Foobar { use include_once } class Foobar { use instanceof } class Foobar { use insteadof } +class Foobar { use interface } +class Foobar { use isset } +class Foobar { use list } class Foobar { use namespace } class Foobar { use new } class Foobar { use or } +class Foobar { use print } class Foobar { use private } class Foobar { use protected } class Foobar { use public } +class Foobar { use require } +class Foobar { use require_once } +class Foobar { use return } class Foobar { use static } class Foobar { use switch } class Foobar { use throw } class Foobar { use trait } class Foobar { use try } +class Foobar { use unset } class Foobar { use use } class Foobar { use var } class Foobar { use while } class Foobar { use xor } +class Foobar { use yield } class Foobar { use __CLASS__ } class Foobar { use __DIR__ } class Foobar { use __FILE__ } diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class.php index 01ffd443..bebdf29b 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/class.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/class.php @@ -14,15 +14,20 @@ class const {} class continue {} class declare {} class default {} +class die {} class do {} +class echo {} class else {} class elseif {} +class empty {} class enddeclare {} class endfor {} class endforeach {} class endif {} class endswitch {} class endwhile {} +class eval {} +class exit {} class extends {} class final {} class finally {} @@ -33,24 +38,34 @@ class global {} class goto {} class if {} class implements {} -class interface {} +class include {} +class include_once {} class instanceof {} class insteadof {} +class interface {} +class isset {} +class list {} class namespace {} class new {} class or {} +class print {} class private {} class protected {} class public {} +class require {} +class require_once {} +class return {} class static {} class switch {} class throw {} class trait {} class try {} +class unset {} class use {} class var {} class while {} class xor {} +class yield {} class __CLASS__ {} class __DIR__ {} class __FILE__ {} diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/const.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/const.php index c8492c33..b8d7da6c 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/const.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/const.php @@ -14,15 +14,20 @@ const continue = 1; const declare = 1; const default = 1; +const die = 1; const do = 1; +const echo = 1; const else = 1; const elseif = 1; +const empty = 1; const enddeclare = 1; const endfor = 1; const endforeach = 1; const endif = 1; const endswitch = 1; const endwhile = 1; +const eval = 1; +const exit = 1; const extends = 1; const final = 1; const finally = 1; @@ -33,24 +38,34 @@ const goto = 1; const if = 1; const implements = 1; -const interface = 1; +const include = 1; +const include_once = 1; const instanceof = 1; const insteadof = 1; +const interface = 1; +const isset = 1; +const list = 1; const namespace = 1; const new = 1; const or = 1; +const print = 1; const private = 1; const protected = 1; const public = 1; +const require = 1; +const require_once = 1; +const return = 1; const static = 1; const switch = 1; const throw = 1; const trait = 1; const try = 1; +const unset = 1; const use = 1; const var = 1; const while = 1; const xor = 1; +const yield = 1; const __CLASS__ = 1; const __DIR__ = 1; const __FILE__ = 1; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/define.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/define.php index 693d2b90..4cf5eb1e 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/define.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/define.php @@ -14,15 +14,20 @@ define('continue', 1); define('declare', 1); define('default', 1); +define('die', 1); define('do', 1); +define('echo', 1); define('else', 1); define('elseif', 1); +define('empty', 1); define('enddeclare', 1); define('endfor', 1); define('endforeach', 1); define('endif', 1); define('endswitch', 1); define('endwhile', 1); +define('eval', 1); +define('exit', 1); define('extends', 1); define('final', 1); define('finally', 1); @@ -33,24 +38,34 @@ define('goto', 1); define('if', 1); define('implements', 1); -define('interface', 1); +define('include', 1); +define('include_once', 1); define('instanceof', 1); define('insteadof', 1); +define('interface', 1); +define('isset', 1); +define('list', 1); define('namespace', 1); define('new', 1); define('or', 1); +define('print', 1); define('private', 1); define('protected', 1); define('public', 1); +define('require', 1); +define('require_once', 1); +define('return', 1); define('static', 1); define('switch', 1); define('throw', 1); define('trait', 1); define('try', 1); +define('unset', 1); define('use', 1); define('var', 1); define('while', 1); define('xor', 1); +define('yield', 1); define('__CLASS__', 1); define('__DIR__', 1); define('__FILE__', 1); diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare-reference.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare-reference.php index be13b0b4..84b499e0 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare-reference.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare-reference.php @@ -14,15 +14,20 @@ function &const() { }; function &continue() { }; function &declare() { }; function &default() { }; +function &die() { }; function &do() { }; +function &echo() { }; function &else() { }; function &elseif() { }; +function &empty() { }; function &enddeclare() { }; function &endfor() { }; function &endforeach() { }; function &endif() { }; function &endswitch() { }; function &endwhile() { }; +function &eval() { }; +function &exit() { }; function &extends() { }; function &final() { }; function &finally() { }; @@ -33,24 +38,34 @@ function &global() { }; function &goto() { }; function &if() { }; function &implements() { }; -function &interface() { }; +function &include() { }; +function &include_once() { }; function &instanceof() { }; function &insteadof() { }; +function &interface() { }; +function &isset() { }; +function &list() { }; function &namespace() { }; function &new() { }; function &or() { }; +function &print() { }; function &private() { }; function &protected() { }; function &public() { }; +function &require() { }; +function &require_once() { }; +function &return() { }; function &static() { }; function &switch() { }; function &throw() { }; function &trait() { }; function &try() { }; +function &unset() { }; function &use() { }; function &var() { }; function &while() { }; function &xor() { }; +function &yield() { }; function &__CLASS__() { }; function &__DIR__() { }; function &__FILE__() { }; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare.php index 2cd2bd88..5ea7c246 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/function-declare.php @@ -14,15 +14,20 @@ function const() { }; function continue() { }; function declare() { }; function default() { }; +function die() { }; function do() { }; +function echo() { }; function else() { }; function elseif() { }; +function empty() { }; function enddeclare() { }; function endfor() { }; function endforeach() { }; function endif() { }; function endswitch() { }; function endwhile() { }; +function eval() { }; +function exit() { }; function extends() { }; function final() { }; function finally() { }; @@ -33,24 +38,34 @@ function global() { }; function goto() { }; function if() { }; function implements() { }; -function interface() { }; +function include() { }; +function include_once() { }; function instanceof() { }; function insteadof() { }; +function interface() { }; +function isset() { }; +function list() { }; function namespace() { }; function new() { }; function or() { }; +function print() { }; function private() { }; function protected() { }; function public() { }; +function require() { }; +function require_once() { }; +function return() { }; function static() { }; function switch() { }; function throw() { }; function trait() { }; function try() { }; +function unset() { }; function use() { }; function var() { }; function while() { }; function xor() { }; +function yield() { }; function __CLASS__() { }; function __DIR__() { }; function __FILE__() { }; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface-extends.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface-extends.php index e1997913..a0037b5c 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface-extends.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface-extends.php @@ -14,15 +14,20 @@ interface Foobar extends const {} interface Foobar extends continue {} interface Foobar extends declare {} interface Foobar extends default {} +interface Foobar extends die {} interface Foobar extends do {} +interface Foobar extends echo {} interface Foobar extends else {} interface Foobar extends elseif {} +interface Foobar extends empty {} interface Foobar extends enddeclare {} interface Foobar extends endfor {} interface Foobar extends endforeach {} interface Foobar extends endif {} interface Foobar extends endswitch {} interface Foobar extends endwhile {} +interface Foobar extends eval {} +interface Foobar extends exit {} interface Foobar extends extends {} interface Foobar extends final {} interface Foobar extends finally {} @@ -33,24 +38,34 @@ interface Foobar extends global {} interface Foobar extends goto {} interface Foobar extends if {} interface Foobar extends implements {} -interface Foobar extends interface {} +interface Foobar extends include {} +interface Foobar extends include_once {} interface Foobar extends instanceof {} interface Foobar extends insteadof {} +interface Foobar extends interface {} +interface Foobar extends isset {} +interface Foobar extends list {} interface Foobar extends namespace {} interface Foobar extends new {} interface Foobar extends or {} +interface Foobar extends print {} interface Foobar extends private {} interface Foobar extends protected {} interface Foobar extends public {} +interface Foobar extends require {} +interface Foobar extends require_once {} +interface Foobar extends return {} interface Foobar extends static {} interface Foobar extends switch {} interface Foobar extends throw {} interface Foobar extends trait {} interface Foobar extends try {} +interface Foobar extends unset {} interface Foobar extends use {} interface Foobar extends var {} interface Foobar extends while {} interface Foobar extends xor {} +interface Foobar extends yield {} interface Foobar extends __CLASS__ {} interface Foobar extends __DIR__ {} interface Foobar extends __FILE__ {} diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface.php index 975cfec1..90ea7f6e 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/interface.php @@ -14,15 +14,20 @@ interface const {} interface continue {} interface declare {} interface default {} +interface die {} interface do {} +interface echo {} interface else {} interface elseif {} +interface empty {} interface enddeclare {} interface endfor {} interface endforeach {} interface endif {} interface endswitch {} interface endwhile {} +interface eval {} +interface exit {} interface extends {} interface final {} interface finally {} @@ -33,24 +38,34 @@ interface global {} interface goto {} interface if {} interface implements {} -interface interface {} +interface include {} +interface include_once {} interface instanceof {} interface insteadof {} +interface interface {} +interface isset {} +interface list {} interface namespace {} interface new {} interface or {} +interface print {} interface private {} interface protected {} interface public {} +interface require {} +interface require_once {} +interface return {} interface static {} interface switch {} interface throw {} interface trait {} interface try {} +interface unset {} interface use {} interface var {} interface while {} interface xor {} +interface yield {} interface __CLASS__ {} interface __DIR__ {} interface __FILE__ {} diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/method-declare.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/method-declare.php index 888e1ca8..39208953 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/method-declare.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/method-declare.php @@ -14,15 +14,20 @@ class Foobar { function const() { } }; class Foobar { function continue() { } }; class Foobar { function declare() { } }; class Foobar { function default() { } }; +class Foobar { function die() { } }; class Foobar { function do() { } }; +class Foobar { function echo() { } }; class Foobar { function else() { } }; class Foobar { function elseif() { } }; +class Foobar { function empty() { } }; class Foobar { function enddeclare() { } }; class Foobar { function endfor() { } }; class Foobar { function endforeach() { } }; class Foobar { function endif() { } }; class Foobar { function endswitch() { } }; class Foobar { function endwhile() { } }; +class Foobar { function eval() { } }; +class Foobar { function exit() { } }; class Foobar { function extends() { } }; class Foobar { function final() { } }; class Foobar { function finally() { } }; @@ -33,24 +38,34 @@ class Foobar { function global() { } }; class Foobar { function goto() { } }; class Foobar { function if() { } }; class Foobar { function implements() { } }; -class Foobar { function interface() { } }; +class Foobar { function include() { } }; +class Foobar { function include_once() { } }; class Foobar { function instanceof() { } }; class Foobar { function insteadof() { } }; +class Foobar { function interface() { } }; +class Foobar { function isset() { } }; +class Foobar { function list() { } }; class Foobar { function namespace() { } }; class Foobar { function new() { } }; class Foobar { function or() { } }; +class Foobar { function print() { } }; class Foobar { function private() { } }; class Foobar { function protected() { } }; class Foobar { function public() { } }; +class Foobar { function require() { } }; +class Foobar { function require_once() { } }; +class Foobar { function return() { } }; class Foobar { function static() { } }; class Foobar { function switch() { } }; class Foobar { function throw() { } }; class Foobar { function trait() { } }; class Foobar { function try() { } }; +class Foobar { function unset() { } }; class Foobar { function use() { } }; class Foobar { function var() { } }; class Foobar { function while() { } }; class Foobar { function xor() { } }; +class Foobar { function yield() { } }; class Foobar { function __CLASS__() { } }; class Foobar { function __DIR__() { } }; class Foobar { function __FILE__() { } }; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/namespace.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/namespace.php index 6e1c333f..741b3d18 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/namespace.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/namespace.php @@ -14,15 +14,20 @@ namespace continue; namespace declare; namespace default; +namespace die; namespace do; +namespace echo; namespace else; namespace elseif; +namespace empty; namespace enddeclare; namespace endfor; namespace endforeach; namespace endif; namespace endswitch; namespace endwhile; +namespace eval; +namespace exit; namespace extends; namespace final; namespace finally; @@ -33,24 +38,34 @@ namespace goto; namespace if; namespace implements; -namespace interface; +namespace include; +namespace include_once; namespace instanceof; namespace insteadof; +namespace interface; +namespace isset; +namespace list; namespace namespace; namespace new; namespace or; +namespace print; namespace private; namespace protected; namespace public; +namespace require; +namespace require_once; +namespace return; namespace static; namespace switch; namespace throw; namespace trait; namespace try; +namespace unset; namespace use; namespace var; namespace while; namespace xor; +namespace yield; namespace __CLASS__; namespace __DIR__; namespace __FILE__; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/nested-namespace.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/nested-namespace.php index 13bc5e22..606b792d 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/nested-namespace.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/nested-namespace.php @@ -14,15 +14,20 @@ namespace Foo\continue\Bar; namespace Foo\declare\Bar; namespace Foo\default\Bar; +namespace Foo\die\Bar; namespace Foo\do\Bar; +namespace Foo\echo\Bar; namespace Foo\else\Bar; namespace Foo\elseif\Bar; +namespace Foo\empty\Bar; namespace Foo\enddeclare\Bar; namespace Foo\endfor\Bar; namespace Foo\endforeach\Bar; namespace Foo\endif\Bar; namespace Foo\endswitch\Bar; namespace Foo\endwhile\Bar; +namespace Foo\eval\Bar; +namespace Foo\exit\Bar; namespace Foo\extends\Bar; namespace Foo\final\Bar; namespace Foo\finally\Bar; @@ -33,24 +38,34 @@ namespace Foo\goto\Bar; namespace Foo\if\Bar; namespace Foo\implements\Bar; -namespace Foo\interface\Bar; +namespace Foo\include\Bar; +namespace Foo\include_once\Bar; namespace Foo\instanceof\Bar; namespace Foo\insteadof\Bar; +namespace Foo\interface\Bar; +namespace Foo\isset\Bar; +namespace Foo\list\Bar; namespace Foo\namespace\Bar; namespace Foo\new\Bar; namespace Foo\or\Bar; +namespace Foo\print\Bar; namespace Foo\private\Bar; namespace Foo\protected\Bar; namespace Foo\public\Bar; +namespace Foo\require\Bar; +namespace Foo\require_once\Bar; +namespace Foo\return\Bar; namespace Foo\static\Bar; namespace Foo\switch\Bar; namespace Foo\throw\Bar; namespace Foo\trait\Bar; namespace Foo\try\Bar; +namespace Foo\unset\Bar; namespace Foo\use\Bar; namespace Foo\var\Bar; namespace Foo\while\Bar; namespace Foo\xor\Bar; +namespace Foo\yield\Bar; namespace Foo\__CLASS__\Bar; namespace Foo\__DIR__\Bar; namespace Foo\__FILE__\Bar; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/trait.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/trait.php index d6a73cd8..4b169134 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/trait.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/trait.php @@ -14,15 +14,20 @@ trait const {} trait continue {} trait declare {} trait default {} +trait die {} trait do {} +trait echo {} trait else {} trait elseif {} +trait empty {} trait enddeclare {} trait endfor {} trait endforeach {} trait endif {} trait endswitch {} trait endwhile {} +trait eval {} +trait exit {} trait extends {} trait final {} trait finally {} @@ -33,24 +38,34 @@ trait global {} trait goto {} trait if {} trait implements {} -trait interface {} +trait include {} +trait include_once {} trait instanceof {} trait insteadof {} +trait interface {} +trait isset {} +trait list {} trait namespace {} trait new {} trait or {} +trait print {} trait private {} trait protected {} trait public {} +trait require {} +trait require_once {} +trait return {} trait static {} trait switch {} trait throw {} trait trait {} trait try {} +trait unset {} trait use {} trait var {} trait while {} trait xor {} +trait yield {} trait __CLASS__ {} trait __DIR__ {} trait __FILE__ {} diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/use-as.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/use-as.php index 7ba77f31..866512f6 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/use-as.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/use-as.php @@ -14,15 +14,20 @@ use Foobar as continue; use Foobar as declare; use Foobar as default; +use Foobar as die; use Foobar as do; +use Foobar as echo; use Foobar as else; use Foobar as elseif; +use Foobar as empty; use Foobar as enddeclare; use Foobar as endfor; use Foobar as endforeach; use Foobar as endif; use Foobar as endswitch; use Foobar as endwhile; +use Foobar as eval; +use Foobar as exit; use Foobar as extends; use Foobar as final; use Foobar as finally; @@ -33,24 +38,34 @@ use Foobar as goto; use Foobar as if; use Foobar as implements; -use Foobar as interface; +use Foobar as include; +use Foobar as include_once; use Foobar as instanceof; use Foobar as insteadof; +use Foobar as interface; +use Foobar as isset; +use Foobar as list; use Foobar as namespace; use Foobar as new; use Foobar as or; +use Foobar as print; use Foobar as private; use Foobar as protected; use Foobar as public; +use Foobar as require; +use Foobar as require_once; +use Foobar as return; use Foobar as static; use Foobar as switch; use Foobar as throw; use Foobar as trait; use Foobar as try; +use Foobar as unset; use Foobar as use; use Foobar as var; use Foobar as while; use Foobar as xor; +use Foobar as yield; use Foobar as __CLASS__; use Foobar as __DIR__; use Foobar as __FILE__; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNames/use.php b/PHPCompatibility/Tests/Keywords/ForbiddenNames/use.php index ead2a4cd..51fee986 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNames/use.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNames/use.php @@ -14,15 +14,20 @@ use continue; use declare; use default; +use die; use do; +use echo; use else; use elseif; +use empty; use enddeclare; use endfor; use endforeach; use endif; use endswitch; use endwhile; +use eval; +use exit; use extends; use final; use finally; @@ -33,24 +38,34 @@ use goto; use if; use implements; -use interface; +use include; +use include_once; use instanceof; use insteadof; +use interface; +use isset; +use list; use namespace; use new; use or; +use print; use private; use protected; use public; +use require; +use require_once; +use return; use static; use switch; use throw; use trait; use try; +use unset; use use; use var; use while; use xor; +use yield; use __CLASS__; use __DIR__; use __FILE__; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsDeclaredUnitTest.php b/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsDeclaredUnitTest.php index 7e3d89bf..59728260 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsDeclaredUnitTest.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsDeclaredUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.8 */ class ForbiddenNamesAsDeclaredUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsInvokedFunctionsUnitTest.php b/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsInvokedFunctionsUnitTest.php index 8b94d893..bd8cf6fd 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsInvokedFunctionsUnitTest.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNamesAsInvokedFunctionsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class ForbiddenNamesAsInvokedFunctionsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.inc b/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.inc index f1914881..141e92d3 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.inc +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.inc @@ -168,3 +168,64 @@ namespace Foo\Bar\Baz {} use function MyFunctionName; use function MyFunctionName as func; use const MyCONSTANT; + +// list +list( $a, $b, $c ) = $array; +foreach ($array as list($key, $value)) {} +foreach ([[1, 2], [2, 3]] as list($a, $b)): + // Do something. +endforeach; + +// die +die(); + +// echo +echo "Hello World!"; + +$str = "Hello World!"; +echo $str; + +// empty +empty($foo); + +// eval +eval( 'echo "Hello World!";' ); + +// exit +exit( 'Exit Message' ); +exit(1); + +// include +include __DIR__ . '/path/to/file.php'; +include( $file ); + +// include_once +include_once __DIR__ . '/path/to/file.php'; +include_once( $file ); + +// interface +interface FoobarInterface {} + +// isset +isset( $foo ); + +// print +print "Hello world!"; + +// require +require __DIR__ . '/path/to/file.php'; +require( $file ); + +// require_once +require_once __DIR__ . '/path/to/file.php'; +require_once( $file ); + +// return +return; +return $foo; + +// unset +unset( $foo ); + +// yield +yield $item; diff --git a/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.php b/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.php index 21a3d78d..85e55524 100644 --- a/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.php +++ b/PHPCompatibility/Tests/Keywords/ForbiddenNamesUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class ForbiddenNamesUnitTest extends BaseSniffTest { @@ -47,7 +48,7 @@ public function testForbiddenNames($usecase) $this->assertNoViolation($file, 2); - $lineCount = count(file($filename)); + $lineCount = \count(file($filename)); // Each line of the use case files (starting at line 3) exhibits an // error. for ($i = 3; $i < $lineCount; $i++) { diff --git a/PHPCompatibility/Tests/Keywords/NewKeywordsUnitTest.php b/PHPCompatibility/Tests/Keywords/NewKeywordsUnitTest.php index f71b11a6..d09adb02 100644 --- a/PHPCompatibility/Tests/Keywords/NewKeywordsUnitTest.php +++ b/PHPCompatibility/Tests/Keywords/NewKeywordsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class NewKeywordsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/LanguageConstructs/NewEmptyNonVariableUnitTest.php b/PHPCompatibility/Tests/LanguageConstructs/NewEmptyNonVariableUnitTest.php index 94662bc4..5fdd09f6 100644 --- a/PHPCompatibility/Tests/LanguageConstructs/NewEmptyNonVariableUnitTest.php +++ b/PHPCompatibility/Tests/LanguageConstructs/NewEmptyNonVariableUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.4 */ class NewEmptyNonVariableUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/LanguageConstructs/NewLanguageConstructsUnitTest.php b/PHPCompatibility/Tests/LanguageConstructs/NewLanguageConstructsUnitTest.php index 25f9f2b5..fbbb1024 100644 --- a/PHPCompatibility/Tests/LanguageConstructs/NewLanguageConstructsUnitTest.php +++ b/PHPCompatibility/Tests/LanguageConstructs/NewLanguageConstructsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.6 */ class NewLanguageConstructsUnitTest extends BaseSniffTest { @@ -46,7 +47,7 @@ public function testNamespaceSeparator() public function testEllipsis() { $file = $this->sniffFile(__FILE__, '5.5'); - $this->assertError($file, 5, 'variadic functions using ... is not present in PHP version 5.5 or earlier'); + $this->assertError($file, 5, 'the ... spread operator is not present in PHP version 5.5 or earlier'); $file = $this->sniffFile(__FILE__, '5.6'); $this->assertNoViolation($file, 5); diff --git a/PHPCompatibility/Tests/Lists/AssignmentOrderUnitTest.php b/PHPCompatibility/Tests/Lists/AssignmentOrderUnitTest.php index 7ae0e280..391c95be 100644 --- a/PHPCompatibility/Tests/Lists/AssignmentOrderUnitTest.php +++ b/PHPCompatibility/Tests/Lists/AssignmentOrderUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class AssignmentOrderUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Lists/ForbiddenEmptyListAssignmentUnitTest.php b/PHPCompatibility/Tests/Lists/ForbiddenEmptyListAssignmentUnitTest.php index ad159cc0..e4bc6ec8 100644 --- a/PHPCompatibility/Tests/Lists/ForbiddenEmptyListAssignmentUnitTest.php +++ b/PHPCompatibility/Tests/Lists/ForbiddenEmptyListAssignmentUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class ForbiddenEmptyListAssignmentUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Lists/NewKeyedListUnitTest.php b/PHPCompatibility/Tests/Lists/NewKeyedListUnitTest.php index 016fb12f..f154f94a 100644 --- a/PHPCompatibility/Tests/Lists/NewKeyedListUnitTest.php +++ b/PHPCompatibility/Tests/Lists/NewKeyedListUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewKeyedListUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Lists/NewListReferenceAssignmentUnitTest.php b/PHPCompatibility/Tests/Lists/NewListReferenceAssignmentUnitTest.php index 7d6d66d5..8e3f92a9 100644 --- a/PHPCompatibility/Tests/Lists/NewListReferenceAssignmentUnitTest.php +++ b/PHPCompatibility/Tests/Lists/NewListReferenceAssignmentUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewListReferenceAssignmentUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Lists/NewShortListUnitTest.php b/PHPCompatibility/Tests/Lists/NewShortListUnitTest.php index 8577455f..524dfad3 100644 --- a/PHPCompatibility/Tests/Lists/NewShortListUnitTest.php +++ b/PHPCompatibility/Tests/Lists/NewShortListUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewShortListUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.inc b/PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.inc new file mode 100644 index 00000000..c41f9080 --- /dev/null +++ b/PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.inc @@ -0,0 +1,50 @@ +__toString(); +$obj::__toString(); +MyClass::__toString( + // Comment. +); + +class MyClass { + public function foo() { + self::__toString(); + static::__toString(); + parent::__toString(); + $this->__toString(); + $another->__toString( /* Comment */ ); + } +} + +// Irrelevant, not the magic method. +echo __toString($param); +echo Some\NameSp\__toString($param); +echo namespace\__toString($param); +MyClass::__TOSTRING; // Constant, not method. +$obj->property; // Property, not method. + +class Bar { + public function foo() { + $this->__toString; // Property. + self::$__toString; // Property. + self::__toString; // Constant. + } +} + +// PHP 5.3: The __toString() magic method can no longer accept arguments. +$obj->__toString($param); +$obj::__toString($param); +MyClass::__toString($param); + +class MyClass { + public function foo() { + // Includes testing case-insensitivity as function names in PHP are (case-insensitive). + self::__toString($param); + static::__tostring($param); + parent::__toSTRING($param); + $this->__toString($param); + $another->__toString($param); + } +} diff --git a/PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.php b/PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.php new file mode 100644 index 00000000..086fe27e --- /dev/null +++ b/PHPCompatibility/Tests/MethodUse/ForbiddenToStringParametersUnitTest.php @@ -0,0 +1,109 @@ +sniffFile(__FILE__, '5.3'); + $this->assertError($file, $line, 'The __toString() magic method will no longer accept passed arguments since PHP 5.3'); + } + + /** + * Data provider. + * + * @see testForbiddenToStringParameters() + * + * @return array + */ + public function dataForbiddenToStringParameters() + { + return array( + array(37), + array(38), + array(39), + array(44), + array(45), + array(46), + array(47), + array(48), + ); + } + + + /** + * testNoFalsePositives. + * + * @dataProvider dataNoFalsePositives + * + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositives($line) + { + $file = $this->sniffFile(__FILE__, '5.3'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + $cases = array(); + // No errors expected on the first 35 lines. + for ($line = 1; $line <= 35; $line++) { + $cases[] = array($line); + } + + return $cases; + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '5.2'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.inc b/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.inc index 87ef0f9b..bf8521ef 100644 --- a/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.inc +++ b/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.inc @@ -15,7 +15,8 @@ class ABC { } /* - * These should be flagged. + * These are class internal calls to __clone(), this is fine. + * See: https://github.com/PHPCompatibility/PHPCompatibility/issues/629#issuecomment-532607809 */ class DEF { function something() @@ -26,5 +27,8 @@ class DEF { } } +/* + * These should be flagged. + */ $a = (new ABC()) -> /* comment */ __clone(); StaticClass::__clone(); diff --git a/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.php b/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.php index 630a9e03..920737ac 100644 --- a/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.php +++ b/PHPCompatibility/Tests/MethodUse/NewDirectCallsToCloneUnitTest.php @@ -3,7 +3,7 @@ * PHPCompatibility, an external standard for PHP_CodeSniffer. * * @package PHPCompatibility - * @copyright 2012-2018 PHPCompatibility Contributors + * @copyright 2012-2019 PHPCompatibility Contributors * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 * @link https://github.com/PHPCompatibility/PHPCompatibility */ @@ -13,7 +13,7 @@ use PHPCompatibility\Tests\BaseSniffTest; /** - * New direct calls to __clone() sniff tests. + * Test the NewDirectCallsToClone sniff. * * @group newDirectCallsToClone * @group methodUse @@ -50,11 +50,8 @@ public function testDirectCallToClone($line) public function dataDirectCallToClone() { return array( - array(23), - array(24), - array(25), - array(29), - array(30), + array(33), + array(34), ); } @@ -83,15 +80,13 @@ public function testNoFalsePositives($line) */ public function dataNoFalsePositives() { - return array( - array(6), - array(7), - array(8), - array(9), - array(10), - array(11), - array(14), - ); + $cases = array(); + // No errors expected on the first 29 lines. + for ($line = 1; $line <= 29; $line++) { + $cases[] = array($line); + } + + return $cases; } diff --git a/PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.1.inc b/PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.1.inc new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/PHPCompatibility/Tests/Miscellaneous/NewPHPOpenTagEOFUnitTest.1.inc @@ -0,0 +1 @@ + + + + + + +
Some HTML
+ + + + +markTestSkipped('Tests can not be run on PHP 5.3 with short_open_tag=On in combination with PHPCS < 2.6.0'); + return; + } + + parent::setUp(); + } + + + /** + * Test detection of stand alone PHP open tag at end of file. + * + * @dataProvider dataNewPHPOpenTagEOF + * + * @param int $fileNumber The number of the test case file. + * @param int $line The line number. + * + * @return void + */ + public function testNewPHPOpenTagEOF($fileNumber, $line) + { + $fileName = __DIR__ . '/' . sprintf(self::TEST_FILE, $fileNumber); + + $file = $this->sniffFile($fileName, '7.3'); + $this->assertError($file, $line, 'A PHP open tag at the end of a file, without trailing newline, was not supported in PHP 7.3 or earlier and would result in a syntax error or be interpreted as a literal string'); + } + + /** + * Data provider. + * + * @see testNewPHPOpenTagEOF() + * + * @return array + */ + public function dataNewPHPOpenTagEOF() + { + return array( + array(4, 1), + array(5, 13), + array(6, 4), + array(7, 6), + ); + } + + + /** + * Verify no false positives are thrown for non-violation open tags in a file + * containing multiple open tags. + * + * @dataProvider dataNoFalsePositivesOnLine + * + * @param int $fileNumber The number of the test case file. + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositivesOnLine($fileNumber, $line) + { + $fileName = __DIR__ . '/' . sprintf(self::TEST_FILE, $fileNumber); + + $file = $this->sniffFile($fileName, '7.3'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositivesOnLine() + * + * @return array + */ + public function dataNoFalsePositivesOnLine() + { + return array( + array(5, 1), + array(5, 8), + array(6, 1), + array(7, 1), + ); + } + + + /** + * Verify no false positives are thrown for valid files. + * + * @dataProvider dataNoFalsePositivesOnFile + * + * @param int $fileNumber The number of the test case file. + * + * @return void + */ + public function testNoFalsePositivesOnFile($fileNumber) + { + $fileName = __DIR__ . '/' . sprintf(self::TEST_FILE, $fileNumber); + + $file = $this->sniffFile($fileName, '7.3'); + $this->assertNoViolation($file); + } + + /** + * Data provider. + * + * @see testNoFalsePositivesOnFile() + * + * @return array + */ + public function dataNoFalsePositivesOnFile() + { + return array( + array(1), + array(2), + array(3), + ); + } + + + /** + * Verify no notices are thrown at all. + * + * @dataProvider dataNoViolationsInFileOnValidVersion + * + * @param int $fileNumber The number of the test case file. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion($fileNumber) + { + $fileName = __DIR__ . '/' . sprintf(self::TEST_FILE, $fileNumber); + + $file = $this->sniffFile($fileName, '7.4'); + $this->assertNoViolation($file); + } + + /** + * Data provider. + * + * @see testNoViolationsInFileOnValidVersion() + * + * @return array + */ + public function dataNoViolationsInFileOnValidVersion() + { + $data = array( + array(1), + array(2), + array(3), + array(5), + array(6), + array(7), + ); + + // In PHPCS 2.x, the `Internal.NoCodeFound` error will come through, even when unit testing. + if (version_compare(PHPCSHelper::getVersion(), '3.0.0', '>')) { + $data[] = array(4); + } + + return $data; + } +} diff --git a/PHPCompatibility/Tests/Miscellaneous/RemovedAlternativePHPTagsUnitTest.php b/PHPCompatibility/Tests/Miscellaneous/RemovedAlternativePHPTagsUnitTest.php index f08d4c25..6a180882 100644 --- a/PHPCompatibility/Tests/Miscellaneous/RemovedAlternativePHPTagsUnitTest.php +++ b/PHPCompatibility/Tests/Miscellaneous/RemovedAlternativePHPTagsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.4 */ class RemovedAlternativePHPTagsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.inc b/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.inc index 8bc66927..0245ce8b 100644 --- a/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.inc +++ b/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.inc @@ -10,3 +10,6 @@ $invalidOctal = 091; $hex = '0xaa78b5'; $hex = 'aa78b5'; // Ok. + +$binaryUpper = 0B10001; +$hexUpper = '0Xbb99EF'; diff --git a/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.php b/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.php index 61654c9a..2b171883 100644 --- a/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.php +++ b/PHPCompatibility/Tests/Miscellaneous/ValidIntegersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class ValidIntegersUnitTest extends BaseSniffTest { @@ -60,6 +61,7 @@ public function dataBinaryInteger() return array( array(3, '0b001001101', true), array(4, '0b01', false), + array(14, '0B10001', true), ); } @@ -129,18 +131,55 @@ public function testValidOctalInteger() /** * testHexNumericString * + * @dataProvider dataHexNumericString + * + * @param int $line Line number where the error should occur. + * @param string $hex Hexidecminal number as a string. + * * @return void */ - public function testHexNumericString() + public function testHexNumericString($line, $hex) + { + $error = "The behaviour of hexadecimal numeric strings was inconsistent prior to PHP 7 and support has been removed in PHP 7. Found: '{$hex}'"; + + $file = $this->sniffFile(__FILE__, '5.6'); + $this->assertWarning($file, $line, $error); + + $file = $this->sniffFile(__FILE__, '7.0'); + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testHexNumericString() + * + * @return array + */ + public function dataHexNumericString() { - $error = 'The behaviour of hexadecimal numeric strings was inconsistent prior to PHP 7 and support has been removed in PHP 7. Found: \'0xaa78b5\''; + // phpcs:disable PHPCompatibility.Miscellaneous.ValidIntegers.HexNumericStringFound + return array( + array(11, '0xaa78b5'), + array(15, '0Xbb99EF'), + ); + // phpcs:enable + } + + /** + * testHexNumericString. + * + * @dataProvider dataHexNumericString + * + * @return void + */ + public function testNoFalsePositivesHexNumericString() + { $file = $this->sniffFile(__FILE__, '5.6'); - $this->assertWarning($file, 11, $error); $this->assertNoViolation($file, 12); $file = $this->sniffFile(__FILE__, '7.0'); - $this->assertError($file, 11, $error); $this->assertNoViolation($file, 12); } diff --git a/PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.inc b/PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.inc new file mode 100644 index 00000000..c5ea20bb --- /dev/null +++ b/PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.inc @@ -0,0 +1,95 @@ + +
+ 'text' . 'text', + 2 => 1 + 2, +); + +$short = [ + 'text' . 'text', + 1 + 2, +]; + +echo 'text'.'text', 10 + 20; + +switch ( $a . 's' ) : + case 10 + 20: + break; + case 's' . 't': + echo 10 + 20; + break; +endswitch; + +$b = $a . ${$b + 10}; + +if () {$a . $b} // Intentional parse error, missing semi-colon. +echo 10 + 10; + +// OK: unary plus/minus. +echo $a . -10; +echo $a . +$b; + +// OK: concat or plus/minus in various brackets. +echo $a[ '_' . $b ] + 20; +$a = array( 'a'.'b' ) + $anotherArray; +return basename('X'.$splited[count($splited) - 1], $suffix); + +// OK: not the same nesting level. +$a = 'Text' . implode( '', array( 'a'.'b' ) + $anotherArray ); + +// Affected by PHP 7.4 changed concat precedence. +echo "sum: " . $a + $b; +$b = 'test' . $a * 10 + 20; +$b = 'test' . ${$a} * 10 + 20; + +/* + * Additional real-world test cases. + * Source concat_results_top2000: https://gist.github.com/nikic/a4df3e8e18c7955c2c21cf6cdb4cbfaa + */ +// (BUG!) /home/nikic/package-analysis/sources/johnpbloch/wordpress-core/wp-admin/includes/class-wp-ajax-upgrader-skin.php:100 +$this->errors->add( 'unknown_upgrade_error_' . $errors_count + 1, $string ); + +// (BUG!) /home/nikic/package-analysis/sources/microsoft/azure-storage/tests/Functional/File/FileServiceFunctionalTest.php:1005 +$this->assertTrue( + count($ret->getFiles()) + count($ret->getDirectories()) <= $options->getMaxResults(), + 'when NextMarker (\'' . $ret->getNextMarker() . + '\')==\'\', Files length (' . + count($ret->getFiles()) + count($ret->getDirectories()) . + ') should be <= MaxResults (' . + $options->getMaxResults() . ')' +); + +// (BUG!) /home/nikic/package-analysis/sources/microsoft/azure-storage/tests/Functional/File/FileServiceFunctionalTest.php:1015 +$this->assertEquals( + $options->getMaxResults(), + count($ret->getFiles()) + count($ret->getDirectories()), + 'when NextMarker (' . $ret->getNextMarker() . + ')!=\'\', Files length (' . + count($ret->getFiles()) + count($ret->getDirectories()) . + ') should be == MaxResults (' . + $options->getMaxResults() .')' +); + +// (BUG!) /home/nikic/package-analysis/sources/sabre/vobject/lib/Recur/RRuleIterator.php:344 +$this->currentDate = $this->currentDate->modify('+'.$this->interval - 1 .' days'); + +// (BUG!) /home/nikic/package-analysis/sources/sabre/vobject/lib/Recur/RRuleIterator.php:404 +$this->currentDate = $this->currentDate->modify('+'.$this->interval - 1 .' weeks'); diff --git a/PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.php b/PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.php new file mode 100644 index 00000000..6a146716 --- /dev/null +++ b/PHPCompatibility/Tests/Operators/ChangedConcatOperatorPrecedenceUnitTest.php @@ -0,0 +1,93 @@ +sniffFile(__FILE__, '7.4'); + $this->assertWarning($file, $line, 'Using an unparenthesized expression containing a "." before a "+" or "-" has been deprecated in PHP 7.4'); + + $file = $this->sniffFile(__FILE__, '8.0'); + $this->assertError($file, $line, 'Using an unparenthesized expression containing a "." before a "+" or "-" has been deprecated in PHP 7.4 and removed in PHP 8.0'); + } + + /** + * Data provider. + * + * @see testChangedConcatOperatorPrecedence() + * + * @return array + */ + public function dataChangedConcatOperatorPrecedence() + { + return array( + array(59), + array(60), + array(61), + array(68), + array(74), + array(85), + array(92), + array(95), + ); + } + + + /** + * Verify the sniff doesn't throw false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '7.4'); + + for ($line = 1; $line < 57; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/Operators/ForbiddenNegativeBitshiftUnitTest.php b/PHPCompatibility/Tests/Operators/ForbiddenNegativeBitshiftUnitTest.php index a53678f8..d1894144 100644 --- a/PHPCompatibility/Tests/Operators/ForbiddenNegativeBitshiftUnitTest.php +++ b/PHPCompatibility/Tests/Operators/ForbiddenNegativeBitshiftUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class ForbiddenNegativeBitshiftUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Operators/NewOperatorsUnitTest.php b/PHPCompatibility/Tests/Operators/NewOperatorsUnitTest.php index 10171777..a5668554 100644 --- a/PHPCompatibility/Tests/Operators/NewOperatorsUnitTest.php +++ b/PHPCompatibility/Tests/Operators/NewOperatorsUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 Detection of new operators was originally included in the + * NewLanguageConstructSniff (since 5.6). */ class NewOperatorsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Operators/NewShortTernaryUnitTest.php b/PHPCompatibility/Tests/Operators/NewShortTernaryUnitTest.php index 463e515d..3d040465 100644 --- a/PHPCompatibility/Tests/Operators/NewShortTernaryUnitTest.php +++ b/PHPCompatibility/Tests/Operators/NewShortTernaryUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewShortTernaryUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.inc b/PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.inc new file mode 100644 index 00000000..e92ec11e --- /dev/null +++ b/PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.inc @@ -0,0 +1,126 @@ + +
+compileRoute($route, $name, !$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex() ?: null, $hasTrailingSlash, false, $conditions); + +// (BUG!) /home/nikic/package-analysis/sources/symfony/symfony/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php:113 +$messageLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageSubscriberInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); + +// (BUG!) /home/nikic/package-analysis/sources/symfony/symfony/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php:126 +$messageLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageSubscriberInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); + +// (possibly okay) /home/nikic/package-analysis/sources/ekino/newrelic-bundle/NewRelic/Config.php:32 +$this->name = !empty($name) ? $name : \ini_get('newrelic.appname') ?: ''; + +// (possibly okay) /home/nikic/package-analysis/sources/ekino/newrelic-bundle/NewRelic/Config.php:34 +$this->licenseKey = !empty($licenseKey) ? $licenseKey : \ini_get('newrelic.license') ?: ''; + +// (BUG!) /home/nikic/package-analysis/sources/kartik-v/yii2-grid/src/ColumnTrait.php:274 +$curr = is_array($this->format) && isset($this->format[1]) ? $this->format[1] : + isset($formatter->currencyCode) ? $formatter->currencyCode . ' ' : ''; + +// (BUG!) /home/nikic/package-analysis/sources/zendframework/zendframework1/library/Zend/Service/Console/Command.php:223 +$handlerDescription = isset($handlerDescriptions[$hi]) ? $handlerDescriptions[$hi] : isset($handlerDescriptions[0]) ? $handlerDescriptions[0] : ''; + +// (BUG!) /home/nikic/package-analysis/sources/hoa/compiler/Bin/Pp.php:232 + printf( + $format, + $i, + $token['namespace'], + $token['token'], + 30 < $token['length'] + ? mb_substr($token['value'], 0, 29) . '…' + : 'EOF' === $token['token'] + ? str_repeat(' ', 30) + : $token['value'] . + str_repeat(' ', 30 - $token['length']), + $token['offset'] + ); + +// (BUG!) /home/nikic/package-analysis/sources/jms/serializer/src/SerializationContext.php:152 +return $this->initialType + ? $this->initialType + : $this->hasAttribute('initial_type') ? $this->getAttribute('initial_type') : null; + +// (BUG!) /home/nikic/package-analysis/sources/psy/psysh/src/Formatter/SignatureFormatter.php:290 +$value = \is_array($value) ? 'array()' : \is_null($value) ? 'null' : \var_export($value, true); + +// (BUG!) /home/nikic/package-analysis/sources/nesbot/carbon/src/Carbon/Lang/ga.php:69 +return $number.($number === 1 ? 'd' : $number % 10 === 2 ? 'na' : 'mh'); + +// (BUG!) /home/nikic/package-analysis/sources/respect/validation/library/Rules/Cnh.php:59 +$check = $dv2 < 0 ? $dv2 + 11 : $dv2 > 9 ? 0 : $dv2; diff --git a/PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.php b/PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.php new file mode 100644 index 00000000..f0c82b35 --- /dev/null +++ b/PHPCompatibility/Tests/Operators/RemovedTernaryAssociativityUnitTest.php @@ -0,0 +1,134 @@ +sniffFile(__FILE__, '7.4'); + $this->assertWarning($file, $line, 'The left-associativity of the ternary operator has been deprecated in PHP 7.4. Multiple consecutive ternaries detected. Use parenthesis to clarify the order in which the operations should be executed'); + + $file = $this->sniffFile(__FILE__, '8.0'); + $this->assertError($file, $line, 'The left-associativity of the ternary operator has been deprecated in PHP 7.4 and removed in PHP 8.0. Multiple consecutive ternaries detected. Use parenthesis to clarify the order in which the operations should be executed'); + } + + /** + * Data provider. + * + * @see testRemovedTernaryAssociativity() + * + * @return array + */ + public function dataRemovedTernaryAssociativity() + { + $cases = array(); + foreach ($this->problemLines as $line) { + $cases[] = array($line); + } + + return $cases; + } + + + /** + * Verify the sniff doesn't throw false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '7.4'); + $exclude = array_flip($this->problemLines); + + for ($line = 1; $line <= $this->totalLines; $line++) { + if (isset($exclude[$line])) { + continue; + } + + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/ForbiddenGetClassNullUnitTest.php b/PHPCompatibility/Tests/ParameterValues/ForbiddenGetClassNullUnitTest.php index bce6b223..d2cdb7b1 100644 --- a/PHPCompatibility/Tests/ParameterValues/ForbiddenGetClassNullUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/ForbiddenGetClassNullUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class ForbiddenGetClassNullUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.inc new file mode 100644 index 00000000..bf1eeb3f --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.inc @@ -0,0 +1,15 @@ +'); +$str = strip_tags($input, '
'); + +// Undetermined. Ignore. +$str = strip_tags($str, $allowable_tags); +$str = strip_tags($str, self::ALLOWABLE_TAGS); +$str = strip_tags($str, MyClass::get_allowable_tags('
')); + +// Not OK - warning. +$str = strip_tags($input, '
'); +$str = strip_tags($input, '
' . ''); diff --git a/PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.php b/PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.php new file mode 100644 index 00000000..8dc72791 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/ForbiddenStripTagsSelfClosingXHTMLUnitTest.php @@ -0,0 +1,88 @@ +sniffFile(__FILE__, '5.4'); + $error = 'Self-closing XHTML tags are ignored. Only non-self-closing tags should be used in the strip_tags() $allowable_tags parameter since PHP 5.3.4. Found: ' . $paramValue; + + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testForbiddenStripTagsSelfClosingXHTML() + * + * @return array + */ + public function dataForbiddenStripTagsSelfClosingXHTML() + { + return array( + array(14, "'
'"), + array(15, "'
' . ''"), + ); + } + + + /** + * Test the sniff does not throw false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '5.4'); + + // No errors expected on the first 12 lines. + for ($line = 1; $line <= 12; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '5.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/NewArrayReduceInitialTypeUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewArrayReduceInitialTypeUnitTest.php index 86f24574..7c29d58f 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewArrayReduceInitialTypeUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/NewArrayReduceInitialTypeUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewArrayReduceInitialTypeUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/NewFopenModesUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewFopenModesUnitTest.php index 5a6d68cb..141ca142 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewFopenModesUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/NewFopenModesUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewFopenModesUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/NewHTMLEntitiesEncodingDefaultUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewHTMLEntitiesEncodingDefaultUnitTest.inc new file mode 100644 index 00000000..b4ac0612 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewHTMLEntitiesEncodingDefaultUnitTest.inc @@ -0,0 +1,11 @@ +sniffFile(__FILE__, '5.3-5.4'); + $error = "The default value of the \$encoding parameter for {$function}() was changed from ISO-8859-1 to UTF-8 in PHP 5.4"; + + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testNewHTMLEntitiesEncodingDefault() + * + * @return array + */ + public function dataNewHTMLEntitiesEncodingDefault() + { + return array( + array(9, 'htmlentities'), + array(10, 'htmlspecialchars'), + array(11, 'HTML_entity_decode'), + ); + } + + + /** + * Test that there are no false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '5.3-5.4'); + + // No errors expected on the first 7 lines. + for ($line = 1; $line <= 7; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @dataProvider dataNoViolationsInFileOnValidVersion + * + * @param string $testVersion The testVersion to use. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion($testVersion) + { + $file = $this->sniffFile(__FILE__, $testVersion); + $this->assertNoViolation($file); + } + + /** + * Data provider. + * + * @see testNoViolationsInFileOnValidVersion() + * + * @return array + */ + public function dataNoViolationsInFileOnValidVersion() + { + return array( + array('5.0-5.3'), + array('5.4-'), + ); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.inc index b47cf4b0..c9185015 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.inc +++ b/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.inc @@ -31,3 +31,4 @@ hash_hmac('sha3-224'); hash_init('sha3-256'); hash_pbkdf2('sha3-384'); hash('sha3-512'); +hash('crc32c'); diff --git a/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.php index bf95e6ac..24e996d9 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/NewHashAlgorithmsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.7 */ class NewHashAlgorithmsUnitTest extends BaseSniffTest { @@ -74,6 +75,7 @@ public function dataNewHashAlgorithms() array('sha3-256', '7.0', 31, '7.1'), array('sha3-384', '7.0', 32, '7.1'), array('sha3-512', '7.0', 33, '7.1'), + array('crc32c', '7.3', 34, '7.4'), ); } diff --git a/PHPCompatibility/Tests/ParameterValues/NewIDNVariantDefaultUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewIDNVariantDefaultUnitTest.inc new file mode 100644 index 00000000..d1cb96d1 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewIDNVariantDefaultUnitTest.inc @@ -0,0 +1,13 @@ +sniffFile(__FILE__, '7.3-'); + $error = 'The default value of the ' . $function . '() $variant parameter has changed from INTL_IDNA_VARIANT_2003 to INTL_IDNA_VARIANT_UTS46 in PHP 7.4.'; + + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testNewIDNVariantDefault() + * + * @return array + */ + public function dataNewIDNVariantDefault() + { + return array( + array(10, 'idn_to_ascii'), + array(11, 'idn_to_ascii'), + array(12, 'IDN_to_utf8'), + array(13, 'idn_to_utf8'), + ); + } + + + /** + * testNoFalsePositives + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '7.3-'); + + // No errors expected on the first 8 lines. + for ($line = 1; $line <= 8; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @dataProvider dataNoViolationsInFileOnValidVersion + * + * @param string $testVersion The testVersion to use. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion($testVersion) + { + $file = $this->sniffFile(__FILE__, $testVersion); + $this->assertNoViolation($file); + } + + /** + * Data provider. + * + * @see testNoViolationsInFileOnValidVersion() + * + * @return array + */ + public function dataNoViolationsInFileOnValidVersion() + { + return array( + array('7.1-7.3'), + array('7.4-'), + ); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.inc new file mode 100644 index 00000000..0bc7b0ae --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.inc @@ -0,0 +1,110 @@ + 'Q', + 'input-charset' => 'ISO-8859-1', + 'output-charset' => 'UTF-8', + 'line-length' => 80, + 'line-break-chars' => "\n", + ) +); // OK. Parameter set and the expected array keys set as well. + +$a = Iconv_Mime_Encode( $field_name, $field_value ); // Error: $preferences not set. +$a = ICONV_mime_encode( $field_name, $field_value, $preferences ); // Warning: unclear whether array keys are set or not. +$a = iconv_mime_encode( + $field_name, + $field_value, + array( + 'scheme' => 'Q', + 'input-charset' => 'ISO-8859-1', + 'line-break-chars' => "\n", + ) +); // Error: 'output-charset' not set. + +$a = iconv_mime_encode( + $field_name, + $field_value, + [ + 'output-charset' => 'UTF-8', + 'line-length' => 80, + ] +); // Error: 'input-charset' not set. diff --git a/PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.php new file mode 100644 index 00000000..556a05a5 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewIconvMbstringCharsetDefaultUnitTest.php @@ -0,0 +1,171 @@ +sniffFile(__FILE__, '5.4-7.0'); + $error = "The default value of the {$paramName} parameter for {$function}() was changed from ISO-8859-1 to UTF-8 in PHP 5.6"; + + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testNewIconvMbstringCharsetDefault() + * + * @return array + */ + public function dataNewIconvMbstringCharsetDefault() + { + return array( + array(44, 'iconv_mime_decode_headers', '$charset'), + array(45, 'iconv_mime_decode', '$charset'), + array(46, 'Iconv_Strlen', '$charset'), + array(47, 'iconv_strpos', '$charset'), + array(48, 'iconv_strrpos', '$charset'), + array(49, 'iconv_substr', '$charset'), + + array(51, 'mb_check_encoding'), + array(52, 'MB_chr'), + array(53, 'mb_convert_case'), + array(54, 'mb_convert_encoding', '$from_encoding'), + array(55, 'mb_convert_kana'), + array(56, 'mb_decode_numericentity'), + array(57, 'mb_encode_numericentity'), + array(58, 'mb_ord'), + array(59, 'mb_scrub'), + array(60, 'mb_strcut'), + array(61, 'mb_stripos'), + array(62, 'mb_stristr'), + array(63, 'mb_strlen'), + array(64, 'mb_strpos'), + array(65, 'mb_strrchr'), + array(66, 'mb_strrichr'), + array(67, 'mb_strripos'), + array(68, 'mb_strrpos'), + array(69, 'mb_strstr'), + array(70, 'mb_strtolower'), + array(71, 'mb_strtoupper'), + array(72, 'mb_strwidth'), + array(73, 'mb_substr_count'), + array(74, 'mb_substr'), + ); + } + + + /** + * Test that there are no false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '5.4-7.0'); + + // No errors expected on the first 40 lines. + for ($line = 1; $line <= 40; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Test the special handling of calls to iconv_mime_encode(). + * + * @return void + */ + public function testIconvMimeEncode() + { + $file = $this->sniffFile(__FILE__, '5.4-7.0'); + $error = 'The default value of the %s parameter index for iconv_mime_encode() was changed from ISO-8859-1 to UTF-8 in PHP 5.6'; + + $this->assertError($file, 91, sprintf($error, '$preferences[\'input/output-charset\']')); + $this->assertWarning($file, 92, sprintf($error, '$preferences[\'input/output-charset\']')); + $this->assertError($file, 96, sprintf($error, '$preferences[\'output-charset\']')); + $this->assertError($file, 106, sprintf($error, '$preferences[\'input-charset\']')); + } + + + /** + * Test that there are no false positives. + * + * @return void + */ + public function testNoFalsePositivesIconvMimeEncode() + { + $file = $this->sniffFile(__FILE__, '5.4-7.0'); + + // No errors expected on line 79 - 89. + for ($line = 79; $line <= 89; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @dataProvider dataNoViolationsInFileOnValidVersion + * + * @param string $testVersion The testVersion to use. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion($testVersion) + { + $file = $this->sniffFile(__FILE__, $testVersion); + $this->assertNoViolation($file); + } + + /** + * Data provider. + * + * @see testNoViolationsInFileOnValidVersion() + * + * @return array + */ + public function dataNoViolationsInFileOnValidVersion() + { + return array( + array('-5.5'), + array('5.6-'), + ); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.inc index 7e038898..7cd0a05e 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.inc +++ b/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.inc @@ -31,7 +31,7 @@ mb_ereg_search_setpos( // phpcs:ignore Standard.Category.Sniff -- for reasons. ); -mb_ereg_search_setpos( - 100); +MB_ereg_search_setpos( - 100); $a = file_get_contents($filename, true, $context, -1024, 1024 ); $a = grapheme_extract($haystack, $size, $extract_type, -10 ); @@ -44,5 +44,5 @@ $a = mb_strpos($haystack, $needle, -5 ); $a = stripos($haystack, $needle, -42 ); $a = strpos($haystack, $needle, -30 ); $a = substr_count($haystack, $needle, -20, -10 ); -$a = substr_count($haystack, $needle, -20, 10 ); +$a = Substr_Count($haystack, $needle, -20, 10 ); $a = substr_count($haystack, $needle, 20, -+-+-10 ); diff --git a/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.php index 5dda4d12..d083b668 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/NewNegativeStringOffsetUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewNegativeStringOffsetUnitTest extends BaseSniffTest { @@ -57,7 +58,7 @@ public function dataNegativeStringOffset() { return array( array(28, 'position', 'mb_ereg_search_setpos'), - array(34, 'position', 'mb_ereg_search_setpos'), + array(34, 'position', 'MB_ereg_search_setpos'), array(36, 'offset', 'file_get_contents'), array(37, 'start', 'grapheme_extract'), array(38, 'offset', 'grapheme_stripos'), @@ -71,7 +72,7 @@ public function dataNegativeStringOffset() array(45, 'offset', 'strpos'), array(46, 'offset', 'substr_count'), array(46, 'length', 'substr_count'), - array(47, 'offset', 'substr_count'), + array(47, 'offset', 'Substr_Count'), array(48, 'length', 'substr_count'), ); } diff --git a/PHPCompatibility/Tests/ParameterValues/NewPCREModifiersUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewPCREModifiersUnitTest.php index dc10f87d..0b91e750 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewPCREModifiersUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/NewPCREModifiersUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class NewPCREModifiersUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/NewPackFormatUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewPackFormatUnitTest.php index 69433220..c6c75bbe 100644 --- a/PHPCompatibility/Tests/ParameterValues/NewPackFormatUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/NewPackFormatUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewPackFormatUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.inc new file mode 100644 index 00000000..47fe78e0 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.inc @@ -0,0 +1,26 @@ +get_algo(), $options ); +$hash = password_hash( $password, static::ALGO, $options ); + +// Not OK - error. +$hash = PassWord_hash( $password, null, $options ); +$hash = password_hash( $password, +1, $options ); +$hash = password_needs_rehash( $password, 2, $options ); +$hash = password_hash( $password, 3, $options ); +$hash = password_hash( $password, '2y', $options ); +$hash = password_HASH( $password, "argon{$type}", $options ); +$hash = password_needs_rehash( $password, 'argon2id', $options ); diff --git a/PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.php new file mode 100644 index 00000000..30eb9c86 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewPasswordAlgoConstantValuesUnitTest.php @@ -0,0 +1,92 @@ +sniffFile(__FILE__, '7.4'); + $error = 'The value of the password hash algorithm constants has changed in PHP 7.4'; + + $this->assertWarning($file, $line, $error); + } + + /** + * Data provider. + * + * @see testNewPasswordAlgoConstantValues() + * + * @return array + */ + public function dataNewPasswordAlgoConstantValues() + { + return array( + array(20), + array(21), + array(22), + array(23), + array(24), + array(25), + array(26), + ); + } + + + /** + * Test that there are no false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '7.4'); + + // No errors expected on the first 18 lines. + for ($line = 1; $line <= 18; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/NewProcOpenCmdArrayUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewProcOpenCmdArrayUnitTest.inc new file mode 100644 index 00000000..bacba908 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewProcOpenCmdArrayUnitTest.inc @@ -0,0 +1,39 @@ +sniffFile(__FILE__, '7.3'); + $error = 'The proc_open() function did not accept $cmd to be passed in array format in PHP 7.3 and earlier.'; + + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testNewProcOpenCmdArray() + * + * @return array + */ + public function dataNewProcOpenCmdArray() + { + return array( + array(18), + array(20), + array(30), + array(32), + ); + } + + + /** + * testInvalidProcOpenCmdArray + * + * @dataProvider dataInvalidProcOpenCmdArray + * + * @param int $line Line number where the error should occur. + * @param bool $itemValue The parameter value detected. + * + * @return void + */ + public function testInvalidProcOpenCmdArray($line, $itemValue) + { + $file = $this->sniffFile(__FILE__, '7.4'); + $error = 'When passing proc_open() the $cmd parameter as an array, PHP will take care of any necessary argument escaping. Found: ' . $itemValue; + + $this->assertWarning($file, $line, $error); + } + + /** + * Data provider. + * + * @see testInvalidProcOpenCmdArray() + * + * @return array + */ + public function dataInvalidProcOpenCmdArray() + { + return array( + array(30, 'escapeshellarg($echo)'), + array(34, '\'--standard=\' . escapeshellarg($standard)'), + array(35, '\'./path/to/\' . escapeshellarg($file)'), + ); + } + + + /** + * Test the sniff does not throw false positives. + * + * @dataProvider dataNoFalsePositives + * + * @param string $testVersion The testVersion to use. + * + * @return void + */ + public function testNoFalsePositives($testVersion) + { + $file = $this->sniffFile(__FILE__, $testVersion); + + // No errors expected on the first 16 lines. + for ($line = 1; $line <= 16; $line++) { + $this->assertNoViolation($file, $line); + } + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + return array( + array('7.3'), + array('7.4'), + ); + } + + + /* + * `testNoViolationsInFileOnValidVersion` test omitted as this sniff will throw warnings/errors + * about independently of the testVersion. + */ +} diff --git a/PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.inc new file mode 100644 index 00000000..8d2f8ad0 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.inc @@ -0,0 +1,34 @@ +

'); + +// Undetermined. Ignore. +$str = strip_tags($str, $allowable_tags); +$str = strip_tags($str, self::ALLOWABLE_TAGS); +$str = strip_tags($str, MyClass::get_allowable_tags(['a', 'p'])); + +// PHP 7.4: passing $allowable_tags as an array. +$str = strip_tags($input, ['a', 'p']); +$str = strip_tags( + $input, + array( + 'a', + 'p' + ) +); + +// PHP 7.4: Warning. Incorrectly passing $allowable_tags as an array. +$str = strip_tags($input, ['', '

']); +$str = strip_tags( + $input, + array( + '', + '

' + ), +); + +// Prevent false positive. +$str = strip_tags($input, ['br', ...function_call(',

')]); // PHP 7.4 syntax: unpacking within an array. +$str = strip_tags($input, []); diff --git a/PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.php b/PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.php new file mode 100644 index 00000000..16d45f21 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/NewStripTagsAllowableTagsArrayUnitTest.php @@ -0,0 +1,169 @@ +sniffFile(__FILE__, '7.3'); + $error = 'The strip_tags() function did not accept $allowable_tags to be passed in array format in PHP 7.3 and earlier.'; + + $this->assertError($file, $line, $error); + } + + /** + * Data provider. + * + * @see testNewStripTagsAllowableTagsArray() + * + * @return array + */ + public function dataNewStripTagsAllowableTagsArray() + { + return array( + array(13), + array(16), + array(23), + array(26), + array(33), + array(34), + ); + } + + + /** + * testInvalidStripTagsAllowableTagsArray + * + * @dataProvider dataInvalidStripTagsAllowableTagsArray + * + * @param int $line Line number where the error should occur. + * @param bool $paramValue The parameter value detected. + * + * @return void + */ + public function testInvalidStripTagsAllowableTagsArray($line, $paramValue) + { + $file = $this->sniffFile(__FILE__, '7.4'); + $error = 'When passing strip_tags() the $allowable_tags parameter as an array, the tags should not be enclosed in <> brackets. Found: ' . $paramValue; + + $this->assertWarning($file, $line, $error); + } + + /** + * Data provider. + * + * @see testInvalidStripTagsAllowableTagsArray() + * + * @return array + */ + public function dataInvalidStripTagsAllowableTagsArray() + { + return array( + array(23, "''"), + array(23, "'

'"), + array(27, "''"), + array(28, "'

'"), + ); + } + + /** + * testNoFalsePositivesInvalidStripTagsAllowableTagsArray + * + * @dataProvider dataNoFalsePositivesInvalidStripTagsAllowableTagsArray + * + * @param int $line The line number. + * + * @return void + */ + public function testNoFalsePositivesInvalidStripTagsAllowableTagsArray($line) + { + $file = $this->sniffFile(__FILE__, '7.4'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositivesInvalidStripTagsAllowableTagsArray() + * + * @return array + */ + public function dataNoFalsePositivesInvalidStripTagsAllowableTagsArray() + { + return array( + array(33), + array(34), + ); + } + + /** + * Test the sniff does not throw false positives. + * + * @dataProvider dataNoFalsePositives + * + * @param string $testVersion The testVersion to use. + * + * @return void + */ + public function testNoFalsePositives($testVersion) + { + $file = $this->sniffFile(__FILE__, $testVersion); + + // No errors expected on the first 11 lines. + for ($line = 1; $line <= 11; $line++) { + $this->assertNoViolation($file, $line); + } + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + return array( + array('7.3'), + array('7.4'), + ); + } + + + /* + * `testNoViolationsInFileOnValidVersion` test omitted as this sniff will throw warnings/errors + * about independently of the testVersion. + */ +} diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedHashAlgorithmsUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedHashAlgorithmsUnitTest.php index 92fd96d8..707b2bbe 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedHashAlgorithmsUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/RemovedHashAlgorithmsUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class RemovedHashAlgorithmsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedIconvEncodingUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedIconvEncodingUnitTest.php index 7b63e454..5058777e 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedIconvEncodingUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/RemovedIconvEncodingUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class RemovedIconvEncodingUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.inc new file mode 100644 index 00000000..cd03da61 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.inc @@ -0,0 +1,68 @@ +get_glue() ); +implode( $pieces, MY_GLUE ); +implode( Custom\array_map( 'strtolower', $pieces ), $glue ); +implode( $obj->array_map( 'strtolower', $pieces ), $glue ); +implode( My_Class::array_map( 'strtolower', $pieces ), $glue ); +implode( array_search( $glue_needle, $haystack ), $pieces ); + +// Not OK. +implode( $pieces, ', ' ); +join($pieces, ' '); + +implode(array('a', 'b'), $glue); +join( [1, 2] /*comment*/, $glue); + +implode(array('a', 'b'), '-' . '|'); +join([1, 2], "$glue"); +implode($pieces, ('-' . '|')); +implode( $pieces, ( $type === 'a' ? '-' : '|') ); + +join ($pieces, << implode( + ' ', + array_filter( [ 'a' => $type === 'a' ? '-' : '|', ] ) + ), +]; + +// Efficiency, second param doesn't even need to be examined here. +implode( ( $type === 'a' ? '-' : '|' ), (array) $pieces ); + +// Recognize array casts. +implode( $glue, (array) $object ); // OK. +implode( (array) $object, $glue ); // Error. diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.php new file mode 100644 index 00000000..402b1b78 --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/RemovedImplodeFlexibleParamOrderUnitTest.php @@ -0,0 +1,123 @@ +sniffFile(__FILE__, '7.4'); + $this->assertWarning($file, $line, 'Passing the $glue and $pieces parameters in reverse order to ' . $function . ' has been deprecated since PHP 7.4; $glue should be the first parameter and $pieces the second'); + } + + /** + * dataRemovedImplodeFlexibleParamOrder + * + * @see testRemovedImplodeFlexibleParamOrder() + * + * @return array + */ + public function dataRemovedImplodeFlexibleParamOrder() + { + return array( + array(29, 'implode'), + array(30, 'join'), + array(32, 'implode'), + array(33, 'join'), + array(35, 'implode'), + array(36, 'join'), + array(37, 'implode'), + array(38, 'implode'), + array(40, 'join'), + array(46, 'implode'), + array(47, 'implode'), + array(48, 'implode'), + array(49, 'implode'), + array(52, 'implode'), + array(53, 'implode'), + array(68, 'implode'), + ); + } + + + /** + * testNoFalsePositives + * + * @dataProvider dataNoFalsePositives + * + * @param int $line Line number. + * + * @return void + */ + public function testNoFalsePositives($line) + { + $file = $this->sniffFile(__FILE__, '7.4'); + $this->assertNoViolation($file, $line); + } + + /** + * Data provider. + * + * @see testNoFalsePositives() + * + * @return array + */ + public function dataNoFalsePositives() + { + $data = array(); + + // No errors expected on the first 27 lines. + for ($line = 1; $line <= 27; $line++) { + $data[] = array($line); + } + + $data[] = array(57); + $data[] = array(64); + $data[] = array(67); + + return $data; + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedMbStrrposEncodingThirdParamUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/RemovedMbStrrposEncodingThirdParamUnitTest.inc new file mode 100644 index 00000000..dad62ccd --- /dev/null +++ b/PHPCompatibility/Tests/ParameterValues/RemovedMbStrrposEncodingThirdParamUnitTest.inc @@ -0,0 +1,24 @@ +sniffFile(__FILE__, '5.2'); + $error = 'Passing the encoding to mb_strrpos() as third parameter is soft deprecated since PHP 5.2'; + $this->assertWarning($file, $line, $error); + + $file = $this->sniffFile(__FILE__, '7.4'); + $error .= ' and hard deprecated since PHP 7.4.'; + $this->assertWarning($file, $line, $error); + } + + /** + * Data provider. + * + * @see testRemovedMbStrrposEncodingThirdParam() + * + * @return array + */ + public function dataRemovedMbStrrposEncodingThirdParam() + { + return array( + array(22), + array(23), + array(24), + ); + } + + + /** + * testNoFalsePositives + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '5.2'); + + // No errors expected on the first 20 lines. + for ($line = 1; $line <= 20; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '5.1'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.inc b/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.inc index feb5a72f..428ea6b6 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.inc +++ b/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.inc @@ -12,7 +12,7 @@ mb_regex_set_options( 'ims' ); // These should all be flagged. mb_ereg_replace( $pattern, $replace, $subject, 'e' ); -mb_eregi_replace( $pattern, $replace, $subject, "seim" ); +MB_eregi_replace( $pattern, $replace, $subject, "seim" ); mb_regex_set_options( 'im' . 'se' ); // Interpolated strings: These should NOT be flagged. @@ -23,7 +23,7 @@ mb_regex_set_options( 'im' . "$se" ); // Interpolated strings: These should all be flagged. mb_ereg_replace( $pattern, $replace, $subject, "e$m" ); mb_eregi_replace( $pattern, $replace, $subject, "me$i" ); -mb_regex_set_options( 'im' . "{$se}e" ); +Mb_Regex_Set_Options( 'im' . "{$se}e" ); // Verify the sniff also pick up on the function aliases. mbereg_replace( $pattern, $replace, $subject, 'e' ); diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.php index d54d584c..b2ab803a 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/RemovedMbstringModifiersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.5 */ class RemovedMbstringModifiersUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedNonCryptoHashUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedNonCryptoHashUnitTest.php index 09095aba..c51d5b09 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedNonCryptoHashUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/RemovedNonCryptoHashUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class RemovedNonCryptoHashUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedPCREModifiersUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedPCREModifiersUnitTest.php index d995c43e..7e20dd8f 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedPCREModifiersUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/RemovedPCREModifiersUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.6 */ class RemovedPCREModifiersUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/ParameterValues/RemovedSetlocaleStringUnitTest.php b/PHPCompatibility/Tests/ParameterValues/RemovedSetlocaleStringUnitTest.php index f3a7a28e..80c6c4c7 100644 --- a/PHPCompatibility/Tests/ParameterValues/RemovedSetlocaleStringUnitTest.php +++ b/PHPCompatibility/Tests/ParameterValues/RemovedSetlocaleStringUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class RemovedSetlocaleStringUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Syntax/ForbiddenCallTimePassByReferenceUnitTest.php b/PHPCompatibility/Tests/Syntax/ForbiddenCallTimePassByReferenceUnitTest.php index 43241145..7f513bf4 100644 --- a/PHPCompatibility/Tests/Syntax/ForbiddenCallTimePassByReferenceUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/ForbiddenCallTimePassByReferenceUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class ForbiddenCallTimePassByReferenceUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Syntax/NewArrayStringDereferencingUnitTest.inc b/PHPCompatibility/Tests/Syntax/NewArrayStringDereferencingUnitTest.inc index 34956a94..e3da06d1 100644 --- a/PHPCompatibility/Tests/Syntax/NewArrayStringDereferencingUnitTest.inc +++ b/PHPCompatibility/Tests/Syntax/NewArrayStringDereferencingUnitTest.inc @@ -1,9 +1,9 @@ + * @since 7.1.4 */ class NewArrayStringDereferencingUnitTest extends BaseSniffTest { @@ -29,15 +30,22 @@ class NewArrayStringDereferencingUnitTest extends BaseSniffTest * * @dataProvider dataArrayStringDereferencing * - * @param int $line The line number. - * @param string $type Whether this is an array or string dereferencing. + * @param int $line The line number. + * @param string $type Whether this is an array or string dereferencing. + * @param bool $skipNoViolation Optional. Whether or not to test for no violation. + * Defaults to false. * * @return void */ - public function testArrayStringDereferencing($line, $type) + public function testArrayStringDereferencing($line, $type, $skipNoViolation = false) { $file = $this->sniffFile(__FILE__, '5.4'); $this->assertError($file, $line, "Direct array dereferencing of {$type} is not present in PHP version 5.4 or earlier"); + + if ($skipNoViolation === false) { + $file = $this->sniffFile(__FILE__, '5.5'); + $this->assertNoViolation($file, $line); + } } /** @@ -52,9 +60,48 @@ public function dataArrayStringDereferencing() return array( array(4, 'arrays'), array(5, 'arrays'), - array(6, 'arrays'), + array(6, 'arrays'), // Error x 2. array(7, 'string literals'), array(8, 'string literals'), + array(27, 'arrays', true), + array(28, 'arrays', true), + ); + } + + + /** + * testArrayStringDereferencingUsingCurlies + * + * @dataProvider dataArrayStringDereferencingUsingCurlies + * + * @param int $line The line number. + * @param string $type Whether this is an array or string dereferencing. + * + * @return void + */ + public function testArrayStringDereferencingUsingCurlies($line, $type) + { + $file = $this->sniffFile(__FILE__, '5.6'); + $this->assertError($file, $line, "Direct array dereferencing of {$type} using curly braces is not present in PHP version 5.6 or earlier"); + } + + /** + * Data provider. + * + * @see testArrayStringDereferencingUsingCurlies() + * + * @return array + */ + public function dataArrayStringDereferencingUsingCurlies() + { + return array( + array(20, 'arrays'), + array(21, 'arrays'), + array(22, 'arrays'), // Error x 2. + array(23, 'string literals'), + array(24, 'string literals'), + array(27, 'arrays'), + array(28, 'arrays'), ); } @@ -101,7 +148,7 @@ public function dataNoFalsePositives() */ public function testNoViolationsInFileOnValidVersion() { - $file = $this->sniffFile(__FILE__, '5.5'); + $file = $this->sniffFile(__FILE__, '7.0'); $this->assertNoViolation($file); } } diff --git a/PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.inc b/PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.inc new file mode 100644 index 00000000..dfda501b --- /dev/null +++ b/PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.inc @@ -0,0 +1,43 @@ + 'banana', 'b' => 'orange', ...$parts, 'watermelon']; + +$arr1 = [1, 2, 3]; +$arr2 = [...$arr1]; +$arr3 = array(0, ...$arr1); +$arr4 = array( + ...$arr1, + ...$arr2, + 111, +); +$arr5 = [ + ...$arr1, + [...$arr1] + getArr(1, 2, ...$passedToFunctionNotArray), +]; +$arr5 = array( + ...$arr1, + array(...$arr1), + getArr(1, 2, ...$passedToFunctionNotArray), +); + +function getArr() { return ['a', 'b']; } +$arr6 = array(...getArr(), 'c'); +$arr7 = [...new ArrayIterator(['a', 'b', 'c'])]; +$arr8 = [...arrGen()]; + +// Unpacking array by reference is not supported in PHP 7.4, but not our concern, throw an error anyway. +$arr2 = [...&$arr1]; + +// Intentional parse error. This has to be the last test in the file. +$arr5 = array( + ...$arr1, + array(...$arr1 diff --git a/PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.php b/PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.php new file mode 100644 index 00000000..4cbcb5e3 --- /dev/null +++ b/PHPCompatibility/Tests/Syntax/NewArrayUnpackingUnitTest.php @@ -0,0 +1,97 @@ +sniffFile(__FILE__, '7.3'); + $this->assertError($file, $line, 'Array unpacking within array declarations using the spread operator is not supported in PHP 7.3 or earlier'); + } + + /** + * Data provider. + * + * @see testNewArrayUnpacking() + * + * @return array + */ + public function dataNewArrayUnpacking() + { + return array( + array(11), + array(14), + array(15), + array(17), + array(18), + array(22), + array(23), + array(27), + array(28), + array(33), + array(34), + array(35), + array(38), + array(42), + array(43), + ); + } + + + /** + * Verify the sniff doesn't throw false positives. + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '7.3'); + + for ($line = 1; $line < 7; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.4'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.inc b/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.inc index 6473abe7..75c2ec31 100644 --- a/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.inc +++ b/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.inc @@ -42,7 +42,7 @@ try { (new $foo())->bar; (new foo)[0]; -$a = (new Foo( array(1, array(4, 5), 3) ))[1][0]; +$a = (new Foo( array(1, array(4, 5), 3) ))[1][0]; // Should give two errors. var_dump((new Bar)->y); $closure = function() {return (new $x)->y;}; @@ -103,3 +103,16 @@ $b = (clone $foo)->bar(); echo (clone $iterable)[20]; $date1 = (clone ($_date1 <= $_date2 ? $_date1 : $_date2))->format('Y'); + +/* + * PHP 7.0: class member access on instantiation using curly braces. + * This "silently" started working in PHP 7.0. See: https://3v4l.org/KsEgH + */ +(new foo){0}; +$a = (new Foo( array(1, array(4, 5), 3) )){1}{0}; // Should give two errors. + +echo (clone $iterable){20}; + +// Mixing access with square brackets and curly braces. +$a = (new Foo( array(1, array(4, 5), 3) )){1}[0]; // Should give two errors (depending on supported PHP version). +$a = (clone $iterable)[1]{0}; // Should give two errors (depending on supported PHP version). diff --git a/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.php b/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.php index 17254353..c44234cc 100644 --- a/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/NewClassMemberAccessUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class NewClassMemberAccessUnitTest extends BaseSniffTest { @@ -29,14 +30,21 @@ class NewClassMemberAccessUnitTest extends BaseSniffTest * * @dataProvider dataNewClassMemberAccess * - * @param int $line The line number. + * @param int $line The line number. + * @param bool $skipNoViolation Optional. Whether or not to test for no violation. + * Defaults to false. * * @return void */ - public function testNewClassMemberAccess($line) + public function testNewClassMemberAccess($line, $skipNoViolation = false) { $file = $this->sniffFile(__FILE__, '5.3'); $this->assertError($file, $line, 'Class member access on object instantiation was not supported in PHP 5.3 or earlier'); + + if ($skipNoViolation === false) { + $file = $this->sniffFile(__FILE__, '5.4'); + $this->assertNoViolation($file, $line); + } } /** @@ -59,7 +67,7 @@ public function dataNewClassMemberAccess() array(51), array(52), array(54), - array(57), + array(58), array(60), array(61), array(62), @@ -68,12 +76,46 @@ public function dataNewClassMemberAccess() array(76), array(79), array(82), - array(86), + array(87), array(91), array(96), + array(117, true), ); } + + /** + * testNewClassMemberAccessUsingCurlies + * + * @dataProvider dataNewClassMemberAccessUsingCurlies + * + * @param int $line The line number. + * + * @return void + */ + public function testNewClassMemberAccessUsingCurlies($line) + { + $file = $this->sniffFile(__FILE__, '5.6'); + $this->assertError($file, $line, 'Class member access on object instantiation using curly braces was not supported in PHP 5.6 or earlier'); + } + + /** + * Data provider. + * + * @see testNewClassMemberAccessUsingCurlies() + * + * @return array + */ + public function dataNewClassMemberAccessUsingCurlies() + { + return array( + array(111), + array(112), // Error x 2. + array(117), + ); + } + + /** * testCloneClassMemberAccess * @@ -102,6 +144,8 @@ public function dataCloneClassMemberAccess() array(101), array(103), array(105), + array(114), + array(118), // Error x 2. ); } diff --git a/PHPCompatibility/Tests/Syntax/NewDynamicAccessToStaticUnitTest.php b/PHPCompatibility/Tests/Syntax/NewDynamicAccessToStaticUnitTest.php index 98b47766..e3914ee2 100644 --- a/PHPCompatibility/Tests/Syntax/NewDynamicAccessToStaticUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/NewDynamicAccessToStaticUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.1.0 */ class NewDynamicAccessToStaticUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.3.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.3.inc index f44e8317..e13e5dde 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.3.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.3.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // PHP 7.3+ indented content and closing marker - heredoc & spaces. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.4.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.4.inc index 7200f47c..a1d4d815 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.4.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.4.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // PHP 7.3+ indented content and closing marker - nowdoc & tabs. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.5.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.5.inc index 22f4732b..8baef631 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.5.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.5.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // PHP 7.3+ indented content and closing marker - quoted heredoc & spaces. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.6.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.6.inc index 43d1ebb7..21cf3f70 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.6.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.6.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // Closing marker is indented further than any lines of the body is a parse error, but not our concern. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.7.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.7.inc index 29f51182..3b433d6b 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.7.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.7.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // Mixing tabs and spaces for the indentation of the body and the closing marker is a parse error, but not our concern. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.8.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.8.inc index 7eb51f3f..546e559c 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.8.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.8.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // PHP 7.3+ follow on code allowed on the same line as the closing marker. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.9.inc b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.9.inc index 8cdf1cad..d16c43df 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.9.inc +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.9.inc @@ -4,7 +4,7 @@ * Test for errors. * Each test case using PHP 7.3 syntax has to be in its own file as otherwise they can't * be tested using PHP < 7.3. - * Everything after the first test case in PHP < 7.3 will be tokennized as T_ENCAPSED_AND_WHITESPACE. + * Everything after the first test case in PHP < 7.3 will be tokenized as T_ENCAPSED_AND_WHITESPACE. */ // PHP 7.3+ follow on code allowed on the same line as the closing marker. diff --git a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.php b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.php index 390acc5c..315b2505 100644 --- a/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/NewFlexibleHeredocNowdocUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class NewFlexibleHeredocNowdocUnitTest extends BaseSniffTest { + + /** + * Sprintf template for the names of the numbered test case files. + * + * @var string + */ const TEST_FILE = 'NewFlexibleHeredocNowdocUnitTest.%d.inc'; /** @@ -45,9 +52,9 @@ class NewFlexibleHeredocNowdocUnitTest extends BaseSniffTest * Set up skip condition based on used PHP version. * * {@internal The data providers are run before the setUpClass method is run, so - * we can't use that method for this skip condition.}} + * we can't use that method for this skip condition.} * - * @return void + * @return bool True if PHPCS is running on PHP 7.3 or higher. False otherwise. */ public static function getSetSkipCondition() { diff --git a/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.inc b/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.inc index 195e0c8c..ed699866 100644 --- a/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.inc +++ b/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.inc @@ -15,5 +15,23 @@ echo $foo->bar()[1]; echo $foo->bar()->baz()[2]; echo testClass::another_test()[0]; +/* + * PHP 7.0: function array dereferencing using curly braces. + * This "silently" started working in PHP 7.0. See: https://3v4l.org/a1TW6 + */ +echo test(){0}; +echo $foo->bar(){1}; +echo $foo->bar()->baz(){2}; +echo testClass::another_test(){0}; + +// Mixing access with square brackets and curly braces. +echo $foo->bar()->baz()[0]{2}; // Should give two errors (depending on supported PHP version). +echo $foo->bar()->baz(){0}[2]; // Should give two errors (depending on supported PHP version). + +// Prevent curly braces false positive on function declared by reference. +function &test() { + echo '123'; +} + // Don't throw errors during live code review. echo test( diff --git a/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.php b/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.php index 7583b8b6..24840356 100644 --- a/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/NewFunctionArrayDereferencingUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewFunctionArrayDereferencingUnitTest extends BaseSniffTest { @@ -29,14 +30,21 @@ class NewFunctionArrayDereferencingUnitTest extends BaseSniffTest * * @dataProvider dataArrayDereferencing * - * @param int $line Line number with valid code. + * @param int $line The line number. + * @param bool $skipNoViolation Optional. Whether or not to test for no violation. + * Defaults to false. * * @return void */ - public function testArrayDereferencing($line) + public function testArrayDereferencing($line, $skipNoViolation = false) { $file = $this->sniffFile(__FILE__, '5.3'); $this->assertError($file, $line, 'Function array dereferencing is not present in PHP version 5.3 or earlier'); + + if ($skipNoViolation === false) { + $file = $this->sniffFile(__FILE__, '5.4'); + $this->assertNoViolation($file, $line); + } } /** @@ -53,6 +61,43 @@ public function dataArrayDereferencing() array(14), array(15), array(16), + array(28, true), + array(29, true), + ); + } + + + /** + * testArrayDereferencingUsingCurlies + * + * @dataProvider dataArrayDereferencingUsingCurlies + * + * @param int $line Line number with valid code. + * + * @return void + */ + public function testArrayDereferencingUsingCurlies($line) + { + $file = $this->sniffFile(__FILE__, '5.6'); + $this->assertError($file, $line, 'Function array dereferencing using curly braces is not present in PHP version 5.6 or earlier'); + } + + /** + * Data provider. + * + * @see testArrayDereferencingUsingCurlies() + * + * @return array + */ + public function dataArrayDereferencingUsingCurlies() + { + return array( + array(22), + array(23), + array(24), + array(25), + array(28), + array(29), ); } @@ -87,7 +132,8 @@ public function dataNoFalsePositives() array(9), array(10), array(11), - array(19), + array(32), + array(37), ); } @@ -99,7 +145,7 @@ public function dataNoFalsePositives() */ public function testNoViolationsInFileOnValidVersion() { - $file = $this->sniffFile(__FILE__, '5.4'); + $file = $this->sniffFile(__FILE__, '7.0'); $this->assertNoViolation($file); } } diff --git a/PHPCompatibility/Tests/Syntax/NewFunctionCallTrailingCommaUnitTest.php b/PHPCompatibility/Tests/Syntax/NewFunctionCallTrailingCommaUnitTest.php index 6bc472c3..413265c0 100644 --- a/PHPCompatibility/Tests/Syntax/NewFunctionCallTrailingCommaUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/NewFunctionCallTrailingCommaUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class NewFunctionCallTrailingCommaUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Syntax/NewShortArrayUnitTest.php b/PHPCompatibility/Tests/Syntax/NewShortArrayUnitTest.php index a0236bdb..53e889fe 100644 --- a/PHPCompatibility/Tests/Syntax/NewShortArrayUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/NewShortArrayUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewShortArrayUnitTest extends BaseSniffTest { @@ -37,8 +38,8 @@ class NewShortArrayUnitTest extends BaseSniffTest public function testViolation($lineOpen, $lineClose) { $file = $this->sniffFile(__FILE__, '5.3'); - $this->assertError($file, $lineOpen, 'Short array syntax (open) is available since 5.4'); - $this->assertError($file, $lineClose, 'Short array syntax (close) is available since 5.4'); + $this->assertError($file, $lineOpen, 'Short array syntax (open) is not supported in PHP 5.3 or lower'); + $this->assertError($file, $lineClose, 'Short array syntax (close) is not supported in PHP 5.3 or lower'); } /** diff --git a/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc b/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc new file mode 100644 index 00000000..feb06abe --- /dev/null +++ b/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc @@ -0,0 +1,136 @@ +array_num[0],PHP_EOL; + echo self::$array_ass['a'],PHP_EOL; + echo static::CSTRING[1],PHP_EOL; + } +} + +echo $obj->array_num[0],PHP_EOL; +echo Foo::$array_ass['a'],PHP_EOL; +echo Foo::CSTRING[1],PHP_EOL; + + +/* + * Parse errors, not our concern. + */ +$array{} = 3; // Parse error: syntax error, unexpected '}' +$array{ /*comment*/ } = 3; // Parse error: syntax error, unexpected '}' +$array = {1, 2}; // Parse error: syntax error, unexpected '{' +{$one, $two} = $array; // Parse error: syntax error, unexpected ',' + + +/* + * Some alternative uses of curlies which shouldn't generate false positives. + */ +echo ${$var}; +echo ${$var['key1']['key2']}; +echo $obj->{$var['key']}; +echo $obj->{$var['key']}(); +echo myClass::{$var['key']}(); +echo myClass::{$var['key1']['key2']['key3']}(); +class Foo {} +if ($var['a']) {} + + +/* + * PHP 7.4: deprecated curly brace array access syntax. + */ +echo $array{1}; +echo $string{0}; + +echo $array{$i}; +echo $array{$i + 1}; +echo $array{++$i}; + +echo $array{'a'}{0}; // Error x 2. + +// Combining both accesses types. +echo $array[0]{0}; +echo $array{0}[0]; +echo myClass::{$var['key1']{'key2'}{'key3'}}(); // Error x 2. + +// Check code style independence. +echo $array{ /*comment */ $i}[0]; +echo $array[$i] /* comment */ {0}; + +echo $array /*comment*/ {'a'}[0]; +echo $array + ['a'] + {0}; + +// Also applies to properties and constants. +class Foo { + public function bar() { + echo $this->array_num{1}, PHP_EOL; + echo self::$array_ass{'b'}, PHP_EOL; + } +} + +echo $obj->array_num{1}, PHP_EOL; +echo Foo::$array_ass{'b'}, PHP_EOL; + +/* + * Additional tests based on tests found in the merged PHP Core PR. + */ +const D_1 = null ?? A[1]{'undefined'}['index'] ?? 1; +const D_2 = null ?? A['undefined']{'index'} ?? 2; +const D_3 = null ?? A[1]{0}{2} ?? 3; // Error x 2. +const D_4 = A[1]{0} ?? 4; + +echo self::MY_CONST[0]{1}; +echo $this->MY_CONST[0]{1}; + +isset($string{"foo"}{"bar"}); // Error x 2. +$str{-strlen($str)} = strtoupper($str{0}); // Error x 2. +$ret['requestId'] = (ord($data{2}) << 8) + ord($data{3}); // Error x 2. + +/* + * Array and string literal dereferencing using curly braces. + */ +echo array(1, 2, 3){0}; +echo [1, 2, 3]{0}; +echo [1, [20, 21, 22], 3]{1}{1}; // Error x 2. +echo 'PHP'{0}; +echo "PHP"{0}; +const FOO_DEPRECATED = "BAR"{0}; + +/* + * Class member access on instantiation/cloning using curly braces. + */ +(new foo){0}; +$a = (new Foo( array(1, array(4, 5), 3) )){1}{0}; // Error x 2. +echo (clone $iterable){20}; + +// Mixing access with square brackets and curly braces. +$a = (new Foo( array(1, array(4, 5), 3) )){1}[0]; +$a = (clone $iterable)[1]{0}; + +/* + * Function array dereferencing using curly braces. + */ +echo test(){0}; +echo $foo->bar(){1}{0}; // Error x 2. +echo $foo->bar()->baz(){2}; +echo testClass::another_test(){0}; + +// Mixing access with square brackets and curly braces. +echo $foo->bar()->baz()[0]{2}; +echo $foo->bar()->baz(){0}[2]; + +/* Live coding. Intentional parse error. This has to be the last test in the file. */ +$a = $b[0] + $c['something']{ diff --git a/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc.fixed b/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc.fixed new file mode 100644 index 00000000..758120f3 --- /dev/null +++ b/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.inc.fixed @@ -0,0 +1,136 @@ +array_num[0],PHP_EOL; + echo self::$array_ass['a'],PHP_EOL; + echo static::CSTRING[1],PHP_EOL; + } +} + +echo $obj->array_num[0],PHP_EOL; +echo Foo::$array_ass['a'],PHP_EOL; +echo Foo::CSTRING[1],PHP_EOL; + + +/* + * Parse errors, not our concern. + */ +$array{} = 3; // Parse error: syntax error, unexpected '}' +$array{ /*comment*/ } = 3; // Parse error: syntax error, unexpected '}' +$array = {1, 2}; // Parse error: syntax error, unexpected '{' +{$one, $two} = $array; // Parse error: syntax error, unexpected ',' + + +/* + * Some alternative uses of curlies which shouldn't generate false positives. + */ +echo ${$var}; +echo ${$var['key1']['key2']}; +echo $obj->{$var['key']}; +echo $obj->{$var['key']}(); +echo myClass::{$var['key']}(); +echo myClass::{$var['key1']['key2']['key3']}(); +class Foo {} +if ($var['a']) {} + + +/* + * PHP 7.4: deprecated curly brace array access syntax. + */ +echo $array[1]; +echo $string[0]; + +echo $array[$i]; +echo $array[$i + 1]; +echo $array[++$i]; + +echo $array['a'][0]; // Error x 2. + +// Combining both accesses types. +echo $array[0][0]; +echo $array[0][0]; +echo myClass::{$var['key1']['key2']['key3']}(); // Error x 2. + +// Check code style independence. +echo $array[ /*comment */ $i][0]; +echo $array[$i] /* comment */ [0]; + +echo $array /*comment*/ ['a'][0]; +echo $array + ['a'] + [0]; + +// Also applies to properties and constants. +class Foo { + public function bar() { + echo $this->array_num[1], PHP_EOL; + echo self::$array_ass['b'], PHP_EOL; + } +} + +echo $obj->array_num[1], PHP_EOL; +echo Foo::$array_ass['b'], PHP_EOL; + +/* + * Additional tests based on tests found in the merged PHP Core PR. + */ +const D_1 = null ?? A[1]['undefined']['index'] ?? 1; +const D_2 = null ?? A['undefined']['index'] ?? 2; +const D_3 = null ?? A[1][0][2] ?? 3; // Error x 2. +const D_4 = A[1][0] ?? 4; + +echo self::MY_CONST[0][1]; +echo $this->MY_CONST[0][1]; + +isset($string["foo"]["bar"]); // Error x 2. +$str[-strlen($str)] = strtoupper($str[0]); // Error x 2. +$ret['requestId'] = (ord($data[2]) << 8) + ord($data[3]); // Error x 2. + +/* + * Array and string literal dereferencing using curly braces. + */ +echo array(1, 2, 3)[0]; +echo [1, 2, 3][0]; +echo [1, [20, 21, 22], 3][1][1]; // Error x 2. +echo 'PHP'[0]; +echo "PHP"[0]; +const FOO_DEPRECATED = "BAR"[0]; + +/* + * Class member access on instantiation/cloning using curly braces. + */ +(new foo)[0]; +$a = (new Foo( array(1, array(4, 5), 3) ))[1][0]; // Error x 2. +echo (clone $iterable)[20]; + +// Mixing access with square brackets and curly braces. +$a = (new Foo( array(1, array(4, 5), 3) ))[1][0]; +$a = (clone $iterable)[1][0]; + +/* + * Function array dereferencing using curly braces. + */ +echo test()[0]; +echo $foo->bar()[1][0]; // Error x 2. +echo $foo->bar()->baz()[2]; +echo testClass::another_test()[0]; + +// Mixing access with square brackets and curly braces. +echo $foo->bar()->baz()[0][2]; +echo $foo->bar()->baz()[0][2]; + +/* Live coding. Intentional parse error. This has to be the last test in the file. */ +$a = $b[0] + $c['something']{ diff --git a/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.php b/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.php new file mode 100644 index 00000000..885eec9f --- /dev/null +++ b/PHPCompatibility/Tests/Syntax/RemovedCurlyBraceArrayAccessUnitTest.php @@ -0,0 +1,131 @@ +sniffFile(__FILE__, '7.4'); + $this->assertWarning($file, $line, 'Curly brace syntax for accessing array elements and string offsets has been deprecated in PHP 7.4.'); + } + + /** + * Data provider. + * + * @see testRemovedCurlyBraceArrayAccess() + * + * @return array + */ + public function dataRemovedCurlyBraceArrayAccess() + { + return array( + array(53), + array(54), + array(56), + array(57), + array(58), + array(60), // x2. + array(63), + array(64), + array(65), // x2. + array(68), + array(69), + array(71), + array(74), + array(79), + array(80), + array(84), + array(85), + array(90), + array(91), + array(92), // x2. + array(93), + array(95), + array(96), + array(98), // x2. + array(99), // x2. + array(100), // x2. + array(105), + array(106), + array(107), // x2. + array(108), + array(109), + array(110), + array(115), + array(116), // x2. + array(117), + array(120), + array(121), + array(126), + array(127), // x2. + array(128), + array(129), + array(132), + array(133), + ); + } + + + /** + * testNoFalsePositives + * + * @return void + */ + public function testNoFalsePositives() + { + $file = $this->sniffFile(__FILE__, '7.4'); + + // No errors expected on the first 49 lines. + for ($line = 1; $line <= 49; $line++) { + $this->assertNoViolation($file, $line); + } + + // ...and on the last few lines. + for ($line = 135; $line <= 137; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion() + { + $file = $this->sniffFile(__FILE__, '7.3'); + $this->assertNoViolation($file); + } +} diff --git a/PHPCompatibility/Tests/Syntax/RemovedNewReferenceUnitTest.php b/PHPCompatibility/Tests/Syntax/RemovedNewReferenceUnitTest.php index e8981f66..1f253d51 100644 --- a/PHPCompatibility/Tests/Syntax/RemovedNewReferenceUnitTest.php +++ b/PHPCompatibility/Tests/Syntax/RemovedNewReferenceUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 */ class RemovedNewReferenceUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/TextStrings/NewUnicodeEscapeSequenceUnitTest.inc b/PHPCompatibility/Tests/TextStrings/NewUnicodeEscapeSequenceUnitTest.inc new file mode 100644 index 00000000..774b4296 --- /dev/null +++ b/PHPCompatibility/Tests/TextStrings/NewUnicodeEscapeSequenceUnitTest.inc @@ -0,0 +1,61 @@ +sniffFile(__FILE__, '5.6'); + $error = 'Unicode codepoint escape sequences are not supported in PHP 5.6 or earlier. Found: ' . $sequence; + $this->assertError($file, $line, $error); + } + + /** + * dataNewUnicodeEscapeSequence + * + * @see testNewUnicodeEscapeSequence() + * + * @return array + */ + public function dataNewUnicodeEscapeSequence() + { + return array( + array(41, '\u{aa}'), + array(43, '\u{0000aa}'), + array(44, '\u{9999}'), + array(46, '\u{9999}'), + array(47, '\u{00F1}'), + array(48, '\u{0303}'), + array(49, '\u{1F602}'), + array(52, '\u{aa}'), + array(55, '\u{202E}'), + array(59, '\u{D801}'), + array(60, '\u{DC00}'), + array(61, '\u{D801}'), + array(61, '\u{DC00}'), + ); + } + + + /** + * testNoFalsePositivesNewSequence + * + * @return void + */ + public function testNoFalsePositivesNewSequence() + { + $file = $this->sniffFile(__FILE__, '5.6'); + + // No errors expected on the first 40 lines. + for ($line = 1; $line <= 40; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * testNewUnicodeEscapeSequenceFatals + * + * @dataProvider dataNewUnicodeEscapeSequenceFatals + * + * @param int $line Line number where the error should occur. + * @param string $sequence The invalid unicode escape sequence found. + * + * @return void + */ + public function testNewUnicodeEscapeSequenceFatals($line, $sequence) + { + $file = $this->sniffFile(__FILE__, '7.0'); + $error = 'Strings containing a literal \u{ followed by an invalid unicode codepoint escape sequence will cause a fatal error in PHP 7.0 and above. Escape the leading backslash to prevent this. Found: ' . $sequence; + + $this->assertError($file, $line, $error); + } + + /** + * dataNewUnicodeEscapeSequenceFatals + * + * @see testNewUnicodeEscapeSequenceFatals() + * + * @return array + */ + public function dataNewUnicodeEscapeSequenceFatals() + { + return array( + array(27, '\u{foobar'), + array(28, '\u{9999'), + array(29, '\u{}'), + array(30, '\u{+1F602}'), + array(31, '\u{-1F602}'), + array(32, '\u{1F602 }'), + array(35, '\u{110000}'), + ); + } + + + /** + * testNoFalsePositivesFatals + * + * @return void + */ + public function testNoFalsePositivesFatals() + { + $file = $this->sniffFile(__FILE__, '7.0'); + + // No errors expected on the first 25 lines. + for ($line = 1; $line <= 25; $line++) { + $this->assertNoViolation($file, $line); + } + + // No errors expected on the last 20 or so lines. + for ($line = 40; $line <= 62; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /* + * `testNoViolationsInFileOnValidVersion` test omitted as this sniff will throw errors + * in all testVersions. + */ +} diff --git a/PHPCompatibility/Tests/TypeCasts/NewTypeCastsUnitTest.php b/PHPCompatibility/Tests/TypeCasts/NewTypeCastsUnitTest.php index 86e6f0c3..9f22d92f 100644 --- a/PHPCompatibility/Tests/TypeCasts/NewTypeCastsUnitTest.php +++ b/PHPCompatibility/Tests/TypeCasts/NewTypeCastsUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.0.1 */ class NewTypeCastsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.inc b/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.inc index b6a2dd80..417f15f5 100644 --- a/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.inc +++ b/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.inc @@ -2,11 +2,16 @@ // Some other type casts. (string) 1234; -(real) '1.5'; +(float) '1.5'; -// Deprecated introduced type casts. +// Deprecated unset type cast. (unset) $a; // Verify space & case independency. ( unset ) $a; ( Unset ) $a; + +// Deprecated real type cast. +$a = (real) '1.5'; +$a = ( real ) '1.5'; +$a = (double) '1.5'; // Make sure this doesn't throw a false positive. diff --git a/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.php b/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.php index 51780665..8006b22b 100644 --- a/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.php +++ b/PHPCompatibility/Tests/TypeCasts/RemovedTypeCastsUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.0.1 */ class RemovedTypeCastsUnitTest extends BaseSniffTest { @@ -31,7 +32,7 @@ class RemovedTypeCastsUnitTest extends BaseSniffTest * * @param string $castDescription The type of type cast. * @param string $deprecatedIn The PHP version in which the function was deprecated. - * @param string $alternative An alternative function. + * @param string $alternative An alternative type cast. * @param array $lines The line numbers in the test file which apply to this function. * @param string $okVersion A PHP version in which the function was still valid. * @param string $deprecatedVersion Optional PHP version to test deprecation message with - @@ -65,6 +66,7 @@ public function dataDeprecatedTypeCastWithAlternative() { return array( array('The unset cast', '7.2', 'unset()', array(8, 11, 12), '7.1'), + array('The real cast', '7.4', '(float)', array(15, 16), '7.3'), ); } @@ -96,6 +98,7 @@ public function dataNoFalsePositives() return array( array(4), array(5), + array(17), ); } diff --git a/PHPCompatibility/Tests/Upgrade/LowPHPCSUnitTest.inc b/PHPCompatibility/Tests/Upgrade/LowPHPCSUnitTest.inc new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/PHPCompatibility/Tests/Upgrade/LowPHPCSUnitTest.inc @@ -0,0 +1 @@ +sniffResult = $this->sniffFile(__FILE__); + $this->phpcsVersion = PHPCSHelper::getVersion(); + } + + + /** + * Test throwing the PHPCS upgrade notice. + * + * @return void + */ + public function testUpgradeNotice() + { + if (version_compare($this->phpcsVersion, LowPHPCSSniff::MIN_SUPPORTED_VERSION, '<')) { + $this->assertError( + $this->sniffResult, + 1, + 'Please be advised that the minimum PHP_CodeSniffer version the PHPCompatibility standard supports is ' . LowPHPCSSniff::MIN_SUPPORTED_VERSION + ); + } elseif (version_compare($this->phpcsVersion, LowPHPCSSniff::MIN_RECOMMENDED_VERSION, '<')) { + $this->assertWarning( + $this->sniffResult, + 1, + 'Please be advised that for the most reliable PHPCompatibility results, PHP_CodeSniffer ' . LowPHPCSSniff::MIN_RECOMMENDED_VERSION . ' or higher should be used' + ); + } else { + $this->assertNoViolation($this->sniffResult); + } + } +} diff --git a/PHPCompatibility/Tests/Upgrade/LowPHPUnitTest.inc b/PHPCompatibility/Tests/Upgrade/LowPHPUnitTest.inc new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/PHPCompatibility/Tests/Upgrade/LowPHPUnitTest.inc @@ -0,0 +1 @@ +sniffResult = $this->sniffFile(__FILE__); + $this->phpVersion = phpversion(); + } + + + /** + * Test throwing the PHP upgrade notice. + * + * @return void + */ + public function testUpgradeNotice() + { + if (version_compare($this->phpVersion, LowPHPSniff::MIN_SUPPORTED_VERSION, '<')) { + $this->assertError( + $this->sniffResult, + 1, + 'Please be advised that the minimum PHP version the PHPCompatibility standard supports is ' . LowPHPSniff::MIN_SUPPORTED_VERSION + ); + } elseif (version_compare($this->phpVersion, LowPHPSniff::MIN_RECOMMENDED_VERSION, '<')) { + $this->assertWarning( + $this->sniffResult, + 1, + 'Please be advised that for the most reliable PHPCompatibility results, PHP ' . LowPHPSniff::MIN_RECOMMENDED_VERSION . ' or higher should be used' + ); + } else { + $this->assertNoViolation($this->sniffResult); + } + } +} diff --git a/PHPCompatibility/Tests/UseDeclarations/NewGroupUseDeclarationsUnitTest.php b/PHPCompatibility/Tests/UseDeclarations/NewGroupUseDeclarationsUnitTest.php index 38447725..cd35d0a4 100644 --- a/PHPCompatibility/Tests/UseDeclarations/NewGroupUseDeclarationsUnitTest.php +++ b/PHPCompatibility/Tests/UseDeclarations/NewGroupUseDeclarationsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class NewGroupUseDeclarationsUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/UseDeclarations/NewUseConstFunctionUnitTest.php b/PHPCompatibility/Tests/UseDeclarations/NewUseConstFunctionUnitTest.php index e8137d31..fee4155f 100644 --- a/PHPCompatibility/Tests/UseDeclarations/NewUseConstFunctionUnitTest.php +++ b/PHPCompatibility/Tests/UseDeclarations/NewUseConstFunctionUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class NewUseConstFunctionUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Variables/ForbiddenGlobalVariableVariableUnitTest.php b/PHPCompatibility/Tests/Variables/ForbiddenGlobalVariableVariableUnitTest.php index ebfd8c7f..35dbd152 100644 --- a/PHPCompatibility/Tests/Variables/ForbiddenGlobalVariableVariableUnitTest.php +++ b/PHPCompatibility/Tests/Variables/ForbiddenGlobalVariableVariableUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.0 */ class ForbiddenGlobalVariableVariableUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Variables/ForbiddenThisUseContextsUnitTest.php b/PHPCompatibility/Tests/Variables/ForbiddenThisUseContextsUnitTest.php index 0ac23a3f..f4c719e3 100644 --- a/PHPCompatibility/Tests/Variables/ForbiddenThisUseContextsUnitTest.php +++ b/PHPCompatibility/Tests/Variables/ForbiddenThisUseContextsUnitTest.php @@ -3,7 +3,7 @@ * PHPCompatibility, an external standard for PHP_CodeSniffer. * * @package PHPCompatibility - * @copyright 2012-2018 PHPCompatibility Contributors + * @copyright 2012-2019 PHPCompatibility Contributors * @license https://opensource.org/licenses/LGPL-3.0 LGPL3 * @link https://github.com/PHPCompatibility/PHPCompatibility */ @@ -13,7 +13,7 @@ use PHPCompatibility\Tests\BaseSniffTest; /** - * New $this usage limitations sniff tests. + * Test the ForbiddenThisUseContexts sniff. * * @group forbiddenThisUseContexts * @group variables diff --git a/PHPCompatibility/Tests/Variables/NewUniformVariableSyntaxUnitTest.php b/PHPCompatibility/Tests/Variables/NewUniformVariableSyntaxUnitTest.php index f677dcab..8164edc4 100644 --- a/PHPCompatibility/Tests/Variables/NewUniformVariableSyntaxUnitTest.php +++ b/PHPCompatibility/Tests/Variables/NewUniformVariableSyntaxUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.2 */ class NewUniformVariableSyntaxUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Tests/Variables/RemovedPredefinedGlobalVariablesUnitTest.php b/PHPCompatibility/Tests/Variables/RemovedPredefinedGlobalVariablesUnitTest.php index 3a5433b5..bdfa80a6 100644 --- a/PHPCompatibility/Tests/Variables/RemovedPredefinedGlobalVariablesUnitTest.php +++ b/PHPCompatibility/Tests/Variables/RemovedPredefinedGlobalVariablesUnitTest.php @@ -1,8 +1,11 @@ + * @since 5.5 Introduced as LongArraysSniffTest. + * @since 7.0 RemovedVariablesSniffTest. + * @since 7.1.3 Merged to one sniff & test. */ class RemovedPredefinedGlobalVariablesUnitTest extends BaseSniffTest { diff --git a/PHPCompatibility/Util/Tests/Core/DoesFunctionCallHaveParametersUnitTest.php b/PHPCompatibility/Util/Tests/Core/DoesFunctionCallHaveParametersUnitTest.php index 9376ed5b..ae1eb6b5 100644 --- a/PHPCompatibility/Util/Tests/Core/DoesFunctionCallHaveParametersUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/DoesFunctionCallHaveParametersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class DoesFunctionCallHaveParametersUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/FunctionsUnitTest.php b/PHPCompatibility/Util/Tests/Core/FunctionsUnitTest.php index 6e14a080..fd66aa38 100644 --- a/PHPCompatibility/Util/Tests/Core/FunctionsUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/FunctionsUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.6 */ class FunctionsUnitTest extends PHPUnit_TestCase { @@ -508,7 +509,7 @@ public function dataStripVariables() */ private function invokeMethod(&$object, $methodName, array $parameters = array()) { - $reflection = new \ReflectionClass(get_class($object)); + $reflection = new \ReflectionClass(\get_class($object)); $method = $reflection->getMethod($methodName); $method->setAccessible(true); diff --git a/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromDoubleColonTokenUnitTest.php b/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromDoubleColonTokenUnitTest.php index 7b81b3dc..527ffc30 100644 --- a/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromDoubleColonTokenUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromDoubleColonTokenUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.5 */ class GetFQClassNameFromDoubleColonTokenUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromNewTokenUnitTest.php b/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromNewTokenUnitTest.php index 19e5c044..c82f3192 100644 --- a/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromNewTokenUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/GetFQClassNameFromNewTokenUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class GetFQClassNameFromNewTokenUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/GetFQExtendedClassNameUnitTest.php b/PHPCompatibility/Util/Tests/Core/GetFQExtendedClassNameUnitTest.php index ffaf5750..abb1b3c8 100644 --- a/PHPCompatibility/Util/Tests/Core/GetFQExtendedClassNameUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/GetFQExtendedClassNameUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class GetFQExtendedClassNameUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/GetFunctionParameterCountUnitTest.php b/PHPCompatibility/Util/Tests/Core/GetFunctionParameterCountUnitTest.php index 9019a95a..62e4fadb 100644 --- a/PHPCompatibility/Util/Tests/Core/GetFunctionParameterCountUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/GetFunctionParameterCountUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.3 These tests were previously included in the GetFunctionParametersTest class. */ class GetFunctionParameterCountUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/GetFunctionParametersUnitTest.php b/PHPCompatibility/Util/Tests/Core/GetFunctionParametersUnitTest.php index 46f76bed..3597c0b6 100644 --- a/PHPCompatibility/Util/Tests/Core/GetFunctionParametersUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/GetFunctionParametersUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.5 */ class GetFunctionParametersUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/IsClassConstantUnitTest.php b/PHPCompatibility/Util/Tests/Core/IsClassConstantUnitTest.php index eab41f4c..33510d19 100644 --- a/PHPCompatibility/Util/Tests/Core/IsClassConstantUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/IsClassConstantUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class IsClassConstantUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/IsClassPropertyUnitTest.php b/PHPCompatibility/Util/Tests/Core/IsClassPropertyUnitTest.php index 6e0a6f05..c3fc0a80 100644 --- a/PHPCompatibility/Util/Tests/Core/IsClassPropertyUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/IsClassPropertyUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.1.4 */ class IsClassPropertyUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/IsNumberUnitTest.php b/PHPCompatibility/Util/Tests/Core/IsNumberUnitTest.php index 80ec5cac..cc359411 100644 --- a/PHPCompatibility/Util/Tests/Core/IsNumberUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/IsNumberUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class IsNumberUnitTest extends CoreMethodTestFrame { @@ -62,7 +63,7 @@ public function testIsNumber($commentString, $allowFloats, $isNumber, $isPositiv * {@internal Case I13 is not tested here on purpose as the result depends on the * `testVersion` which we don't use in the utility tests. * For a `testVersion` with a minimum of PHP 7.0, the result will be false. - * For a `testVersion` which includes any PHP 5 version, the result will be true.}} + * For a `testVersion` which includes any PHP 5 version, the result will be true.} * * @return array */ diff --git a/PHPCompatibility/Util/Tests/Core/IsNumericCalculationUnitTest.php b/PHPCompatibility/Util/Tests/Core/IsNumericCalculationUnitTest.php index 7145a0cf..e93672a7 100644 --- a/PHPCompatibility/Util/Tests/Core/IsNumericCalculationUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/IsNumericCalculationUnitTest.php @@ -1,8 +1,11 @@ + * @since 9.0.0 */ class IsNumericCalculationUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/IsShortListUnitTest.php b/PHPCompatibility/Util/Tests/Core/IsShortListUnitTest.php index c1de57e1..978265ab 100644 --- a/PHPCompatibility/Util/Tests/Core/IsShortListUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/IsShortListUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.2.0 */ class IsShortListUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/IsUseOfGlobalConstantUnitTest.php b/PHPCompatibility/Util/Tests/Core/IsUseOfGlobalConstantUnitTest.php index f45cb877..4f04527c 100644 --- a/PHPCompatibility/Util/Tests/Core/IsUseOfGlobalConstantUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/IsUseOfGlobalConstantUnitTest.php @@ -1,8 +1,11 @@ + * @since 8.1.0 */ class IsUseOfGlobalConstantUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/Core/TokenScopeUnitTest.php b/PHPCompatibility/Util/Tests/Core/TokenScopeUnitTest.php index d10ecb94..36e28055 100644 --- a/PHPCompatibility/Util/Tests/Core/TokenScopeUnitTest.php +++ b/PHPCompatibility/Util/Tests/Core/TokenScopeUnitTest.php @@ -1,8 +1,11 @@ + * @since 7.0.5 */ class TokenScopeUnitTest extends CoreMethodTestFrame { diff --git a/PHPCompatibility/Util/Tests/CoreMethodTestFrame.php b/PHPCompatibility/Util/Tests/CoreMethodTestFrame.php index 083c186f..90a07349 100644 --- a/PHPCompatibility/Util/Tests/CoreMethodTestFrame.php +++ b/PHPCompatibility/Util/Tests/CoreMethodTestFrame.php @@ -1,8 +1,11 @@ + * @since 7.0.3 + * @since 7.0.5 Renamed from `BaseAbstractClassMethodTest` to `CoreMethodTestFrame`. + * @since 7.1.2 No longer extends the `BaseSniffTest` class. */ abstract class CoreMethodTestFrame extends PHPUnit_TestCase { @@ -25,6 +30,8 @@ abstract class CoreMethodTestFrame extends PHPUnit_TestCase /** * The \PHP_CodeSniffer_File object containing parsed contents of this file. * + * @since 7.0.3 + * * @var \PHP_CodeSniffer_File */ protected $phpcsFile; @@ -32,6 +39,8 @@ abstract class CoreMethodTestFrame extends PHPUnit_TestCase /** * A wrapper for the abstract PHPCompatibility sniff. * + * @since 7.0.3 + * * @var \PHPCompatibility\Sniff */ protected $helperClass; @@ -40,6 +49,8 @@ abstract class CoreMethodTestFrame extends PHPUnit_TestCase /** * Sets up this unit test. * + * @since 7.0.3 + * * @return void */ protected function setUp() @@ -48,7 +59,7 @@ protected function setUp() $this->helperClass = new TestHelperPHPCompatibility(); - $FQClassName = get_class($this); + $FQClassName = \get_class($this); $parts = explode('\\', $FQClassName); $className = array_pop($parts); $subDir = array_pop($parts); @@ -82,6 +93,8 @@ protected function setUp() /** * Clean up after finished test. * + * @since 7.0.3 + * * @return void */ public function tearDown() @@ -93,6 +106,9 @@ public function tearDown() /** * Get the token pointer for a target token based on a specific comment found on the line before. * + * @since 7.1.3 + * @since 8.1.0 New `$tokenContent` parameter. + * * @param string $commentString The comment to look for. * @param int|array $tokenType The type of token(s) to look for. * @param string $tokenContent Optional. The token content for the target token. diff --git a/PHPCompatibility/Util/Tests/TestHelperPHPCompatibility.php b/PHPCompatibility/Util/Tests/TestHelperPHPCompatibility.php index ce37eb43..efaa76a1 100644 --- a/PHPCompatibility/Util/Tests/TestHelperPHPCompatibility.php +++ b/PHPCompatibility/Util/Tests/TestHelperPHPCompatibility.php @@ -1,8 +1,11 @@ + * @since 7.0.3 */ class TestHelperPHPCompatibility extends Sniff { /** * Dummy method to bypass the abstract method implementation requirements. * + * @since 7.0.3 + * * @return void */ public function register() @@ -31,6 +34,8 @@ public function register() /** * Dummy method to bypass the abstract method implementation requirements. * + * @since 7.0.3 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack. @@ -44,6 +49,8 @@ public function process(File $phpcsFile, $stackPtr) /** * Wrapper to make the protected parent::isNumber() method testable. * + * @since 8.2.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Start of the snippet (inclusive), i.e. this * token will be examined as part of the snippet. @@ -61,6 +68,8 @@ public function isNumber(File $phpcsFile, $start, $end, $allowFloats = false) /** * Wrapper to make the protected parent::isNumericCalculation() method testable. * + * @since 9.0.0 + * * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $start Start of the snippet (inclusive), i.e. this * token will be examined as part of the snippet. diff --git a/readme_moodle.txt b/readme_moodle.txt index 46179427..d90a07d5 100644 --- a/readme_moodle.txt +++ b/readme_moodle.txt @@ -17,6 +17,11 @@ Local modifications (only allowed if there is a PR upstream backing it): we bump to to phpcs 3.3.0 this hack can be left out. Upstream ref: https://github.com/squizlabs/PHP_CodeSniffer/issues/2009 + - 04810ac and 8ea64e0: php 7.4 basic compatibility. Will auto-fix once + we bump to to phpcs 3.5.0 or later. Upstream refs: + - https://github.com/squizlabs/PHP_CodeSniffer/issues/2558 + - https://github.com/squizlabs/PHP_CodeSniffer/issues/2561 + ===== ===== ===== ===== ===== ===== ===== Instructions to upgrade the PHPCompatibility bundled version: @@ -28,7 +33,7 @@ Instructions to upgrade the PHPCompatibility bundled version: Current checkout: - 9.1.1+ (4487042) + 9.3.5+ (9fb3244) Local modifications (only allowed if there is a PR upstream backing it): diff --git a/thirdpartylibs.xml b/thirdpartylibs.xml index 6f6272bd..c95c46bf 100644 --- a/thirdpartylibs.xml +++ b/thirdpartylibs.xml @@ -9,7 +9,7 @@ PHPCompatibility PHP Compatibility Coding Standard - 9.1.1+ (4487042) + 9.3.5+ (9fb3244) LGPL 3