pixelfederation / coding-standards
Standardized PHPCS sniffs for backend developers.
Package info
github.com/pixelfederation/coding-standards
Type:phpcodesniffer-standard
pkg:composer/pixelfederation/coding-standards
Requires
- php: ^8.4
- slevomat/coding-standard: ^8.31.1
- squizlabs/php_codesniffer: ^4.0.4
Requires (Dev)
- ext-dom: *
- ext-libxml: *
- ergebnis/composer-normalize: ^2.56
- friendsofphp/php-cs-fixer: ^3.95.28
- friendsoftwig/twigcs: ^6.6.1
- nikic/php-parser: ^5.9
- php-parallel-lint/php-parallel-lint: ^1.4
- phpro/grumphp: ^2.26
- phpstan/phpstan: ^2.3.5
- phpunit/phpunit: ^13.4.6
Suggests
- friendsoftwig/twigcs: Required to use the Pixel Federation Twigcs ruleset (^6.6.1).
Provides
None
Conflicts
None
Replaces
None
README
This package provides the shared PHP_CodeSniffer rules, Twigcs ruleset, and GrumPHP tasks used by Pixel Federation projects.
Requirements
- PHP 8.4 or 8.5
- PHP_CodeSniffer 4
- Slevomat Coding Standard 8
Installation
composer require --dev pixelfederation/coding-standards
PHP_CodeSniffer
The package contains rulesets for PHP 8.4 and PHP 8.5. Each PHP version has a standard ruleset and a less restrictive ruleset for projects that do not use DDD:
| PHP | Standard ruleset | Non-DDD ruleset |
|---|---|---|
| 8.4 | phpcs.ruleset.84.xml |
phpcs.ruleset.84.non-ddd.xml |
| 8.5 | phpcs.ruleset.85.xml |
phpcs.ruleset.85.non-ddd.xml |
Create phpcs.ruleset.xml in the project root:
<?xml version="1.0" encoding="UTF-8"?> <ruleset name="PixelFederation"> <description>Project coding standard.</description> <exclude-pattern>tests/</exclude-pattern> <rule ref="vendor/pixelfederation/coding-standards/phpcs.ruleset.84.xml"> <!-- Project-specific exclusions can be added here. --> <exclude name="SlevomatCodingStandard.Functions.FunctionLength"/> </rule> </ruleset>
Run the checks with:
vendor/bin/phpcs --standard=phpcs.ruleset.xml src
Automatically fix supported violations with:
vendor/bin/phpcbf --standard=phpcs.ruleset.xml src
Alphabetical array keys
The standard checks multi-line associative arrays with
PixelFederationCodingStandard.Arrays.AlphabeticallySortedByKeys. Arrays used as the direct value of a choices
key are excluded because their order controls the order shown in the user interface. The containing array is still
checked.
Projects can replace the configured list of ignored parent keys in their ruleset:
<rule ref="PixelFederationCodingStandard.Arrays.AlphabeticallySortedByKeys"> <properties> <property name="ignoredParentKeys" type="array"> <element value="choices"/> <element value="steps"/> </property> </properties> </rule>
Without ignoredParentKeys, the sniff behaves like Slevomat's
SlevomatCodingStandard.Arrays.AlphabeticallySortedByKeys sniff and checks every multi-line associative array.
The complete Slevomat sniff documentation is available in the Slevomat Coding Standard repository.
Twigcs
The optional Twigcs ruleset extends the official Twig standard with HTML layout checks. Install Twigcs in the consuming project:
composer require --dev friendsoftwig/twigcs:^6.6.1
Select the ruleset directly:
vendor/bin/twigcs templates --ruleset='PixelFederation\CodingStandards\Twigcs\Ruleset\PixelFederation'
Or configure the standard GrumPHP twigcs task:
grumphp: tasks: twigcs: path: templates/ ruleset: 'PixelFederation\CodingStandards\Twigcs\Ruleset\PixelFederation'
Write every HTML attribute on a separate line, even when there is only one. Place the opening tag terminator
(> or />), content, and closing tag on separate lines:
<span class="label" aria-hidden="true" > {{- label -}} </span>
Elements without attributes can keep their opening tag on one line, such as <span> or <br>. The rule supports
Twig expressions, conditional attributes, and attribute helpers. It ignores HTML and Twig comments and preserves
the literal bodies of pre, textarea, script, and style. Their opening attributes are still checked.
By default, strong, em, b, i, and small can contain inline content and have their closing tag on the
same line. Their attributes still follow the layout checks. Use inlineTags to replace this list
(case-insensitive), or pass [] to disable these exceptions.
GrumPHP selects a ruleset class; it does not expose inlineTags as a YAML option. To customize the list,
create an autoloadable project ruleset, for example src/CodingStandard/TwigRuleset.php in a project with
the App\ namespace mapped to src/:
<?php declare(strict_types=1); namespace App\CodingStandard; use FriendsOfTwig\Twigcs\Rule\RuleInterface; use FriendsOfTwig\Twigcs\Ruleset\Official; use FriendsOfTwig\Twigcs\Validator\Violation; use Override; use PixelFederation\CodingStandards\Twigcs\Rule\HtmlLayoutRule; final class TwigRuleset extends Official { /** * @return list<RuleInterface> */ #[Override] public function getRules(): array { return [ ...array_values(parent::getRules()), new HtmlLayoutRule( Violation::SEVERITY_ERROR, inlineTags: ['strong', 'em', 'b', 'i', 'small', 'span'], ), ]; } }
The shared PixelFederation ruleset is final, so the custom ruleset extends Official and adds its own
configured HtmlLayoutRule. This preserves the official Twig checks.
Select the custom ruleset in grumphp.yml:
grumphp: tasks: twigcs: path: templates/ ruleset: 'App\CodingStandard\TwigRuleset'
Keep the shared PixelFederation ruleset configuration above when the default inline tags are sufficient.
Preserve meaningful whitespace when formatting inline content; use Twig whitespace control selectively. The rule does not validate indentation depth, HTML validity, or rendered whitespace semantics, and does not fix violations automatically. Enable it explicitly when the project's templates are ready for these checks.
PHP-CS-Fixer
The package ships a .php-cs-fixer.dist.php with a minimal, explicit rule set (no bundled rule sets like
@Symfony), so every enabled rule is visible directly in the config.
Forbidding use function/use const imports of global names
SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly (see PHP_CodeSniffer) always treats a
global function or constant that is imported via use function/use const and referenced by its short name as
correct, regardless of allowFallbackGlobalFunctions/allowFallbackGlobalConstants. It has no option to forbid
that import style while still allowing the unqualified fallback call, for example:
use function strlen; strlen('foo'); // always considered OK by ReferenceUsedNamesOnly, even if you want to forbid the import.
To actually forbid importing global (root-namespace) functions or constants and require the plain, unqualified
call instead, use PHP-CS-Fixer's global_namespace_import rule with import_functions/import_constants set to
false:
'global_namespace_import' => [ 'import_classes' => null, // leave classes as-is; handled by ReferenceUsedNamesOnly instead. 'import_constants' => false, // removes `use const` imports of global constants. 'import_functions' => false, // removes `use function` imports of global functions. ],
With this rule, running vendor/bin/php-cs-fixer fix removes a use function strlen;/use const PHP_EOL; import
and rewrites any fully qualified call (\strlen()) to the unqualified form, keeping only the bare, unqualified
call style. The rule only affects names in the global (root) namespace — use function Foo\Bar\baz; for a
namespaced function is left untouched.
Before enabling this project-wide, run vendor/bin/php-cs-fixer fix --dry-run --diff (and the full GrumPHP suite)
first, since existing code may rely on the imported style and would need to be fixed.
GrumPHP
Custom tasks require the Composer installation of GrumPHP. They do not work with phpro/grumphp-shim when
parallel execution is enabled because PHAR task classes cannot be serialized.
Register the extension in the project's grumphp.yml:
grumphp: extensions: - PixelFederation\CodingStandards\GrumPHP\ExtensionLoader
Doctrine ORM mapping validation
grumphp: tasks: doctrine_schema_validate: console_path: bin/console em: default skip_mapping: false skip_property_types: false skip_sync: false triggered_by: [php, xml, yml]
For multiple entity managers, configure multiple tasks using the shared task implementation:
grumphp: tasks: doctrine_schema_validate_application: em: application metadata: task: doctrine_schema_validate doctrine_schema_validate_reporting: em: reporting metadata: task: doctrine_schema_validate
Options:
| Option | Default | Description |
|---|---|---|
console_path |
bin/console |
Path to the Symfony console. |
em |
null |
Entity manager name. Requires Doctrine ORM 3 or newer. |
skip_mapping |
false |
Skip mapping validation. |
skip_property_types |
null |
Skip the Doctrine property type check. Requires Doctrine ORM 3 or newer. |
skip_sync |
false |
Skip the database synchronization check. |
triggered_by |
[php, xml, yml] |
File extensions that trigger the task. |
Composer install check
This task checks whether changes to Composer files require dependencies to be installed again.
grumphp: tasks: composer_install_check: script: ./vendor/pixelfederation/coding-standards/bin/composer_install_check.sh ignore_patterns: [] triggered_by: [json, lock, php, xml, yaml, yml] whitelist_patterns: [] metadata: priority: 900
Options:
| Option | Default | Description |
|---|---|---|
script |
./vendor/pixelfederation/coding-standards/bin/composer_install_check.sh |
Path to the check script. |
ignore_patterns |
[] |
Patterns excluded from the check. |
triggered_by |
[json, lock, php, xml, yaml, yml] |
File extensions that trigger the task. |
whitelist_patterns |
[] |
Patterns limiting which changed files are checked. |
PHPStan Extended
This task extends the standard PHPStan task and splits files into smaller chunks to avoid operating-system command length limits.
grumphp: tasks: phpstan_extended: autoload_file: ~ chunk_size: 1000 configuration: phpstan.neon force_patterns: [] ignore_patterns: [] level: max memory_limit: "-1" triggered_by: [php] use_grumphp_paths: true
chunk_size determines the maximum number of files processed by one PHPStan execution.
XMLLint Extended
This task extends the standard XMLLint task with DTD, XInclude, and XML Schema validation. It requires the dom
and libxml PHP extensions.
grumphp: tasks: xmllint_extended: dtd_validation: false ignore_patterns: [] load_from_net: false scheme_validation: false triggered_by: [xml] x_include: false
When schema validation is enabled, the linter supports xsi:noNamespaceSchemaLocation and multi-namespace
xsi:schemaLocation documents. Imported namespaces are resolved by the root document schema.