PHP kódminőség fenntartás

« All posts

(PHP kódminőség fenntartás)

PHP kódminőség fenntartás

Néha rámtör az "ezúttal mindent jól csinálok" érzés, ilyenkor megpróbálom az említett téma aspektusait a legmélyebb részletig kidolgozni. Két ilyen roham között általában van két verziónyi eltérés az eszközök között, így mindig van egy kis izgalom is, hogy éppen mi hogyan működik.

A felsorolt - többségében statikus kódelemző - szoftverek abban segítenek, hogy bizonyos közmegállapodás vagy egyénre/projektre szabott szabályok mentén megvizsgálja a gyártott PHP kódot és felhívja a figyelmet az eltérésekre. Mindegyik valahogy máshogy áll hozzá, máshogy kell konfigurálni, használni, más tartalmú és formájú lesz az eredmény. Egyesek jól támogatják a CI/CD integrációt, mások olyan kimenetet tudnak gyártani, amit aztán fejlettebb, aggregálási- és riportolási célú eszközökbe lehet betölteni.

Az én felhasználási módom nagyon egyszerű: egyszemélyes, egyedi kód, kis projekt, egy dev és egy éles rendszer, közötte pedig FTP kapcsolat ("Vér István EV" mód).

Részletezett eszközök:

PHPUnit

A PHP de-facto unit-tesztelő eszköze, aktív fejlesztés alatt, számomra elég full-fledged-nek tűnik a mai napig. Nagyon gyorsan tesztel (másodpercenként 100-200 assertion), informatív lefedettség-riportokat csinál HTML-ben.

Honlap: https://phpunit.de/index.html
Doksi: https://phpunit.de/getting-started/phpunit-11.html
Letöltés (PHAR): https://phar.phpunit.de/phpunit-11.phar

Használat:

php phpunit.phar --configuration phpunit.xml projektkonyvtar

A phpunit.xml tartalma (értelemszerűen testreszabandó, főleg a megadott könyvtárak):

<?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.3/phpunit.xsd" backupGlobals="false" backupStaticProperties="false" cacheDirectory="../work/.phpunit.cache" cacheResult="true" colors="true" columns="200" requireCoverageMetadata="false" processIsolation="false" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnRisky="false" stopOnSkipped="false" stopOnWarning="false" stopOnDefect="false" failOnEmptyTestSuite="true" failOnIncomplete="true" failOnRisky="true" failOnSkipped="false" failOnWarning="true" beStrictAboutChangesToGlobalState="true" beStrictAboutOutputDuringTests="true" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutCoverageMetadata="true" enforceTimeLimit="false" defaultTimeLimit="0" timeoutForSmallTests="1" timeoutForMediumTests="10" timeoutForLargeTests="60" stderr="false" reverseDefectList="false" executionOrder="depends,defects" resolveDependencies="true" testdox="false" displayDetailsOnIncompleteTests="true" displayDetailsOnSkippedTests="true" displayDetailsOnTestsThatTriggerDeprecations="true" displayDetailsOnTestsThatTriggerErrors="true" displayDetailsOnTestsThatTriggerNotices="true" displayDetailsOnTestsThatTriggerWarnings="true" shortenArraysForExportThreshold="10" displayDetailsOnPhpunitDeprecations="true" failOnPhpunitDeprecation="true"

tests

<source ignoreIndirectDeprecations="false" restrictNotices="false" restrictWarnings="false"

../src

<coverage includeUncoveredFiles="true" pathCoverage="true" ignoreDeprecatedCodeUnits="false" disableCodeCoverageIgnore="false"

Fontos: a coverage riporthoz szükség van az xdebug extension bekapcsolására (php.ini-ben):

zend_extension=xdebug-3.3.2-8.2-vs16-nts-x86_64 xdebug.mode=coverage

Példariport:

PHP CodeSniffer (phpcs) és PHP Code Beautifier and Fixer (phpcbf)

A marketing szerint tiszta kód és konzisztencia elősegítő eszköz. A phpcs csak kiírja a szabálysértéseket, a phpcbf képes ezekből néhányat automatikusan javítani (pl. kódformázással kapcsolatosakat).

Honlap: https://github.com/PHPCSStandards/PHP_CodeSniffer/
Doksi: https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki
Letöltés (PHAR): https://github.com/PHPCSStandards/PHP_CodeSniffer/releases

Használat:

php phpcs.phar -s --standard=phpcs.xml -l projektkonyvtar

Van néhány előre összeállított szabálykészlet, amiket lehet használni, ezeket amúgy ki lehet iratni a phpcs -i paranccsal, amiről két dolgot kell tudni:

  1. nem listázza a "Generic" készletet
  2. még benne van a listában a "MySource" szabálykészlet, ami a következő verziótól deprecated lesz

A phpcs.xml tartalma (a könyvtárakat és a kihagyott elemeket érdemes elsősorban testreszabni, de akár a szabályokat is, törekedtem azért egy elég szigorú eredményt összerakni):

<?xml version="1.0"?>

The coding standard MVC Framework src \*/tests

Példa kimenet:

PHP Mess Detector (régen: PHP Depend)

A phpmd azt tűzte ki célul, hogy felhívja a figyelmet azokra a kódrészletekre, amik nem követik a best practice-eket, fölöslegesen bonyolultak, esetleg nem használt részletek/változók vannak benne.

Honlap: https://phpmd.org/
Doksi: https://phpmd.org/documentation/index.html
Letöltés (PHAR): https://phpmd.org/static/latest/phpmd.phar

Használat:

php phpmd.phar projektkonyvtar text phpmd.xml

A phpmd.xml tartalma:

<?xml version="1.0"?> <ruleset name="The coding standard MVC Framework" xmlns="https://phpmd.org/xml/ruleset/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://phpmd.org/xml/ruleset/1.0.0 http://phpmd.org/xml/ruleset\_xml\_schema\_1.0.0.xsd" xsi:noNamespaceSchemaLocation="http://phpmd.org/xml/ruleset\_xml\_schema\_1.0.0.xsd"

The coding standard MVC Framework
<rule ref="rulesets/codesize.xml">
    <exclude name="TooManyPublicMethods" />
</rule>

<rule ref="rulesets/cleancode.xml">
    <exclude name="ElseExpression" />
    <exclude name="MissingImport" />
</rule>
<rule ref="rulesets/controversial.xml" />
<rule ref="rulesets/design.xml" />
<rule ref="rulesets/naming.xml" />
<rule ref="rulesets/unusedcode.xml" />

Példakimenet:

PHPStan

A PHP jelenlegi statikus típus-támogatása hagy maga után kívánnivalót. A phpstan célja, hogy a következő szintre emelje ezt, hozzáadva olyan dolgokat, amik még nem a nyelv szerves részei (pl. generikus típusok) és magával hozza annak összes előnyét (karbantarthatóság, csapatmunka, jövőállóság, stb).

Honlap: https://phpstan.org/
Doksi: https://phpstan.org/user-guide/getting-started
Letöltés (PHAR): https://github.com/phpstan/phpstan/releases

Használat:

php phpstan.phar analyse -c phpstan.neon %%projektkonyvtar

A phpstan.neon tartalma:

parameters: level: max featureToggles: bleedingEdge: true skipCheckGenericClasses!: [] explicitMixedInUnknownGenericNew: true explicitMixedForGlobalVariables: true explicitMixedViaIsArray: true arrayFilter: true arrayUnpacking: true nodeConnectingVisitorCompatibility: false nodeConnectingVisitorRule: true disableCheckMissingIterableValueType: true strictUnnecessaryNullsafePropertyFetch: true looseComparison: true consistentConstructor: true checkUnresolvableParameterTypes: true readOnlyByPhpDoc: true phpDocParserRequireWhitespaceBeforeDescription: true runtimeReflectionRules: true notAnalysedTrait: true curlSetOptTypes: true listType: true missingMagicSerializationRule: true nullContextForVoidReturningFunctions: true unescapeStrings: true duplicateStubs: true invarianceComposition: true alwaysTrueAlwaysReported: true disableUnreachableBranchesRules: true varTagType: true closureDefaultParameterTypeRule: true newRuleLevelHelper: true instanceofType: true paramOutVariance: true allInvalidPhpDocs: true strictStaticMethodTemplateTypeVariance: true propertyVariance: true genericPrototypeMessage: true stricterFunctionMap: true excludePaths:

  • ../tests/*

Churn-php

A churn-php segít megállapítani, hogy mit érdemes refaktorálni. Ezt egy varázsképlettel számolja ki, ami figyelembe veszi a fájl változásának ütemét (git logból) illetve a komplexitását. Ezekből előállít egy számot, ami indikátora lehet annak, hogy mihez érdemes nyúlni. Összehasonlítva azzal, ha pl. a kód keveset változik vagy egyszerű, akkor fölösleges lenne a refaktoráláson gondolkodni.

Honlap: https://github.com/bmitch/churn-php
Doksi: https://github.com/bmitch/churn-php
Letöltés (PHAR): https://github.com/bmitch/churn-php/releases

Használat:

php churn.phar run --configuration=php-churn.yml projektkonyvtar

A php-churn.xml tartalma:

filesToShow: 100 minScoreToShow: null maxScoreThreshold: null parallelJobs: 16 commitsSince: '2000-01-01' fileExtensions:

  • php directoriesToScan:
  • projektkonyvtar vcs: git cachePath: .churn.cache

Példakimenet:

PhpDeprecationDetector

Ahogy a neve állítja: kiszúrja azokat a dolgokat, amik elavultak, kivezetésre kerültek vagy fognak kerülni, röviden: ami eltörik, ha az app újabb PHP-vel fut.

Honlap: https://wapmorgan.github.io/PhpDeprecationDetector/
Doksi: https://wapmorgan.github.io/PhpDeprecationDetector/
Letöltés (PHAR): https://github.com/wapmorgan/PhpDeprecationDetector/releases

Használat:

php phpdd-2.0.33.phar scan projektkonyvtar

Példakimenet:

Psalm - PHP Static Analysis Tool

A psalm-et a vimeo tartja karban, statikus kódelemzést csinál, ahogyan a többi említett eszköz is. Ez is figyelmet fordít a típusokra illetve "hibás kódot keres". A doksijában le van pontosan írva az összes elérhető szabály, a lenti konfiggal pedig a legszigorúbb ellenőrzést kaphatjuk.

Honlap: https://psalm.dev/
Doksi: https://psalm.dev/docs/
Letöltés (PHAR): https://github.com/vimeo/psalm/releases

Használat:

php psalm.phar --config=psalm.xml projektkonyvtar

A psalm.xml fájl tartalma:

<?xml version="1.0"?>

Példakimenet:

PHPLint

Általános linter eszköz PHP-hez.

Honlap: https://github.com/overtrue/phplint
Doksi: https://github.com/overtrue/phplint/blob/main/docs/usage/console.md
Letöltés (PHAR): https://github.com/overtrue/phplint/releases

Használat:

php D:\usr\prg\php\pear\phplint.phar -c qa/phplint.yml

A phplint.yml tartalma:

path: ./src jobs: 16 extensions:

  • php exclude:
  • tests warning: true memory-limit: -1 cache: ./work/phplint.cache

Példakimenet:

Phan - PHP Analyzer

A phan célja nem a helyesség ellenőrzése, hanem a hibák kimutatása. Rengeteg pluginje van, a típushibáktól a clean kód aspektusaiig elég sokmindent felölel. Van pl. olyan pluginje, ami azt ellenőrzi, hogy egy modernebb PHP verzióhoz készült kód hol törhet el, ha régebbi értelmezővel futtatjuk.

Honlap: https://github.com/phan/phan
Doksi: https://github.com/phan/phan/wiki
Letöltés (PHAR): https://github.com/phan/phan/releases

Fontos, hogy szükséges hozzá a php_ast extension a php-hez, amit innen lehet beszerezni: https://downloads.php.net/~windows/pecl/releases/ast/.

A php.ini-be pedig csak ennyi kell utána:

extension=ast

Használat:

php phan.phar -k phan.php -C -m verbose

A phan.php tartalma:

<?php declare(strict_types=1);

use Phan\Issue;

return [ 'target_php_version' => null, 'pretend_newer_core_functions_exist' => true, 'allow_missing_properties' => false, 'null_casts_as_any_type' => false, 'null_casts_as_array' => false, 'array_casts_as_null' => false, 'strict_method_checking' => true, 'strict_param_checking' => true, 'strict_property_checking' => true, 'strict_return_checking' => true, 'scalar_implicit_cast' => false, 'scalar_array_key_cast' => false, 'scalar_implicit_partial' => [], 'ignore_undeclared_variables_in_global_scope' => false, 'backward_compatibility_checks' => false, 'check_docblock_signature_return_type_match' => true, 'check_docblock_signature_param_type_match' => true, 'prefer_narrowed_phpdoc_param_type' => true, 'prefer_narrowed_phpdoc_return_type' => true, 'analyze_signature_compatibility' => true, 'allow_method_param_type_widening' => false, 'guess_unknown_parameter_type_using_default' => false, 'phpdoc_type_mapping' => [], 'dead_code_detection' => false, 'unused_variable_detection' => true, 'force_tracking_references' => false, 'warn_about_redundant_use_namespaced_class' => true, 'quick_mode' => false, 'simplify_ast' => true, 'enable_class_alias_support' => false, 'generic_types_enabled' => true, 'warn_about_undocumented_throw_statements' => true, 'warn_about_undocumented_exceptions_thrown_by_invoked_functions' => true, 'exception_classes_with_optional_throws_phpdoc' => [], 'max_literal_string_type_length' => 1000, 'consistent_hashing_file_order' => false, 'globals_type_map' => [], 'minimum_severity' => Issue::SEVERITY_LOW, 'suppress_issue_types' => [], 'whitelist_issue_types' => [], 'file_list' => [], 'exclude_file_regex' => '#tests/.*#', 'enable_include_path_checks' => true, 'include_paths' => ['.'], 'warn_about_relative_include_statement' => true, 'exclude_file_list' => [], 'processes' => 1, 'directory_list' => ['src'], 'analyzed_file_extensions' => ['php'], 'exclude_analysis_directory_list' => ['vendor/'], 'skip_slow_php_options_warning' => true, 'autoload_internal_extension_signatures' => [], 'ignore_undeclared_functions_with_known_signatures' => false, 'plugin_config' => ['php_native_syntax_check_max_processes' => 16], 'plugins' => [ 'AddNeverReturnTypePlugin', 'AlwaysReturnPlugin', 'AvoidableGetterPlugin', 'ConstantVariablePlugin', 'DemoPlugin', 'DeprecateAliasPlugin', 'DollarDollarPlugin', 'DuplicateArrayKeyPlugin', 'DuplicateConstantPlugin', 'DuplicateExpressionPlugin', 'EmptyMethodAndFunctionPlugin', 'EmptyStatementListPlugin', 'FFIAnalysisPlugin', 'HasPHPDocPlugin', 'InlineHTMLPlugin', 'InvalidVariableIssetPlugin', 'InvokePHPNativeSyntaxCheckPlugin', 'LoopVariableReusePlugin', 'MoreSpecificElementTypePlugin', 'NoAssertPlugin', 'NonBoolBranchPlugin', 'NonBoolInLogicalArithPlugin', 'NotFullyQualifiedUsagePlugin', 'NumericalComparisonPlugin', 'PhanSelfCheckPlugin', 'PHPDocInWrongCommentPlugin', 'PHPDocRedundantPlugin', 'PHPDocToRealTypesPlugin', 'PHPUnitAssertionPlugin', 'PHPUnitNotDeadCodePlugin', 'PossiblyStaticMethodPlugin', 'PreferNamespaceUsePlugin', 'PregRegexCheckerPlugin', 'PrintfCheckerPlugin', 'RedundantAssignmentPlugin', 'RemoveDebugStatementPlugin', 'SimplifyExpressionPlugin', 'SleepCheckerPlugin', 'StaticVariableMisusePlugin', 'StrictComparisonPlugin', 'StrictLiteralComparisonPlugin', 'SuspiciousParamOrderPlugin', 'UnknownClassElementAccessPlugin', 'UnknownElementTypePlugin', 'UnreachableCodePlugin', 'UnsafeCodePlugin', 'UnusedSuppressionPlugin', 'UseReturnValuePlugin', 'WhitespacePlugin', ], ];

Példakimenet:

PHP Copy/Paste Detector (PHPCPD)

Ahogy a neve mondja: megkeresi azokat a kódrészleteket, amik többszörözve vannak. A projekt elvileg már nem aktív.

Honlap: https://github.com/sebastianbergmann/phpcpd
Doksi: https://github.com/sebastianbergmann/phpcpd
Letöltés (PHAR): https://phar.phpunit.de/phpcpd.phar

Használat:

php phpcpd.phar --fuzzy projektkonyvtar

Példakimenet:

Bónusz

A fenti eszközök futtatása akár kézi, akár automatizált, igénybe vesz egy kis időt. Viszont, ha hasonló funkcionalitást szeretnénk, mint pl. a jest-nél a "watch mode" (az egyik JavaScript unit-testing eszköz), akkor igénybe vehetjük a "nodemon", vagyis a node monitor nevű toolt.

Telepítés:

npm install -g nodemon

Ezzel elérhetővé válik a nodemon parancs, amit változatos módon felparaméterezhetünk, mint például:

nodemon --watch src --watch tests --ext php --exec "php phpunit.phar --no-coverage --configuration qa/phpunit.xml tests || exit 0" nodemon --watch src --watch qa --ext php,xml --exec "php D:\usr\prg\php\pear\phpcs.phar -s --standard=qa/phpcs.xml src | head -n 30" nodemon --watch src --watch qa --ext php,xml --exec "php D:\usr\prg\php\pear\phpmd.phar src text qa/phpmd.xml || exit 0"

Ezzel a módszerrel a megadott könyvtárakban lévő szintén megadott kiterjesztésű fájlokat figyeli, és amint bármelyik változik, újraindítja az --exec kapcsolóban adott parancsot. Azaz, minden mentés után automatikusan lefuttatja azt az eszközt, ami alapján éppen kódot csiszolunk.

Egyéb hasznos eszközök