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.
Installation:
composer require symfony/polyfill-php85
Add to composer.json under require or require-dev depending on your needs.
First Use Case: Replace legacy array operations with PHP 8.5+ functions:
// Before (PHP <8.5)
$firstItem = array_slice($array, 0, 1)[0] ?? null;
// After (with polyfill)
$firstItem = array_first($array) ?? null;
Verify Compatibility:
Check PHP version support in composer.json:
"config": {
"platform": {
"php": "8.1" // or your current version
}
}
array_first, array_last, NoDiscard, and FilterException for immediate value./**
* @return mixed
*/
function array_first(array $array): mixed { /* ... */ }
Array Operations:
// Find first/last item in a collection
$first = array_first($users->toArray());
$last = array_last($users->toArray());
// Replace Laravel Collection methods
$user = $users->first(fn($u) => $u->isActive());
// vs.
$user = array_first(array_filter($users->toArray(), fn($u) => $u->isActive()));
Attribute-Based Features:
// Enforce non-discardable return values
#[NoDiscard]
public function getCriticalData(): string {
return $this->data;
}
// Delayed validation (e.g., for DTOs)
#[DelayedTargetValidation]
public function setName(string $name) {
$this->name = $name;
}
Filter Exception Handling:
// Throw exceptions on filter failures
try {
$cleaned = filter_var($input, FILTER_VALIDATE_EMAIL);
} catch (FilterException $e) {
// Handle invalid input
}
Error/Exception Inspection:
// Debug custom handlers
$handler = get_exception_handler();
if ($handler instanceof Closure) {
// Log or inspect handler logic
}
Incremental Modernization:
array_first/array_last in non-critical modules (e.g., admin panels).NoDiscard in new classes to catch bugs early.FilterException in validation layers (e.g., FormRequests) before full PHP upgrade.Laravel-Specific Patterns:
array_first to simplify whereIn clauses:
$ids = array_first($request->input('ids', []));
User::where('id', $ids)->update(...);
$this->getMockBuilder('Symfony\Component\Polyfill\Php85\array_first')
->disableOriginalConstructor()
->getMock();
CI/CD Integration:
jobs:
test:
runs-on: ubuntu-latest
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
Collection::toArray() for seamless integration:
$first = array_first($collection->toArray());
FilterException in FormRequest validation:
public function rules(): array {
return [
'email' => ['required', 'email:rfc,dns,spoof'],
];
}
public function withValidator($validator) {
$validator->after(function ($validator) {
if ($validator->errors()->any()) {
throw new FilterException('Validation failed');
}
});
}
use Symfony\Component\Polyfill\Php85\NoDiscard;
#[NoDiscard]
#[HasFactory]
class User {
// ...
}
IDE False Positives:
vendor/autoload.php:
// In a helper file (e.g., `stubs.php`)
declare(strict_types=1);
function array_first(array $array): mixed { return null; }
function array_last(array $array): mixed { return null; }
Attribute Reflection Issues:
NoDiscard or DelayedTargetValidation attributes are ignored.phpstan.neon:
parameters:
attributes:
knownAttributes:
- Symfony\Component\Polyfill\Php85\NoDiscard
- Symfony\Component\Polyfill\Php85\DelayedTargetValidation
locale_is_right_to_left Requires intl:
false or errors.intl extension:
pecl install intl
Or enable in php.ini:
extension=intl
FilterException Changes Error Flow:
filter_var calls now throw exceptions.try {
$cleaned = filter_var($input, FILTER_VALIDATE_EMAIL);
} catch (FilterException $e) {
$this->fail('Invalid email format.');
}
Polyfill Removal Post-Upgrade:
composer post-update script:
"scripts": {
"post-update": "php -r \"if (file_exists(__DIR__.'/vendor/symfony/polyfill-php85') && version_compare(PHP_VERSION, '8.5.0') >= 0) echo 'WARNING: polyfill-php85 is unused after PHP 8.5 upgrade.\\n';\""
}
Verify Polyfill Registration:
// Check if polyfill is loaded
if (!function_exists('array_first')) {
echo 'Polyfill not loaded. Run `composer dump-autoload`.';
}
Attribute Debugging:
// Inspect attributes at runtime
$reflection = new ReflectionClass(User::class);
$attributes = $reflection->getAttributes(NoDiscard::class);
Filter Exception Handling:
// Log filter failures
set_error_handler(function ($errno, $errstr) {
if (strpos($errstr, 'filter') !== false) {
Log::error("Filter error: {$errstr}");
}
});
PHP Version Mismatches:
composer.json enforces a higher PHP version than your server.platform config:
"config": {
"platform": {
"php": "8.1"
}
}
PCRE Version Warnings:
grapheme_* functions fail with PCRE <10.44.error_reporting(E_ALL & ~E_WARNING);
How can I help you explore Laravel packages today?