gskema / phpcs-type-sniff
PHPCS sniff to enforce PHP7 types and array types
Installs: 85 721
Dependents: 2
Suggesters: 0
Security: 0
Stars: 40
Watchers: 2
Forks: 3
Open Issues: 0
Requires
- php: >=8.3
- squizlabs/php_codesniffer: ^3.10
Requires (Dev)
- phpunit/phpunit: ^11.2
- scrutinizer/ocular: ^1.9
- 83.x-dev
- 83.0.0
- 82.x-dev
- 82.3.0
- 82.2.1
- 82.2.0
- 82.1.0
- 82.0.2
- 82.0.1
- 82.0.0
- 81.x-dev
- 81.6.4
- 81.6.3
- 81.6.2
- 81.6.1
- 81.6.0
- 81.5.2
- 81.5.1
- 81.5.0
- 81.4.0
- 81.3.2
- 81.3.1
- 81.3.0
- 81.2.0
- 81.1.0
- 81.0.0
- 80.x-dev
- 80.2.0
- 80.1.0
- 80.0.0
- 74.x-dev
- 74.3.0
- 74.2.0
- 74.1.2
- 74.1.1
- 74.1.0
- 74.0.5
- 74.0.4
- 74.0.3
- 74.0.2
- 74.0.1
- 74.0.0
- 0.x-dev
- 0.18.2
- 0.18.1
- 0.18.0
- 0.17.0
- 0.16.1
- 0.16.0
- 0.15.0
- 0.14.1
- 0.14.0
- 0.13.1
- 0.13.0
- 0.12.4
- 0.12.3
- 0.12.2
- 0.12.1
- 0.12.0
- 0.11.1
- 0.11.0
- 0.10.5
- 0.10.4
- 0.10.3
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.0
This package is auto-updated.
Last update: 2024-11-12 10:03:54 UTC
README
Custom phpcs
CodeSniffer rule that:
- Enforces usage of PHP8, PHP7 type declarations (where possible)
- Enforces documenting array types with more specific types (e.g.
int[]
) - Checks for useless PHPDoc blocks (no repeated information)
- Many more other checks
Example PHP class (comments on the right = phpcs
warnings):
<?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; }
Install
Via Composer
$ composer require --dev gskema/phpcs-type-sniff
Usage
This is a standalone sniff file, you need to add it to your phpcs.xml
file.
Usage Without Reflection
Inspections for methods with @inheritdoc
tag are skipped.
If a method does not have this tag, it is inspected. This is the recommended setup.
<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>
Usage With Reflection
With reflection enabled, this sniff can assert if @inheritoc
tag
is needed. Inspections for extended/implemented methods are skipped.
Reflections need to load actual classes, which is why we need to include
the autoloader. This option is good for inspecting extended methods, however using ReflectionClass
may
cause phpcs
crashes while editing (not possible to catch 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 Baseline
It may be hard to integrate new rules/standards into old projects because too many warnings may be detected. If you would like to fix them later, but use the standard for all new code, you can "save" warnings detected on current code, then ignore them on subsequent builds. The code standard remains the same, but on subsequent builds you "subtract" the old (baseline) warnings:
# 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=."/>
Note: By default all baseline warnings are tracked by filename + line + column + message
hash.
By default, all warnings detected by this sniff (CompositeCodeElementSniff
) will have a violation ID,
which is used to track and compare baseline warnings independently of line and column.
This allows you to change other things in the same file where these warnings are detected, the violation ID does
not change because of line numbers / code style.
Configuration
Sniffs are registered and saved by their short class name.
This allows easily specifying configuration options for a specific code element sniff,
e.g. FqcnMethodSniff.invalidTags
. All custom code sniff classes must have unique
short class names.
String true/false
values are automatically converted to booleans.
<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>
Change log
Please see CHANGELOG for more information on what has changed recently.
Testing
$ ./vendor/bin/phpunit
License
The MIT License (MIT). Please see License File for more information.