Join us
As we all know by now, applications evolve a lot during their lifetime and so does the team that works on those applications. There are always people that leave a team to transition to another team or leave the company as there are also new joiners to a team.
It’s a continuous struggle to keep up with the same approach, paradigms, or coding styles in any programming language and application with so many changes in the team or application itself.
Thankfully we have the so-called, coding standard tools, at our disposal as developers to help us mitigate these issues and make sure that everyone is on the same page and adheres to the defined rules when writing new code to extend the application and reading old code when something has to be changed.
For PHP, these tools are:
PHPMD makes sure that your code follows the SOLID principles, adheres to the software design patterns, follows the naming conventions, and checks for unused code. This is all done by the list of rules the PHPMD has and those rules are grouped into 6 rulesets:
You can find more about each of these groups and the rules that they have by visiting their respective documentation. Below you’ll find my most used configuration for this tool with 2 examples of how to override rules, specifically for naming to ignore the $id
property of a class or database model or i
,j
loop variables and how to ignore a static call for a class if there is no other way how to call that class.
<?xml version="1.0"?>
<ruleset name="PHPMD rule set"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="
http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>
Rule set that checks the code against the specified rules to avoid unnecessary complexity
</description>
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/codesize.xml" />
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/design.xml" />
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/naming.xml" >
<exclude name="ShortVariable"/>
</rule>
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/naming.xml/ShortVariable">
<properties>
<property name="exceptions" value="id,q,i,j" />
</properties>
</rule>
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/unusedcode.xml" />
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/unusedcode.xml" />
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/cleancode.xml">
<exclude name="StaticAccess" />
</rule>
<rule ref="vendor/phpmd/phpmd/src/main/resources/rulesets/cleancode.xml/StaticAccess">
<properties>
<property name="exceptions">
<value>
\libphonenumber\PhoneNumberUtil
</value>
</property>
</properties>
</rule>
</ruleset>
2. PHP Code Sniffer (PHPCS)
This tool is used to detect code violations based on a predefined set of rules, like, for example, forbidding the use of certain functions like var_dump
, delete
, extract
, sizeof
, etc. Standardize the usage of single or double quotes, type-hinting, doc block, spacing, forbidden annotations, etc.
Personally, I prefer to use this tool in combination with https://github.com/slevomat/coding-standard and that’s where the configuration below also relies on.
<?xml version="1.0" encoding="UTF-8"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<description>Check the code of the sniffs.</description>
<arg name="basepath" value="."/>
<arg name="cache" value=".phpcs-cache"/>
<arg name="colors"/>
<arg name="extensions" value="php"/>
<config name="installed_paths" value="vendor/slevomat/coding-standard/"/>
<rule ref="PSR12">
<exclude name="Generic.Files.LineLength"/>
</rule>
<rule ref="Generic.Arrays.DisallowLongArraySyntax.Found">
<type>warning</type>
</rule>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Generic.PHP.ForbiddenFunctions">
<properties>
<property name="forbiddenFunctions" type="array">
<element key="chop" value="rtrim"/>
<element key="close" value="closedir"/>
<element key="compact" value="null"/>
<element key="delete" value="unset"/>
<element key="doubleval" value="floatval"/>
<element key="echo" value="null"/>
<element key="extract" value="null"/>
<element key="fputs" value="fwrite"/>
<element key="ini_alter" value="ini_set"/>
<element key="is_double" value="is_float"/>
<element key="is_integer" value="is_int"/>
<element key="is_long" value="is_int"/>
<!--<element key="is_null" value="null"/>-->
<element key="is_real" value="is_float"/>
<element key="is_writeable" value="is_writable"/>
<element key="join" value="implode"/>
<element key="key_exists" value="array_key_exists"/>
<element key="pos" value="current"/>
<element key="settype" value="null"/>
<element key="show_source" value="highlight_file"/>
<element key="sizeof" value="count"/>
<element key="strchr" value="strstr"/>
<element key="var_dump" value="null"/>
</property>
</properties>
</rule>
<rule ref="Squiz.Strings.ConcatenationSpacing">
<properties>
<property name="spacing" value="1"/>
</properties>
</rule>
<rule ref="Squiz.Strings.DoubleQuoteUsage"/>
<rule ref="SlevomatCodingStandard.Classes.ClassStructure"/>
<rule ref="SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces">
<properties>
<property name="linesCountAfterOpeningBrace" value="0"/>
<property name="linesCountBeforeClosingBrace" value="0"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration"/>
<rule ref="SlevomatCodingStandard.Commenting.UselessFunctionDocComment"/>
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses"/>
<rule ref="SlevomatCodingStandard.Namespaces.DisallowGroupUse"/>
<rule ref="SlevomatCodingStandard.Namespaces.MultipleUsesPerLine"/>
<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly"/>
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
<properties>
<property name="searchAnnotations" value="true"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash"/>
<rule ref="SlevomatCodingStandard.TypeHints.LongTypeHints"/>
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing"/>
<rule ref="Squiz.WhiteSpace.FunctionSpacing">
<properties>
<property name="spacing" value="1"/>
<property name="spacingBeforeFirst" value="0"/>
<property name="spacingAfterLast" value="0"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.FunctionOpeningBraceSpace" />
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
<properties>
<!-- turned on by PSR2 -> turning back off -->
<property name="ignoreBlankLines" value="false"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
<!-- turned off by PSR2 -> turning back on -->
<severity>5</severity>
</rule>
<rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations">
<properties>
<property name="forbiddenAnnotations" type="array">
<element value="@api"/>
<element value="@author"/>
<element value="@category"/>
<element value="@copyright"/>
<element value="@covers"/>
<element value="@coversDefaultClass"/>
<element value="@coversNothing"/>
<element value="@created"/>
<element value="@license"/>
<element value="@package"/>
<element value="@since"/>
<element value="@subpackage"/>
<element value="@version"/>
</property>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Exceptions.DeadCatch"/>
<rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
<file>config/</file>
<file>public/</file>
<file>src/</file>
<file>tests/</file>
<exclude-pattern type="relative">*.(js|css)</exclude-pattern>
</ruleset>
3. PHP Code Sniffer Fixer (PHPCBF)
PHPCBF is an addition to PHPCS. What it does is that it tries to fix as many of the reported issues as possible. I’m emphasizing the as many as possible
here since PHPCBF cannot fix all of the reported issues. It fixes the simple line code formatting in regards to spacing, quotes format, and some minor code changes on some conditions, using fully qualified names for your imports, but it doesn’t automatically correct everything for you.
4. PHPSTAN
This tool makes sure that your code is properly annotated and that it has the correct return types. It makes it a lot easier to work with the code base, not only for the automated tools and tests but also for other technical or non-technical people who work with the code. Here is my configuration with a custom rule for this tool.
<?php
namespace App\PHPStanRule;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
class MyRule implements Rule
{
private bool $reportMaybes;
public function __construct(bool $reportMaybes = false)
{
$this->reportMaybes = $reportMaybes;
}
public function getNodeType(): string
{
return Node::class;
}
public function processNode(Node $node, Scope $scope): array
{
var_dump($this->reportMaybes);
//var_dump(get_class($node));
return [];
}
}
parameters:
ignoreErrors:
-
message: "#^Only numeric types are allowed in pre\\-decrement, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/Analyser/Scope.php
-
message: "#^Anonymous function has an unused use \\$container\\.$#"
count: 2
path: src/Command/CommandHelper.php
includes:
- phpstan-baseline.neon
rules:
- App\PHPStanRule\MyRule
parameters:
level: max
paths:
- src
- tests
scanDirectories:
- src
- tests
reportUnmatchedIgnoredErrors: false
ignoreErrors:
- '#Call to an undefined method [a-zA-Z0-9\\_]+::doFoo\(\)#'
excludePaths:
analyse:
- src/thirdparty
analyseAndScan:
- src/broken
parallel:
processTimeout: 120.0
services:
-
class: App\PHPStanRule\MyRule
arguments:
reportMaybes: true
tags:
- phpstan.rules.rule
5. PSALM
Psalm is also a static analysis tool like PHPSTAN but PASLM attempts to dig into your program and find many more issues than PHPSTAN. It has a few features that go further than other similar tools:
mixed
as type-hint or return type but PHPSTAN allows this.if ($a && $a) {}
and if ($a && !$a) {}
are both treated as issues. Checks also logical assertions made in prior code paths, preventing issues like if ($a) {} elseif ($a) {}
.Here is my basic PSALM configuration.
<?xml version="1.0"?>
<psalm
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
reportMixedIssues="false"
>
<projectFiles>
<directory name="src"/>
<directory name="tests"/>
<ignoreFiles>
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
<issueHandlers>
<PropertyNotSetInConstructor>
<errorLevel type="suppress">
<directory name="tests/"/>
<referencedProperty name="$backupStaticAttributes"/>
<referencedProperty name="$runTestInSeparateProcess"/>
</errorLevel>
</PropertyNotSetInConstructor>
<UnusedClass>
<errorLevel type="suppress">
<directory name="tests/"/>
</errorLevel>
</UnusedClass>
</issueHandlers>
</psalm>
NOTE: Personally I like to use a combination of PHPSTAN and PSALM on my projects since they check different aspects of your codebase, even though the seem quite similar
Hope you find this article useful and that it can help you in your day-to-day work. Please follow and subscribe for more articles like this.
Join other developers and claim your FAUN account now!
Staff Software Engineer, Emma - The Sleep Company
@abameInfluence
Total Hits
Posts
Only registered users can post comments. Please, login or signup.