gskema/phpcs-type-sniff

PHPCS 检查器,强制执行 PHP7 类型以及数组类型

83.0.0 2024-06-12 09:07 UTC

README

Latest Version on Packagist Software License Build Status Coverage Status Quality Score Total Downloads

自定义 phpcs CodeSniffer 规则,

  • 强制使用 PHP8、PHP7 类型声明(尽可能)
  • 强制使用更具体的类型来记录数组类型(例如 int[]
  • 检查无用的 PHPDoc 块(无重复信息)
  • 许多其他检查

示例 PHP 类(右侧注释 = phpcs 警告)

<?php

namespace Fruits;

/**
 * Class Banana                     // useless description
 * @package Fruits                  // useless tag
 */
class Banana
{
    public const C1 = [];           // missing typed array doc type

    /** @var array */               // must use typed array doc type
    public const C2 = [];

    /** @var array[] */             // must use specific typed array doc type
    public const C3 = [[]];

    /** @var bool|false */          // redundant false type
    public const C4 = false;

    /**
     * @var int                     // incompatible int type, missing null type
     */
    public const C5 = null;

    /** @var int */
    public const C6 = 1;            // useless PHPDoc

    #[ArrayShape(['foo' => 'int'])]
    public const C7 = ['foo' => 1]; // ArrayShape supported

    public $prop1 = [];             // missing typed array doc type + array type decl.

    /** @var array */               // must use typed array doc type + array type decl.
    public $prop2 = [];

    public $prop3;                  // missing type declaration

    /** @var */                     // missing doc type, missing type declaration
    public $prop4;

    /** @var array[][] */           // must use specific typed array doc type
    public $prop5;                  // + missing type declaration

    /** @var array|string[] */      // redundant array type, missing type declaration
    public $prop6;

    /** @var int|string */          // missing null doc type, missing type declaration
    public $prop7 = null;

    /** @var int $prop8 */          // prop name must be removed, missing type decl.
    public $prop8;

    public int $prop9;

    public ?int $prop10;

    /** @var int|null */            // Useless PHPDoc
    public ?int $prop11;

    /** @var string|int */          // missing null type, wrong string type
    public ?int $prop12;

    /** @var string[] */
    public array $prop13;

    #[ArrayShape(['foo' => 'int'])]
    public $prop14 = ['foo' => 1];  // ArrayShape supported

    /** @var class-string */
    public string $prop15;

    /** @var iterable<int, Acme> */
    public iterable $prop16;

    public function __construct(
        $param1,                    // missing param type decl. in method PHPDoc
        public $param2,             // missing param type decl. (in method PHPDoc or inline PHPDoc)
        public array $param3,       // missing typed array doc type (in method PHPDoc or inline PHPDoc)
        public int $param4,
        public int|null             // must use shorthand nullable syntax: ?int
        public readonly $obj = new stdClass(),
        public Iterator&Countable $obj2,
    ) {}

    /**
     * @return $this                // missing type decl. e.g. 'static'
     */
    public function setThing()
    {
        return $this;
    }

    /**
     * @param array|string          // must used typed array + wrong type: string + missing: int 
     * @param mixed $id 
     * @return $this                // useless PHPDoc (does not provide additional code intel over 'static')
     */
    public function setSomething(
        array|int
        $id = null                  // missing type decl. "mixed"
    ): self {
        return $this;
    }

    public function func1(
        $param1,                    // missing param type decl.
        int $param2
    ) {                             // missing return type decl.
    }

    /**
     * @param int|null  $param1
     * @param int|null  $param2
     * @param array     $param3     // must use typed array doc type
     *
     * @param           $param5     // suggested int doc type
     * @param           $param6     // missing doc type
     * @param array[]   $param7     // must use specific typed array doc type
     * @param bool|true $param8     // remove true doc type
     * @param null      $param9     // suggested compound doc type, e.g. int|null
     * @param string    $param10    // incompatible string type, missing int, null types
     * @param stdClass  $param11
     * @param bool|int  $param12
     *
     * @return void                 // useless tag
     */
    public function func2(
        $param1,                    // suggested ?int type decl.
        int $param2 = null,         // suggested ?int type decl.
        array $param3,
        $param4,                    // missing @param tag
        int $param5,
        $param6,
        array $param7,
        bool $param8,
        $param9 = null,             // missing type decl.
        ?int $param10 = null,
        stdClass $param11,
        $param12
    ): void {
    }

    /**
     * @return int
     */
    public function func3(): int    // useless PHPDoc
    {
    }

    /**
     * @param array<int, bool>           $arg1 // alternative array documentation
     * @param array{foo: bool, bar: int} $arg2 // supported, no warning
     * @param (int|string)[]             $arg3 //
     * @param array('key1' => int, ...)  $arg4 //
     */
    public function func4(
        array $arg1,
        array $arg2,
        array $arg3,
        array $arg4
    ): void {
    }

    /**
     * Description
     */ 
    #[ArrayShape(['foo' => 'int'])]     // ArrayShape supported
    public function func5(
        #[ArrayShape(['foo' => 'int'])] // ArrayShape supported
        array $arg1
    ): array {
        return ['foo' => 1];
    }
    
    /**
     * @return Generator<int, string>   // supported
     */
    public function func6(): Generator;
}

安装

通过 Composer

$ composer require --dev gskema/phpcs-type-sniff

使用方法

这是一个独立的检查文件,您需要将其添加到您的 phpcs.xml 文件中。

无反射使用方法

跳过带有 @inheritdoc 标记的方法的检查。如果一个方法没有此标记,则进行检查。这是推荐的配置

<ruleset name="your_ruleset">
    <!-- your configuration -->
    <rule ref="PSR2"/>

    <!-- phpcs-type-sniff configuration -->   
    <rule ref="./vendor/gskema/phpcs-type-sniff/src/Sniffs/CompositeCodeElementSniff.php"/>
</ruleset>

带反射使用方法

启用反射后,此检查器可以断言是否需要 @inheritoc 标记。跳过对扩展/实现的方法的检查。反射需要加载实际类,因此我们需要包含自动加载器。此选项适用于检查扩展方法,但是使用 ReflectionClass 可能会导致 phpcs 在编辑时崩溃(无法捕获 FatalError)。

<ruleset name="your_ruleset">
    <!-- your base configuration -->
    <rule ref="PSR12"/>

    <!-- phpcs-type-sniff configuration -->   
    <autoload>./vendor/autoload.php</autoload>
    <rule ref="./vendor/gskema/phpcs-type-sniff/src/Sniffs/CompositeCodeElementSniff.php">
        <properties>
            <property name="useReflection" value="true"/>
        </properties>
    </rule>
</ruleset>

PHPCS 基准

由于可能检测到太多警告,可能很难将新规则/标准集成到旧项目中。如果您稍后想修复这些问题,但想对所有新代码使用标准,则可以“保存”当前代码上检测到的警告,然后在后续构建中忽略它们。代码标准保持不变,但在后续构建中您“减去”旧(基准)警告

# Add this configuration option to phpcs.xml (see [configuration](#Configuration)):
# <property name="addViolationId" value="true"/>

# Generate report with ignored warnings.
# You may want to commit this file to your repository until you fix all the warnings.
# You may also update this file once in a while. 
./vendor/bin/phpcs --standard=phpcs.xml --report=checkstyle --report-file=baseline.xml

# Run you main code style check command (on build) to generate a report.
# This will contain all warnings, the ignored errors will be subtracted using a command below.
./vendor/bin/phpcs --standard=phpcs.xml --report=checkstyle --report-file=report.xml

# Run a custom PHP script (on build) that subtracts ignored warnings.
# First argument is target report file (that was just built).
# second argument is the baseline report file with ignored warnings which we want to subtract.
# "Subtract baseline.xml warnings from report.xml (report - baseline)":
./bin/phpcs-subtract-baseline ./report.xml ./baseline.xml

# If you generate baseline file locally, but execute build on a different environment,
# you may need to specify additional options to trim filename base paths
# to ensure same relative filename is used when subtracting baseline:
./bin/phpcs-subtract-baseline ./report.xml ./baseline.xml \
  --trim-basepath="/remote/project1/" --trim-basepath="/local/project1/"

# Or you may add this option to generate phpcs report with relative paths, which will
# generate same relative paths. However, some tools may not fully work with relative paths (e.g. Jenkins checkstyle) 
<arg line="-p --basepath=."/>

注意:默认情况下,所有基准警告都通过 filename + line + column + message 哈希进行跟踪。默认情况下,此检查器(CompositeCodeElementSniff)检测到的所有警告都将有一个违规 ID,用于独立于行和列跟踪和比较基准警告。这允许您更改同一文件中的其他内容,其中包含这些警告,违规 ID 不会因为行号/代码风格而更改。

配置

检查器通过其简短类名注册和保存。这允许轻松指定特定代码元素检查器的配置选项,例如 FqcnMethodSniff.invalidTags。所有自定义代码检查器类都必须具有唯一的简短类名。

字符串 true/false 值自动转换为布尔值。

<ruleset name="your_ruleset">
    <!-- your configuration -->
    <rule ref="PSR12"/>

    <!-- phpcs-type-sniff configuration -->   

    <!-- Includes an autoloader which is needed when using reflection API -->
    <!-- or custom code element sniff(s) -->
    <autoload>./vendor/autoload.php</autoload>

    <!-- Includes a standalone sniff to your custom coding standard -->
    <rule ref="./vendor/gskema/phpcs-type-sniff/src/Sniffs/CompositeCodeElementSniff.php">
        <properties>

            <!-- Enables usage of reflection API when inspecting extended classes. -->
            <!-- Autoloader is needed. -->
            <property name="useReflection" value="true"/>

            <!-- Appends violation ID to each error/warning detected by this sniff. -->
            <!-- Used to track baseline warnings of this sniff -->
            <!-- independently of line/column. Default is true. -->
            <property name="addViolationId" value="false"/>

            <!-- Disables one of the default code element sniffs -->
            <property name="FqcnConstSniff.enabled" value="false" />
            <property name="FqcnMethodSniff.enabled" value="false" />
            <property name="FqcnPropSniff.enabled" value="false" />
            <property name="FqcnDescriptionSniff.enabled" value="false" />
            <property name="IteratorItemTypeSniff.enabled" value="false" />

            <!-- Change violation report type for all sniffs. Default is warning. -->
            <property name="reportType" value="error" />

            <!-- Or change violation report type for individual sniffs. Default is warning. -->
            <property name="FqcnConstSniff.reportType" value="error" />
            <property name="FqcnMethodSniff.reportType" value="error" />
            <property name="FqcnPropSniff.reportType" value="warning" />
            <property name="FqcnDescriptionSniff.reportType" value="warning" />
            <property name="IteratorItemTypeSniff.reportType" value="warning" />

            <!-- Tags that should be removed from method PHPDoc -->
            <property name="FqcnMethodSniff.invalidTags" type="array">
                <element value="@someTag1"/>
                <element value="@someTag2"/>
            </property>

            <!-- Description lines and tags that should be removed from FQCN PHPDoc -->
            <property name="FqcnDescriptionSniff.invalidPatterns" type="array">
                <element value="^Nothing.+Useful$"/>
            </property>
            <property name="FqcnDescriptionSniff.invalidTags" type="array">
                <element value="@api"/>
            </property>

            <!-- Enables reporting missing @param, @return tags in non-empty method PHPDoc -->
            <!-- when method type declarations are present -->
            <property name="FqcnMethodSniff.reportMissingTags" value="true"/>

            <!-- When `useReflection:true`, extended methods will be detected -->
            <!-- and sniff will require adding `inheritDoc` tag to these methods. Default: false -->
            <property name="FqcnMethodSniff.requireInheritDoc" value="true"/>

            <!-- Disables reporting missing null type in basic getter return PHPDoc -->
            <!-- and return type declaration -->
            <property name="FqcnPropSniff.reportNullableBasicGetter" value="false"/>

            <!-- Promoted properties in __construct can be inspected as: 'prop' or 'param'. Default: 'prop' -->
            <!-- If set to 'prop', all prop sniffs will apply (but not FqcnMethodSniff parameter sniffs). -->
            <!-- If set to 'param', all parameter sniffs  will apply (but not FqcnPropSniff prop sniffs). -->
            <property name="inspectPromotedConstructorPropertyAs" value="prop"/>

            <!-- Your own custom code element sniff(s). Autoloader is needed. -->
            <!-- These classes implement CodeElementSniffInterface -->
            <property name="sniffs" type="array">
                <element value="\Acme\CustomCodeElementSniff" />
                <element value="\Acme\AnotherCustomMethodSniff" />
            </property>

            <!-- Configuration options for custom code element sniffs -->
            <property name="CustomCodeElementSniff.opt1" value="str1" />
            <!-- Specifying element key(s) will create an associative array -->
            <property name="AnotherCustomMethodSniff.arrayOpt1" type="array">
                <element key="key1" value="str1"/>
                <element key="key2" value="str2"/>
            </property>

        </properties>
    </rule>
</ruleset>

变更日志

有关最近更改的更多信息,请参阅 CHANGELOG

测试

$ ./vendor/bin/phpunit

许可证

MIT 许可证(MIT)。有关更多信息,请参阅 许可证文件