<?xml version="1.0" encoding="UTF-8"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    name="CakePHP"
    namespace="CakePHP"
    xsi:noNamespaceSchemaLocation="https://schema.phpcodesniffer.com/phpcs.xsd"
>
    <description>CakePHP coding standard</description>

    <exclude-pattern>\.git</exclude-pattern>
    <exclude-pattern>/*/tmp/</exclude-pattern>
    <exclude-pattern>tests/*/templates/*</exclude-pattern>

    <!-- PSR12 Standard -->
    <rule ref="PSR12">
        <exclude name="PSR12.Files.FileHeader.SpacingAfterTagBlock"/>
        <exclude name="PSR12.Files.FileHeader.SpacingAfterDocblockBlock"/>
        <exclude name="PSR12.Files.FileHeader.IncorrectOrder"/>
        <exclude name="PSR12.Files.FileHeader.SpacingAfterUseBlock"/>
        <exclude name="PSR12.Files.FileHeader.SpacingAfterUseFunctionBlock"/>
        <!--
        Property and method names with underscore prefix are allowed in CakePHP.
        Not using underscore prefix is a recommendation of PSR2, not a requirement.
        -->
        <exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
        <exclude name="PSR2.Methods.MethodDeclaration.Underscore"/>
    </rule>

    <!-- Relax rules from PSR12 -->
    <rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
        <exclude-pattern>*/config/Migrations/*</exclude-pattern>
        <exclude-pattern>*/config/Seeds/*</exclude-pattern>
    </rule>
    <rule ref="PSR1.Files.SideEffects">
        <exclude-pattern>*/config/*</exclude-pattern>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="PSR1.Methods.CamelCapsMethodName">
        <exclude-pattern>*/src/Controller/*</exclude-pattern>
        <exclude-pattern>*/src/Command/*</exclude-pattern>
        <exclude-pattern>*/src/Shell/*</exclude-pattern>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>

    <!-- Additional sniffs outside of PSR12 -->

    <!-- phpcs Generic sniffs -->
    <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
    <rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
    <rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
    <rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
    <rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
    <rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
    <rule ref="Generic.Files.LineLength">
        <properties>
            <property name="ignoreComments" type="boolean" value="true"/>
        </properties>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="Generic.Formatting.SpaceAfterCast">
        <properties>
            <property name="spacing" value="0"/>
        </properties>
    </rule>
    <rule ref="Generic.PHP.DeprecatedFunctions"/>
    <rule ref="Generic.PHP.ForbiddenFunctions"/>
    <rule ref="Generic.PHP.NoSilencedErrors"/>
    <rule ref="Generic.WhiteSpace.LanguageConstructSpacing"/>

    <!-- Relax CakePHP rules -->
    <rule ref="CakePHP.Commenting.FunctionComment">
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="CakePHP.Commenting.TypeHint">
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>

    <!-- phpcs Squiz sniffs -->
    <rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
    <rule ref="Squiz.Classes.ClassFileName"/>
    <rule ref="Squiz.Classes.ClassFileName.NoMatch">
        <exclude-pattern>*/config/Migrations/*</exclude-pattern>
    </rule>
    <rule ref="Squiz.Classes.LowercaseClassKeywords"/>
    <rule ref="Squiz.Commenting.DocCommentAlignment"/>
    <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
        <properties>
            <property name="equalsSpacing" value="1"/>
        </properties>
    </rule>
    <rule ref="Squiz.Operators.ValidLogicalOperators"/>
    <rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
    <rule ref="Squiz.PHP.Eval"/>
    <rule ref="Squiz.PHP.NonExecutableCode"/>
    <rule ref="Squiz.Scope.MemberVarScope"/>
    <rule ref="Squiz.Scope.StaticThisUsage"/>
    <rule ref="Squiz.WhiteSpace.CastSpacing"/>
    <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
    <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"/>
    <rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
    <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
        <properties>
            <property name="ignoreBlankLines" type="boolean" value="false"/>
        </properties>
    </rule>

    <!-- Slevomat sniffs-->
    <rule ref="SlevomatCodingStandard.Arrays.TrailingArrayComma"/>
    <rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInDeclaration"/>
    <rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInClosureUse"/>
    <rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInCall"/>
    <rule ref="SlevomatCodingStandard.Functions.DisallowTrailingCommaInDeclaration">
        <properties>
            <property name="onlySingleLine" type="boolean" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Functions.DisallowTrailingCommaInClosureUse">
        <properties>
            <property name="onlySingleLine" type="boolean" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Functions.DisallowTrailingCommaInCall">
        <properties>
            <property name="onlySingleLine" type="boolean" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Classes.ClassConstantVisibility">
        <properties>
            <property name="fixable" type="boolean" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces">
        <properties>
            <property name="linesCountAfterOpeningBrace" value="0"/>
            <property name="linesCountBeforeClosingBrace" value="0"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Classes.ModernClassNameReference">
        <properties>
            <property name="enableOnObjects" value="false"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Commenting.DisallowOneLinePropertyDocComment"/>
    <rule ref="SlevomatCodingStandard.Commenting.DocCommentSpacing">
        <properties>
            <property name="linesCountBeforeFirstContent" value="0"/>
            <property name="linesCountBetweenDescriptionAndAnnotations" value="1"/>
            <property name="linesCountBetweenDifferentAnnotationsTypes" value="0"/>
            <property name="linesCountBetweenAnnotationsGroups" value="0"/>
            <property name="linesCountAfterLastContent" value="0"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Commenting.EmptyComment"/>
    <rule ref="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration">
        <properties>
            <property name="allowDocCommentAboveReturn" type="boolean" value="true"/>
        </properties>
        <exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.NoAssignment"/>
        <exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable"/>
    </rule>
    <rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.DisallowContinueWithoutIntegerOperandInSwitch"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.DisallowYodaComparison"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.LanguageConstructWithParentheses"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.NewWithParentheses"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator"/>
    <rule ref="SlevomatCodingStandard.Exceptions.DeadCatch"/>
    <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses"/>
    <rule ref="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation">
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>

    <rule ref="SlevomatCodingStandard.Namespaces.NamespaceDeclaration"/>
    <rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly">
        <properties>
            <property name="allowFallbackGlobalFunctions" type="boolean" value="true"/>
            <property name="allowFallbackGlobalConstants" type="boolean" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Namespaces.DisallowGroupUse"/>
    <rule ref="SlevomatCodingStandard.Namespaces.UnusedUses"/>
    <rule ref="SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash"/>
    <rule ref="SlevomatCodingStandard.Namespaces.UseFromSameNamespace"/>
    <rule ref="SlevomatCodingStandard.Namespaces.UseSpacing">
        <exclude-pattern>*/templates/*</exclude-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.PHP.ShortList"/>
    <rule ref="SlevomatCodingStandard.PHP.TypeCast"/>
    <rule ref="SlevomatCodingStandard.PHP.UselessParentheses"/>
    <rule ref="SlevomatCodingStandard.PHP.UselessSemicolon"/>
    <rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
        <properties>
            <property name="linesCountBeforeDeclare" value="0"/>
            <property name="linesCountAfterDeclare" value="1"/>
            <property name="spacesCountAroundEqualsSign" value="0"/>
        </properties>
        <exclude-pattern>*/config/*</exclude-pattern>
        <exclude-pattern>*/templates/*</exclude-pattern>
        <exclude-pattern>*/tests/Fixture/*</exclude-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.TypeHints.LongTypeHints"/>
    <rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue"/>
    <rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint">
        <properties>
            <property name="enableMixedTypeHint" type="boolean" value="true"/>
            <property name="enableUnionTypeHint" type="boolean" value="true"/>
        </properties>
        <exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
        <exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.UselessAnnotation"/>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHintSpacing"/>
    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint">
        <properties>
            <property name="enableMixedTypeHint" type="boolean" value="true"/>
            <property name="enableStaticTypeHint" type="boolean" value="true"/>
            <property name="enableUnionTypeHint" type="boolean" value="true"/>
        </properties>
        <exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
        <exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation"/>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
        <properties>
            <property name="enableMixedTypeHint" type="boolean" value="true"/>
            <property name="enableUnionTypeHint" type="boolean" value="true"/>
        </properties>
        <exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
        <exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.UselessAnnotation"/>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.TypeHints.DNFTypeHintFormat">
        <properties>
            <property name="enable" type="boolean" value="true"/>
            <property name="withSpacesAroundOperators" value="no"/>
            <property name="withSpacesInsideParentheses" value="no"/>
            <property name="shortNullable" value="yes"/>
        </properties>
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.Classes.PropertyDeclaration"/>
    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing"/>
    <rule ref="SlevomatCodingStandard.Variables.DuplicateAssignmentToVariable"/>
    <rule ref="SlevomatCodingStandard.Variables.UnusedVariable">
        <properties>
            <property name="ignoreUnusedValuesWhenOnlyKeysAreUsedInForeach" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Functions.NamedArgumentSpacing"/>
    <rule ref="SlevomatCodingStandard.Functions.ArrowFunctionDeclaration">
        <properties>
            <property name="spacesCountAfterKeyword" value="0"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Attributes.AttributeAndTargetSpacing"/>
    <rule ref="SlevomatCodingStandard.Attributes.RequireAttributeAfterDocComment"/>
    <rule ref="SlevomatCodingStandard.Classes.BackedEnumTypeSpacing"/>
    <rule ref="SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator"/>
    <rule ref="SlevomatCodingStandard.Whitespaces.DuplicateSpaces">
        <properties>
            <property name="ignoreSpacesInComment" value="true"/>
        </properties>
    </rule>

    <!-- phpcs Zend sniffs -->
    <rule ref="Zend.NamingConventions.ValidVariableName">
        <exclude name="Zend.NamingConventions.ValidVariableName.PrivateNoUnderscore"/>
        <exclude name="Zend.NamingConventions.ValidVariableName.ContainsNumbers"/>
        <exclude name="Zend.NamingConventions.ValidVariableName.StringVarContainsNumbers"/>
        <exclude name="Zend.NamingConventions.ValidVariableName.MemberVarContainsNumbers"/>
        <exclude name="Zend.NamingConventions.ValidVariableName.NotCamelCaps"/>
        <exclude name="Zend.NamingConventions.ValidVariableName.MemberVarNotCamelCaps"/>
    </rule>

    <!-- All rules in ./Sniffs are included automatically -->
</ruleset>
