PHP offre plusieurs outils d' (ou "analyse de code statique").
microtime() et memory_get_usage().
Pour mesurer un temps d'exécution dans le code, on peut utiliser des fonctions natives :
$startTime = microtime(true);
$startMemory = memory_get_usage(true);
maFonctionMesurée();
$endTime = microtime(true);
$endMemory = memory_get_usage(true);
echo sprintf(
 'L\'exécution a pris %1$f secondes et %2$f mémoire',
 number_format($endTime - $startTime),
 number_format($endMemory - $startMemory)
phpcs.
PHP_CodeSniffer liste ou corrige les violations des normes de codage.
Installation.
 composer require squizlabs/php_codesniffer --dev
Lancement.
 vendor/bin/phpcs --standard=PSR12 src
NB : le fait de préciser l'utilisation de la PSR12 évite les erreurs inutiles sur les espaces autour des points de concaténation.
Configuration.
Pour ignorer une erreur, ajouter un commentaire :
phpinsights.
Ajoute codice_3 dans tous les fichiers, et formate code code et le style.
Installation.
 composer require nunomaduro/phpinsights --dev
Lancement.
Pour la vérification :
 vendor/bin/phpinsights analyse src -n
Pour réparer automatiquement, ajouter "--fix".
Configuration.
Voici un exemple de fichier phpinsights.php :
<?php
declare(strict_types=1);
return [
 'preset' => 'symfony',
 'ide' => 'phpstorm',
 'exclude' => [
 'add' => [
 'remove' => [
 \PHP_CodeSniffer\Standards\Generic\Sniffs\ControlStructures\DisallowYodaConditionsSniff::class,
 \PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting\SpaceAfterNotSniff::class,
 \SlevomatCodingStandard\Sniffs\ControlStructures\DisallowYodaComparisonSniff::class,
 \SlevomatCodingStandard\Sniffs\ControlStructures\RequireYodaComparisonSniff::class,
 'config' => [
 'requirements' => [
 'min-quality' => 50,
 'min-complexity' => 50,
 'min-architecture' => 50,
 'min-style' => 50,
 'threads' => null,
phpmd.
"PHP Mess Detector" recense les mauvaises pratiques du type "code mort", paramètre inutilisé, mauvais nommage camelCase, présence d'un codice_4, etc..
Installation.
 composer require phpmd/phpmd --dev
Lancement.
 vendor/bin/phpmd src ansi rulesets.xml
Configuration.
Comme CodeSniffer, il utilise un fichier XML pour lister les vérifications à inclure ou exclure.
Exemple de fichier rulesets.xml :
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
 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>
 My custom rule set that checks my code...
 </description>
 <rule ref="rulesets/codesize.xml">
 <exclude name="CyclomaticComplexity" />
 <exclude name="NPathComplexity" />
 <exclude name="ExcessiveMethodLength" />
 <exclude name="ExcessiveParameterList" />
 </rule>
 <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
 <properties>
 <property name="reportLevel" value="20" />
 </properties>
 </rule>
 <rule ref="rulesets/codesize.xml/NPathComplexity">
 <properties>
 <property name="minimum" value="600" />
 </properties>
 </rule>
 <rule ref="rulesets/codesize.xml/ExcessiveMethodLength">
 <properties>
 <property name="minimum" value="110" />
 <property name="ignore-whitespace" value="true" />
 </properties>
 </rule>
 <rule ref="rulesets/codesize.xml/ExcessiveParameterList">
 <properties>
 <property name="minimum" value="14" />
 </properties>
 </rule>
 <rule ref="rulesets/cleancode.xml">
 <exclude name="ElseExpression" />
 <exclude name="IfStatementAssignment" />
 </rule>
 <rule ref="rulesets/controversial.xml" />
 <rule ref="rulesets/design.xml">
 <exclude name="CouplingBetweenObjects" />
 </rule>
 <rule ref="rulesets/design.xml/CouplingBetweenObjects">
 <properties>
 <property name="maximum" value="20" />
 </properties>
 </rule>
 <rule ref="rulesets/naming.xml">
 <exclude name="ShortVariable" />
 <exclude name="LongVariable" />
 </rule>
 <rule ref="rulesets/unusedcode.xml" />
 <exclude-pattern>src/DataFixtures</exclude-pattern>
 <exclude-pattern>src/Migrations</exclude-pattern>
</ruleset>
De plus, pour exclure une classe d'une analyse, on peut placer le nom de l'analyse dans une annotation de la classe. Ex :
 @SuppressWarnings(PHPMD.CyclomaticComplexity)
Ou tout ignorer :
 @SuppressWarnings(PHPMD)
phpmetrics.
Outil d'analyse fournissant un rapport graphique.
Installation.
 composer require phpmetrics/phpmetrics --dev
Lancement.
 vendor/bin/phpmetrics --report-html=reports/phpmetrics-$$(date '+%Y%m%d_%H%M%S').html src
Configuration.
Éviter de versionner les rapports avec l'application. Par exemple ajouter "reports" au .gitignore.
phpstan.
PHP Static Analysis détecte des erreurs potentielles à l'exécution (ex : mauvais type) sans réellement exécuter le code.
Installation.
 composer require phpstan/phpstan --dev
Voir plus pour vérifier l'utilisation de certaines dépendances :
 composer require phpstan/phpstan-phpunit --dev
 composer require phpstan/phpstan-symfony --dev
 composer require phpstan/phpstan-doctrine --dev
Lancement.
 vendor/bin/phpstan analyse --no-progress --memory-limit=256M src
Configuration.
Pour ignorer une erreur sur un élément, il y a trois solutions :
 /** @phpstan-ignore-next-line */
 /** @phpstan-ignore-line */
Exemple de fichier phpstan.neon :
includes:
 - 'vendor/phpstan/phpstan-symfony/extension.neon'
 - 'vendor/phpstan/phpstan-phpunit/extension.neon'
 - 'vendor/phpstan/phpstan-doctrine/extension.neon'
parameters:
 level: 7
 paths:
 - src
 excludePaths:
 - src/Migrations/*
 reportUnmatchedIgnoredErrors: false
 inferPrivatePropertyTypeFromConstructor: true
 checkMissingIterableValueType: false
 checkGenericClassInNonGenericObjectType: false
 ignoreErrors:
 - '#Cannot assign offset [^ ]+ to [^\.]+.#'
 - '#Call to an undefined method [a-zA-Z0-9\\_]+::getItem\(\)#'
 - '#Call to an undefined method Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::add\(\)#'
Le champ "level" va de 0 (violations les plus graves) à 9 (bonnes pratiques idéales). Donc plus il est haut et plus il y a de vérifications.
psalm.
Psalm cherche à détecter des bugs, par exemple des erreurs de type.
Installation.
 composer require --dev vimeo/psalm
 vendor/bin/psalm --init
Lancement.
 vendor/bin/psalm
Configuration.
Pour ignorer une erreur, ajouter l'annotation de la fonction concernée :
 /** @psalm-suppress UndefinedConstant */
En effet, les métriques telles que les taux de duplication ou de couverture du code (par les tests automatiques) sont révélatrices de la qualité du projet. Pour les mesurer il existe plusieurs outils :
php-scrutinizer.
Comprend des analyses de sécurité et de performances.
GrumPHP.
Idem.
 composer require --dev phpro/grumphp
Il s'agit d'un logiciel de qualimétrie dont la version gratuite supporte PHP.
Il nécessite trois composants :
Exemples.
Pour lancer le scan sur un projet Symfony installé sur Windows (dans un .cmd) :
Sur Docker :
Pour remplir le taux de couverture de code, il faut ajouter le lien vers un rapport PhpUnit :
 -Dsonar.php.tests.reportPath=reports/phpunit.logfile.xml \
 -Dsonar.php.coverage.reportPaths=reports/phpunit.coverage.xml
Makefile d'analyse.
Pour éviter de retenir toutes ces commandes, on peut les inclure dans le composer.json à la rubrique "scripts", ou dans un Makefile.
Exemple sur Symfony :
all: lint phpcs-check phpinsights-check phpmd phpstan-check psalm
lint:
 bin/console lint:container
 bin/console lint:twig templates
 bin/console lint:yaml config
phpcs-fix:
 vendor/bin/phpcbf --standard=PSR12 --ignore=Migrations src/
phpcs-check:
 vendor/bin/phpcs --standard=PSR12 --ignore=Migrations src/
phpinsights-check:
 vendor/bin/phpinsights analyse src -n
phpmd:
 vendor/bin/phpmd src ansi rulesets.xml
phpmd-report:
 vendor/bin/phpmd src html rulesets.xml --reportfile reports/phpmd-$$(date '+%Y%m%d_%H%M%S').html
phpmetrics:
 vendor/bin/phpmetrics --report-html=reports/phpmetrics-$$(date '+%Y%m%d_%H%M%S').html src
phpstan:
 vendor/bin/phpstan analyse src/
phpstan-check:
 vendor/bin/phpstan analyse --no-progress --memory-limit=256M src/
psalm:
 vendor/bin/psalm --no-cache