lumnd/plato-coding-standard

The PHP_CodeSniffer standard behind PlatoPHP: PSR-12 minus the naming and brace conventions it does not follow, plus the sniff that enforces them

Maintainers

Package info

github.com/lumnd/plato-coding-standard

Homepage

Type:phpcodesniffer-standard

pkg:composer/lumnd/plato-coding-standard

Transparency log

Statistics

Installs: 16

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-08-05 14:57 UTC

This package is not auto-updated.

Last update: 2026-08-07 04:25:37 UTC


README

The PHP_CodeSniffer standard behind PlatoPHP: PSR-12 minus the naming and brace conventions it deliberately does not follow, plus the sniff that enforces them.

简体中文

Excluding a PSR-12 sniff only makes both spellings legal. A tree that excludes Squiz.Classes.ValidClassName does not require snake_case — it stops having an opinion, and the two styles drift apart inside one repository with nothing to say so. This package is both halves: the exclusions, and ProjectConventionsSniff, which rejects the PSR-12 spelling in their place.

It exists because the exclusions were pasted into three repositories and drifted. One copy was missing Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace and still errored on wrapped signatures; one carried a bug in the sniff that the others had already fixed; one shipped a standard named after the wrong project. A convention with three copies has no authority.

Requirements

PHP 8.0+
PHP_CodeSniffer ^3.11

Install

composer require --dev lumnd/plato-coding-standard

This package's type is phpcodesniffer-standard, which is what dealerdirect/phpcodesniffer-composer-installer scans installed packages for. It writes this directory into phpcs's installed_paths, and that is what lets a consumer name the standard instead of spelling out a path into vendor/. The plugin is a hard requirement rather than a suggestion: without it the standard installs but never registers, and the ruleset fails to resolve the name with an error that does not say why.

Composer will not run a plugin a project has not allowed, so the consuming composer.json needs:

{
    "config": {
        "allow-plugins": {
            "dealerdirect/phpcodesniffer-composer-installer": true
        }
    }
}

Use

A consumer's phpcs.xml keeps what is genuinely its own — which directories to check, which generated output to skip — and refs the standard by name:

<?xml version="1.0"?>
<ruleset name="MyProject">
    <file>src</file>
    <exclude-pattern>src/generated/*</exclude-pattern>

    <rule ref="PlatoPHP"/>
</ruleset>

Everything else — the PSR-12 base, the exclusions, the 120 column soft limit, Allman braces for functions and closures, ignore_warnings_on_exit — comes with the name.

What it enforces

Code Rule
ClassName Classes, interfaces, traits and enums are lowercase snake_case
FunctionName Functions and methods are lowercase snake_case
MethodVisibilityPrefix private / protected methods start with _; public ones do not
PropertyVisibilityPrefix The same for properties
SpaceAfterOpenParenthesis SpaceBeforeCloseParenthesis One space inside control parentheses: if ( $ready )
ControlBraceLine A control structure's opening brace is on the line after the signature
ContinuationLine else, catch, finally and do-while start on a new line, not } else

All codes are prefixed PlatoPHP.Style.ProjectConventions.

Zero errors is the gate. The 120 column limit is a warning, which PSR-12 requires it to be, and ignore_warnings_on_exit keeps it out of the exit code.

What phpcbf fixes, and what it will not

The whitespace rules are fixable — composer style:fix rewrites if($x){ into the convention, including moving else and catch onto their own lines. Two cases are reported without a fix on purpose, because closing the gap would change more than formatting:

  • A condition wrapped over several lines. Turning the newline into a space would join two lines rather than insert one space.
  • A comment between the signature and the brace. It belongs on one side of the break and the sniff does not know which.

The naming rules are never fixable. Renaming a method means finding its callers, which is not something a formatter should attempt.

Exceptions

Magic methods are exempt from both naming rules. __toString, __callStatic and __debugInfo are camelCase and __construct is public while starting with an underscore — the engine owns those spellings, so they are not a project's to choose. The sniff carries PHP's list and skips it.

A name a third party dictates is not relaxed, it is suppressed where it happens. A class implementing PSR-16 must provide getMultiple(); renaming it would mean not implementing the interface. Mark that class rather than widening the rule for everybody:

// phpcs:disable PlatoPHP.Style.ProjectConventions.FunctionName -- PSR-16 owns these names.
public function getMultiple(iterable $keys, mixed $default = null): iterable
// phpcs:enable PlatoPHP.Style.ProjectConventions.FunctionName

ProjectConventionsSniff does the same to itself: PHPCS's loader requires a StudlyCaps class name ending in Sniff, so the file carries a one-line phpcs:ignore for its own ClassName rule.

Tests

composer test

Runs the standard over tests/fixtures and asserts what fires, what does not, what phpcbf rewrites, that a second fix run changes nothing, and that the two unfixable cases come back byte-identical. There is no test framework: a coding standard is a dependency of everybody else's test setup, and giving it one of its own buys a version conflict for nothing.

composer style

Checks this package against the standard it ships.

License

MIT.