LoginSignup
7
4

More than 5 years have passed since last update.

色んなPHP CS Fixerの紹介

Posted at

色んなPHP CS Fixer達を紹介します。

CodingStandard

Indexed PHP arrays should have 1 item per line

-$friends = [1 => 'Peter', 2 => 'Paul'];
+$friends = [
+    1 => 'Peter',
+    2 => 'Paul'
+];

There should not be empty PHPDoc blocks

Just like PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer, but this one removes all doc block lines.

-/**
- */
 public function someMethod()
 {
 }

Block comment should only contain useful information about types

 /**
- * @param int $value
- * @param $anotherValue
- * @param SomeType $someService
- * @return array
  */
 public function setCount(int $value, $anotherValue, SomeType $someService): array
 {
 }

This checker keeps 'mixed' and 'object' and other types by default. But if you need, you can configure it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\Commenting\RemoveUselessDocBlockFixer:
        useless_types: ['mixed', 'object'] # [] by default

Block comment should not have 2 empty lines in a row

 /**
  * @param int $value
  *
- *
  * @return array
  */
 public function setCount($value)
 {
 }

Include/Require should be followed by absolute path

-require 'vendor/autoload.php';
+require __DIR__.'/vendor/autoload.php';

Types should not be referenced via a fully/partially qualified name, but via a use statement

 namespace SomeNamespace;

+use AnotherNamespace\AnotherType;

 class SomeClass
 {
     public function someMethod()
     {
-        return new \AnotherNamespace\AnotherType;
+        return new AnotherType;
     }
 }

This checker imports single name classes like \Twig_Extension or \SplFileInfo by default. But if you need, you can configure it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\Import\ImportNamespacedNameFixer:
        allow_single_names: true # false by default

You can also configure to check /** @var Namespaced\DocBlocks */ as well:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\Import\ImportNamespacedNameFixer:
        include_doc_blocks: true # false by default

And what about duplicate class name? They are uniquized by vendor name:

<?php declare(strict_types=1);

namespace SomeNamespace;

use Nette\Utils\Finder as NetteFinder;
use Symfony\Finder\Finder;

class SomeClass
{
    public function create(NetteFinder $someClass)
    {
        return new Finder;
    }
}

Parameters, arguments and array items should be on the same/standalone line to fit line length

 class SomeClass
 {
-    public function someMethod(SuperLongArguments $superLongArguments, AnotherLongArguments $anotherLongArguments, $oneMore)
+    public function someMethod(
+        SuperLongArguments $superLongArguments,
+        AnotherLongArguments $anotherLongArguments,
+        $oneMore
+    )
     {
     }

-    public function someOtherMethod(
-        ShortArgument $shortArgument,
-        $oneMore
-    ) {
+    public function someOtherMethod(ShortArgument $shortArgument, $oneMore) {
     }
 }
  • Are 120 characters too long for you?
  • Do you want to break longs lines but not inline short lines or vice versa?

Change it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer:
        max_line_length: 100 # default: 120
        break_long_lines: true # default: true
        inline_short_lines: false # default: true

Magic PHP methods (__*()) should respect their casing form

 class SomeClass
 {
-    public function __CONSTRUCT()
+    public function __construct()
     {
     }
 }

Property name should match its key, if possible

-public function __construct(EntityManagerInterface $eventManager)
+public function __construct(EntityManagerInterface $entityManager)
 {
-    $this->eventManager = $eventManager;
+    $this->entityManager = $entityManager;
 }

This checker ignores few system classes like std* or Spl* by default. In case want to skip more classes, you can configure it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\Naming\PropertyNameMatchingTypeFixer:
        extra_skipped_classes:
            - 'MyApp*' # accepts anything like fnmatch

::class references should be used over string for classes and interfaces

-$className = 'DateTime';
+$className = DateTime::class;

This checker takes only existing classes by default. In case want to check another code not loaded by local composer, you can configure it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer:
        class_must_exist: false # true by default

Array property should have default value, to prevent undefined array issues

 class SomeClass
 {
     /**
      * @var string[]
      */
-    public $apples;
+    public $apples = [];

     public function run()
     {
         foreach ($this->apples as $mac) {
             // ...
         }
     }
 }

Strict types declaration has to be followed by empty line

 <?php declare(strict_types=1);
+
 namespace SomeNamespace;

Non-abstract class that implements interface should be final

Except for Doctrine entities, they cannot be final.

-class SomeClass implements SomeInterface
+final class SomeClass implements SomeInterface
 {
 }

In case want check this only for specific interfaces, you can configure them:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\Solid\FinalInterfaceFixer:
        onlyInterfaces:
            - 'Symfony\Component\EventDispatcher\EventSubscriberInterface'
            - 'Nette\Application\IPresenter'

Block comment should be used instead of one liner

 class SomeClass
 {
-    /** @var int */
+    /**
+     * @var int
+     */
     public $count;
 }

Use explicit and informative exception names over generic ones

:x:

throw new RuntimeException('...');

:thumbsup:

throw new FileNotFoundException('...');

Use explicit return values over magic "&$variable" reference

:x:

function someFunction(&$var)
{
    $var + 1;
}

:thumbsup:

function someFunction($var)
{
    return $var + 1;
}

Use services and constructor injection over static method

:x:

class SomeClass
{
    public static function someFunction()
    {
    }
}

:thumbsup:

class SomeClass
{
    public function someFunction()
    {
    }
}

Constant should have docblock comment

class SomeClass
{
    private const EMPATH_LEVEL = 55;
}

:thumbsup:

class SomeClass
{
    /**
     * @var int
     */
    private const EMPATH_LEVEL = 55;
}

Prefer sprintf() over multiple concats ( . ).

:x:

return 'Class ' . $oldClass . ' was removed from ' . $file . '. Use ' . self::class . " instead';;

:thumbsup:

return sprintf('Class "%s" was removed from "%s". Use "%s" instead', $oldClass, $file, self::class);

Is 2 . too strict? Just configure it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Sniffs\ControlStructure\SprintfOverContactSniff:
        maxConcatCount: 4 # "3" by default

There should not be comments with valid code

:x:

// $file = new File;
// $directory = new Diretory([$file]);

Debug functions should not be left in the code

:x:

dump($value);

Use service and constructor injection rather than instantiation with new

:x:

class SomeController
{
   public function renderEdit(array $data)
   {
        $database = new Database;
        $database->save($data);
   }
}

:thumbsup:

class SomeController
{
   public function renderEdit(array $data)
   {
        $this->database->save($data);
   }
}

This checkers ignores by default some classes, see $allowedClasses property.

In case want to exclude more classes, you can configure it with class or pattern using fnmatch:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\DependencyInjection\NoClassInstantiationSniff:
        extraAllowedClasses:
            - 'PhpParser\Node\*'

Doctrine entities are skipped as well. You can disable that by:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Fixer\DependencyInjection\NoClassInstantiationSniff:
        includeEntities: true

Abstract class should have prefix "Abstract"

:x:

abstract class SomeClass
{
}

:thumbsup:

abstract class AbstractSomeClass
{
}

Class should have suffix by parent class/interface

:x:

class Some extends Command
{
}

:thumbsup:

class SomeCommand extends Command
{
}

This checker check few names by default. But if you need, you can configure it:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Sniffs\Naming\ClassNameSuffixByParentSniff:
        parentTypesToSuffixes:
            # defaults
            - 'Command'
            - 'Controller'
            - 'Repository'
            - 'Presenter'
            - 'Request'
            - 'Response'
            - 'EventSubscriber'
            - 'FixerInterface'
            - 'Sniff'
            - 'Exception'
            - 'Handler'

Or keep all defaults values by using extra_parent_types_to_suffixes:

# easy-coding-standard.yml
services:
    Symplify\CodingStandard\Sniffs\Naming\ClassNameSuffixByParentSniff:
        extraParentTypesToSuffixes:
            - 'ProviderInterface'

It also covers Interface suffix as well, e.g EventSubscriber checks for EventSubscriberInterface as well.

Interface should have suffix "Interface"

:x:

interface Some
{
}

:thumbsup:

interface SomeInterface
{
}

Trait should have suffix "Trait"

:x:

trait Some
{
}

:thumbsup:

trait SomeTrait
{
}

Brave Checkers

Possible Unused Public Method

:x:

class SomeClass
{
    public function usedMethod()
    {
    }

    public function unusedMethod()
    {
    }
}

$someObject = new SomeClass;
$someObject->usedMethod();

:thumbsup:

class SomeClass
{
    public function usedMethod()
    {
    }
}

$someObject = new SomeClass;
$someObject->usedMethod();

phpcs-calisthenics-rules

1. Only X Level of Indentation per Method

:x:

foreach ($sniffGroups as $sniffGroup) {
    foreach ($sniffGroup as $sniffKey => $sniffClass) {
        if (! $sniffClass instanceof Sniff) {
            throw new InvalidClassTypeException;
        }
    }
}

:thumbsup:

foreach ($sniffGroups as $sniffGroup) {
    $this->ensureIsAllInstanceOf($sniffGroup, Sniff::class);
}

// ...
private function ensureIsAllInstanceOf(array $objects, string $type)
{
    // ...
}

Use Just This One?

--sniffs=ObjectCalisthenics.Metrics.MaxNestingLevel
# easy-coding-standard.yml
services:
    ObjectCalisthenics\Sniffs\Metrics\MaxNestingLevelSniff: ~

:wrench: Configurable


2. Do Not Use "else" Keyword

:x:

if ($status === self::DONE) {
    $this->finish();
} else {
    $this->advance();
}

:thumbsup:

if ($status === self::DONE) {
    $this->finish();
    return;
}

$this->advance();

Use Just This One?

--sniffs=ObjectCalisthenics.ControlStructures.NoElseSniff
# easy-coding-standard.yml
services:
    ObjectCalisthenics\Sniffs\ControlStructures\NoElseSniff: ~

5. Use Only One Object Operator (->) per Line

:x:

$this->container->getBuilder()->addDefinition(SniffRunner::class);

:thumbsup:

$containerBuilder = $this->getContainerBuilder();
$containerBuilder->addDefinition(SniffRunner::class);

Use Just This One?

--sniffs=ObjectCalisthenics.CodeAnalysis.OneObjectOperatorPerLine
# easy-coding-standard.yml
services:
    ObjectCalisthenics\Sniffs\CodeAnalysis\OneObjectOperatorPerLineSniff: ~

:wrench: Configurable


6. Do not Abbreviate

This is related to class, trait, interface, constant, function and variable names.

:x:

class EM
{
    // ...
}

:thumbsup:

class EntityMailer
{
    // ...
}

Use Just This One?

--sniffs=ObjectCalisthenics.NamingConventions.ElementNameMinimalLength
# easy-coding-standard.yml
services:
    ObjectCalisthenics\Sniffs\NamingConventions\ElementNameMinimalLengthSniff: ~

:wrench: Configurable


7. Keep Your Classes Small

:x:

class SimpleStartupController
{
    // 300 lines of code
}

:thumbsup:

class SimpleStartupController
{
    // 50 lines of code
}

:x:

class SomeClass
{
    public function simpleLogic()
    {
        // 30 lines of code
    }
}

:thumbsup:

class SomeClass
{
    public function simpleLogic()
    {
        // 10 lines of code
    }
}

:x:

class SomeClass
{
    // 20 properties
}

:thumbsup:

class SomeClass
{
    // 5 properties
}

:x:

class SomeClass
{
    // 20 methods
}

:thumbsup:

class SomeClass
{
    // 5 methods
}

Use Just These Ones?

--sniffs=ObjectCalisthenics.Files.ClassTraitAndInterfaceLength,ObjectCalisthenics.Files.FunctionLengthSniff,ObjectCalisthenics.Metrics.MethodPerClassLimit,ObjectCalisthenics.Metrics.PropertyPerClassLimitSniff
# easy-coding-standard.yml
services:
    ObjectCalisthenics\Sniffs\Files\ClassTraitAndInterfaceLengthSniff: ~
    ObjectCalisthenics\Sniffs\Files\FunctionLengthSniff: ~
    ObjectCalisthenics\Sniffs\Metrics\MethodPerClassLimitSniff: ~
    ObjectCalisthenics\Sniffs\Metrics\PropertyPerClassLimitSniff: ~

:wrench: Configurable


9. Do not Use Getters and Setters

This rules is partially related to Domain Driven Design.

  • Classes should not contain public properties.
  • Method should represent behavior, not set values.

:x:

class ImmutableBankAccount
{
    public $currency = 'USD';
    private $amount;

    public function setAmount(int $amount)
    {
        $this->amount = $amount;
    }
}

:thumbsup:

class ImmutableBankAccount
{
    private $currency = 'USD';
    private $amount;

    public function withdrawAmount(int $withdrawnAmount)
    {
        $this->amount -= $withdrawnAmount;
    }
}

bmitch/Codor

Codor.ControlStructures.NoElse

Does not allow for any else or elseif statements.

:x:
php
if ($foo) {
return 'bar';
} else {
return 'baz';
}

:white_check_mark:
php
if ($foo) {
return 'bar';
}
return 'baz';

Codor.Files.FunctionLength

Functions/methods must be no more than 20 lines.

:x:
php
public function foo()
{
// more than 20 lines
}

:white_check_mark:
php
public function foo()
{
// no more than 20 lines
}

Codor.Files.FunctionParameter

Functions/methods must have no more than 3 parameters.

:x:
php
public function foo($bar, $baz, $bop, $portugal)
{
//
}

:white_check_mark:
php
public function foo($bar, $baz, $bop)
{
//
}

Codor.Files.ReturnNull

Functions/methods must not return null.

:x:
php
public function getAdapter($bar)
{
if ($bar === 'baz') {
return new BazAdapter;
}
return null;
}

:white_check_mark:
php
public function getAdapter($bar)
{
if ($bar === 'baz') {
return new BazAdapter;
}
return NullAdapter;
}

Codor.Files.MethodFlagParameter

Functions/methods cannot have parameters that default to a boolean.

:x:
```php
public function getCustomers($active = true)
{
if ($active) {
// Code to get customers from who are active
}

// Code to get all customers

}
```

:white_check_mark:
```php
public function getAllCustomers()
{

// Code to get all customers
}

public function getAllActiveCustomers()
{

// Code to get customers from who are active
}
```

Codor.Classes.ClassLength

Classes must be no more than 200 lines.

:x:
php
class ClassTooLong
{
// More than 200 lines
}

:white_check_mark:
php
class ClassNotTooLong
{
// No more than 200 lines
}

Codor.Classes.ConstructorLoop

Class constructors must not contain any loops.

:x:
php
public function __construct()
{
for($index = 1; $index < 100; $index++) {
// foo
}
}

:white_check_mark:
```php
public function __construct()
{
$this->someMethod();
}

private function someMethod()
{
for($index = 1; $index < 100; $index++) {
// foo
}
}

### Codor.Classes.Extends ###
Warns if a class extends another class. Goal is to promote composition over inheritance (https://en.wikipedia.org/wiki/Composition_over_inheritance).

:x:
```php
class GasolineCar extends Vehicle
{
    //
}

class GasolineVehicle extends Vehicle
{
    //
}

:white_check_mark:
```php
class Vehicle
{
private $fuel;

public function __construct(FuelInterface $fuel)
{
    $this->fuel;    
}

}

class Gasoline implements FuelInterface
{

}

$gasolineCar = new Vehicle($gasoline);
```

Codor.Classes.FinalPrivate

Final classes should not contain protected methods or variables. Should use private instead.

:x:
```php
final class Foo
{
protected $baz;

protected function bar()
{
    //
}

}
```

:white_check_mark:
```php
final class Foo
{
private $baz;

private function bar()
{
    //
}

}
```

Codor.Classes.PropertyDeclaration

Produces an error if your class uses undeclared member variables. Only warns if class extends another class.

:x:
php
class Foo
{
private function bar()
{
$this->baz = 13;
}
}

:white_check_mark:
```php
class Foo
{
private $baz;

private function bar()
{
    $this->baz = 13;
}

}
```

Codor.Files.FunctionNameContainsAndOr

Functions/methods cannot contain "And" or "Or". This could be a sign of a function that does more than one thing.

:x:
php
public function validateStringAndUpdateDatabase()
{
// code to validate string
// code to update database
}

:white_check_mark:
```php
public function validateString()
{
// code to validate string
}

public function updateDatabase()
{
// code to update database
}
```

Codor.Files.IndentationLevel

Functions/methods cannot have more than 2 level of indentation.

:x:
php
public function foo($collection)
{
foreach ($collection as $bar) {
foreach ($bar as $baz) {
//
}
}
}

:white_check_mark:
```php
public function foo($collection)
{
foreach ($collection as $bar) {
$this->process($bar);
}
}

private function process($bar)
{
foreach ($bar as $baz) {
//
}
}
```

Codor.ControlStructures.NestedIf

Nested if statements are not allowed.

:x:
```php
public function allowedToDrink($person)
{
if ($person->age === 19) {
if ($person->hasValidId()) {
return true;
}
}

return false;

}
```

:white_check_mark:
```php
public function allowedToDrink($person)
{
if ($person->age !== 19) {
return false;
}

if (! $person->hasValidId()) {
    return false;
}

return true;

}
```

Codor.Syntax.NullCoalescing

Produces an error if a line contains a ternary operator that could be converted to a Null Coalescing operator.

:x:
php
$username = isset($customer['name']) ? $customer['name'] : 'nobody';

:white_check_mark:
php
$username = $customer['name'] ?? 'nobody';

Codor.Syntax.LinesAfterMethod

Only allows for 1 line between functions/methods. Any more than 1 will produce an error.

:x:
```php
public function foo()
{
//
}

public function bar()
{
//
}
```

:white_check_mark:
```php
public function foo()
{
//
}

public function bar()
{
//
}
```

Codor.TypeHints.MixedReturnType

Prevents you from having a mixed type returned in a doc block.

:x:
php
/**
* @return mixed
*/
public function foo()
{
//
}

:white_check_mark:
php
/**
* @return string
*/
public function foo()
{
//
}

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4