PHP PER: The Evolution Beyond PSR-12 Coding Standards
PHP Evolving Recommendations (PER) represent a fundamental shift in how the PHP community approaches standards. Unlike the static PSRs, PERs are designed to evolve with the language, ensuring standards stay relevant as PHP continues its rapid modernization.
What is PHP PER?
A PHP Evolving Recommendation is a "meta document accompanied by one or more artifacts that are set to evolve over time with multiple releases." This evolutionary approach addresses a critical limitation of the PSR system: once accepted, PSRs are essentially frozen in time.
Currently, there's only one active PER: the PER Coding Style 3.0, which extends, expands, and ultimately replaces PSR-12. But the implications go far beyond just coding style.
The Problem with Static Standards
When PSR-12 was accepted in 2019, PHP 7.3 was the latest version. Since then, we've seen:
- Union Types (PHP 8.0)
- Enumerations (PHP 8.1)
- Readonly Properties (PHP 8.1)
- Intersection Types (PHP 8.1)
- Property Hooks (upcoming in PHP 8.4)
PSR-12 couldn't provide guidance for these features because they didn't exist. Enter PER: a living standard that can adapt as PHP evolves.
PER vs PSR: The Key Differences
Aspect | PSR (PHP Standard Recommendation) | PER (PHP Evolving Recommendation) |
---|---|---|
Mutability | Immutable once accepted | Designed to evolve with multiple releases |
Update Process | Requires new PSR to supersede old one | Can be updated through defined workflow |
Scope | Fixed at time of acceptance | Expands to cover new language features |
Leadership | Working group disbanded after acceptance | Maintains active Editor and Sponsor |
Community Input | Limited to initial draft period | Ongoing through evolution process |
What's New in PER Coding Style 3.0?
1. Modern Type Declarations
PER addresses the explosion of type system features in modern PHP:
// Union types (PHP 8.0+)
public function process(int|string $value): void {}
// Intersection types (PHP 8.1+)
public function handle(Countable&Traversable $items): void {}
// Complex compound types with proper formatting
function complex(
array
|(ArrayAccess&Traversable)
|(Traversable&Countable) $input
): ArrayAccess&Traversable {
// Implementation
}
2. Attributes (Annotations)
PHP 8 Attributes get comprehensive formatting rules:
// Single attribute
#[Route('/api/users')]
class UserController {}
// Multiple attributes
#[
Route('/api/users'),
Middleware('auth'),
Cache(ttl: 3600)
]
class UserController {}
// Inline for simple cases
class User {
#[Required] #[Email]
public string $email;
}
3. Enumerations
Clear guidelines for PHP 8.1 enums:
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
public function isActive(): bool
{
return $this === self::Published;
}
}
4. Property Hooks (PHP 8.4+)
Forward-looking support for upcoming features:
class User
{
public string $name {
get => $this->firstName . ' ' . $this->lastName;
set => [$this->firstName, $this->lastName] = explode(' ', $value, 2);
}
}
5. Trailing Commas
Mandatory trailing commas in multi-line contexts:
// Required in multi-line arrays
$config = [
'host' => 'localhost',
'port' => 3306,
'database' => 'app', // ← Required trailing comma
];
// Required in multi-line function calls
$result = processSomething(
$firstArgument,
$secondArgument,
$thirdArgument, // ← Required trailing comma
);
Enforcing PER with QA Tools
PHP-CS-Fixer: The Gold Standard
PHP-CS-Fixer already includes PER support. The Symfony ruleset incorporates PER Coding Style by default:
// .php-cs-fixer.php
<?php
$finder = PhpCsFixerFinder::create()
->in(__DIR__)
->exclude('vendor');
return (new PhpCsFixerConfig())
->setRules([
'@Symfony' => true, // Includes @PER-CS2.0
'@PER-CS' => true, // Explicit PER compliance
'declare_strict_types' => true,
'void_return' => true,
])
->setFinder($finder)
->setRiskyAllowed(true);
Run with:
vendor/bin/php-cs-fixer fix --dry-run --diff # Check changes
vendor/bin/php-cs-fixer fix # Apply fixes
PHPStan Integration
While PHPStan focuses on static analysis, you can enforce some PER conventions:
# phpstan.neon
parameters:
level: 9
strictRules:
strictCalls: true
strictProperties: true
# Enforce modern PHP features
phpVersion: 80300 # Minimum PHP 8.3
includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
Composer Scripts
Integrate into your workflow:
{
"scripts": {
"check-style": "php-cs-fixer fix --dry-run --diff",
"fix-style": "php-cs-fixer fix",
"analyse": "phpstan analyse",
"qa": [
"@check-style",
"@analyse"
]
}
}
CI/CD Integration
GitHub Actions example:
name: Code Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: php-cs-fixer, phpstan
- name: Check PER Compliance
run: php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php
- name: Static Analysis
run: phpstan analyse
A Brief History of PHP Standards
The PSR Era (2009-Present)
- 2009: PHP-FIG formed
- 2010: PSR-0 (Autoloading) - The first PSR
- 2012: PSR-1 & PSR-2 (Basic & Coding Style)
- 2013: PSR-4 (Improved Autoloading)
- 2019: PSR-12 (Extended Coding Style)
The Problem Emerges
As PHP accelerated its release cycle with annual major versions, the static nature of PSRs became problematic. PSR-12 couldn't be updated for new syntax, leading to:
- Fragmented community standards
- Tool-specific interpretations
- Inconsistent codebases
Enter PER (2022-2023)
PHP-FIG introduced the PER Workflow Bylaw, creating a new category of living standards. PER Coding Style 2.0 was released in April 2023, followed by 3.0 in July 2023. Key innovations:
- Active Maintainership: Each PER has an Editor and Sponsor
- Version Control: PERs use semantic versioning
- Community Evolution: Regular updates based on language changes
The Future of PHP Standards
Expected PER Evolution
As PHP continues to evolve, PER Coding Style will likely address:
- Pattern Matching: If PHP adds pattern matching
- Generics: Should generics finally arrive
- Async/Await: For potential async PHP features
- Package Visibility: New access modifiers
Potential New PERs
The community is discussing PERs for:
- Documentation Standards: Evolving PHPDoc alternatives
- Testing Conventions: Modern PHPUnit/Pest practices
- API Design: RESTful and GraphQL standards
- Security Practices: Evolving security recommendations
Tool Ecosystem Alignment
Major tools are aligning with PER:
- PHP_CodeSniffer: Adding PER rulesets
- Psalm: Considering PER-aware analysis
- IDEs: PhpStorm and VS Code updating formatters
Practical Migration Guide
From PSR-12 to PER
Migrating is straightforward with proper tooling:
# 1. Install/update PHP-CS-Fixer
composer require --dev friendsofphp/php-cs-fixer
# 2. Create configuration
cat > .php-cs-fixer.php << 'EOF'
<?php
return (new PhpCsFixer\Config())
->setRules([
'@PER-CS' => true,
// Your additional rules
])
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude('vendor')
);
EOF
# 3. Check what will change
vendor/bin/php-cs-fixer fix --dry-run --diff
# 4. Apply changes
vendor/bin/php-cs-fixer fix
# 5. Commit
git add .
git commit -m "Migrate from PSR-12 to PER Coding Style"
Common Migration Issues
- Trailing commas: Now required in multi-line contexts
- Type declarations: May need reformatting
- Attributes: New formatting rules apply
Conclusion
PHP Evolving Recommendations represent a maturation of the PHP community's approach to standards. By acknowledging that languages evolve and standards must evolve with them, PER provides a sustainable path forward.
For teams already using PHP-CS-Fixer with Symfony rules, you're likely already PER-compliant. For others, the migration is painless with modern tooling.
The key insight: PER isn't just about coding style—it's about creating living standards that grow with PHP. As PHP continues its renaissance with performance improvements, type safety, and modern features, PER ensures our standards keep pace. With PHP 8.5 on the horizon and new features constantly being added, PER's evolutionary approach is more important than ever.