symfony/polyfill-php85
Symfony Polyfill for PHP 8.5 features on older runtimes. Adds get_error_handler/get_exception_handler, NoDiscard attribute, array_first/array_last, DelayedTargetValidation, Filter exceptions, and locale_is_right_to_left. MIT licensed.
## Getting Started
### Minimal Steps
1. **Installation**:
Update the package via Composer in your Laravel project:
```bash
composer require symfony/polyfill-php85:^1.38.0
No additional configuration is required—it remains a drop-in polyfill.
First Use Case:
Replace legacy array operations with PHP 8.5’s array_first and array_last:
// Before (PHP < 8.5)
$firstItem = array_slice($array, 0, 1)[0] ?? null;
// After (with polyfill)
$firstItem = array_first($array) ?? null;
Verify Compatibility: Check your PHP version with:
php -r "echo PHP_VERSION;"
Ensure it’s PHP 7.4–8.4 (the target range for this polyfill). Note: This release fixes a bug related to PCRE < 10.44, which may affect grapheme cluster splitting in edge cases (e.g., multibyte strings).
Array Operations:
Replace manual array slicing with array_first/array_last for cleaner code:
// Find first matching item in a collection
$user = array_first($users, fn($u) => $u['active']);
Note: Ensure your PCRE version is ≥ 10.44 if working with multibyte strings (e.g., emojis, CJK characters).
Attribute-Based Features:
Use #[NoDiscard] to enforce return value usage (PHP 8.5+ attribute):
#[NoDiscard]
public function getUserData(): array {
return $this->user->toArray();
}
Note: Requires PHP 8.0+ for attributes.
Error/Exception Handling: Access global handlers dynamically:
$handler = get_error_handler();
$exceptionHandler = get_exception_handler();
Filter Validation: Enable strict filter validation with exceptions:
$value = filter_var($input, FILTER_VALIDATE_EMAIL);
if ($value === false) {
throw new Filter\FilterException('Invalid email');
}
Gradual Adoption:
array_first, locale_is_right_to_left).NoDiscard, DelayedTargetValidation) in new code only.#[NoDiscard] to minimize disruption.Testing Strategy:
array_first with emojis):
$emojis = ['😀', '😃', '😄'];
$firstEmoji = array_first($emojis); // Should return '😀'
$this->assertEquals('😀', $firstEmoji);
NoDiscard behavior:
$this->expectException(NoReturnInVoidContext::class);
$this->getUserData(); // Should trigger warning if return value is discarded
Laravel Integration:
array_first with Laravel’s Collection methods:
$firstActive = $users->firstWhere('active', true);
// Equivalent to: array_first($users->toArray(), fn($u) => $u['active'])
FilterException:
use Symfony\Component\Validator\Constraints\Filter;
$validator = Validator::make(['email' => $input], [
'email' => [new Filter(['filter' => FILTER_VALIDATE_EMAIL])]
]);
Composer Scripts: Add a script to check for unused polyfills post-upgrade:
{
"scripts": {
"post-upgrade:check": "grep -r \"symfony/polyfill-php85\" app/ || echo \"Polyfill found—remove after PHP 8.5 upgrade\""
}
}
IDE Support:
Add PHPDoc stubs for better autocompletion (e.g., in stubs/php85.php):
/** @return mixed */
function array_first(array $array, ?callable $callback = null, mixed $default = null): mixed {}
CI/CD: Test polyfills on all supported PHP versions in your CI pipeline:
# .github/workflows/test.yml
jobs:
test:
strategy:
matrix:
php: [7.4, 8.0, 8.1, 8.2, 8.3, 8.4]
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- run: composer install
- run: phpunit
Note: Add a step to verify PCRE version if multibyte strings are critical:
- run: php -r "echo preg_version();" | grep -q "10.44" || exit 1
False Positives with NoDiscard:
NoDiscard:
# phpstan.neon
parameters:
level: 8
checkReturnTypeInNonVoidFunctions: true
checkReturnTypeInVoidFunctions: true
Attribute System Requirements:
NoDiscard and DelayedTargetValidation require PHP 8.0+ (for attributes).if (version_compare(PHP_VERSION, '8.0.0') < 0) {
throw new RuntimeException('PHP 8.0+ required for attributes');
}
locale_is_right_to_left Extension Dependency:
intl extension is missing.if (!extension_loaded('intl')) {
throw new RuntimeException('Intl extension required for locale_is_right_to_left');
}
Performance Overhead:
php -d memory_limit=-1 bench.php # Compare native vs. polyfilled `array_first`
Deprecation Risk:
"symfony/polyfill-php85": "^1.38.0"
PCRE Version Bug (v1.38.0):
pecl install pcre-10.44).if (version_compare(preg_version(), '10.44') < 0) {
throw new RuntimeException('PCRE 10.44+ required for grapheme cluster support');
}
Verify Polyfill Registration: Check if functions are loaded:
var_dump(function_exists('array_first')); // Should return true
Attribute Reflection Issues:
If #[NoDiscard] doesn’t work, ensure:
#[Attribute] (PHP 8.1+ syntax).FilterException Edge Cases:
FilterFailedException may not bubble up in all contexts.try {
$value = filter_var($input, FILTER_VALIDATE_EMAIL);
if ($value === false) {
throw new Filter\FilterException('Invalid input');
}
} catch (Filter\FilterException $e) {
// Handle
}
**Locale
How can I help you explore Laravel packages today?