PHP 8 Compatibility Guide
Making XOOPS modules compatible with PHP 8.x and leveraging modern features.
PHP Version Timeline
Section titled “PHP Version Timeline”timeline title PHP Version Support 2020 : PHP 8.0 Released : Named Arguments : Attributes : Union Types 2021 : PHP 8.1 Released : Enums : Readonly Properties : Fibers 2022 : PHP 8.2 Released : Readonly Classes : Null/False Types : Deprecation Notices 2023 : PHP 8.3 Released : Typed Class Constants : json_validate() 2024 : PHP 8.4 Released : Property Hooks : Asymmetric VisibilityBreaking Changes
Section titled “Breaking Changes”Deprecated/Removed Functions
Section titled “Deprecated/Removed Functions”// ❌ REMOVED in PHP 8.0create_function('$a', 'return $a * 2'); // Use closureseach($array); // Use foreach$string{0}; // Use $string[0]
// ❌ DEPRECATED in PHP 8.1strftime('%Y-%m-%d', $timestamp); // Use date() or IntlDateFormatter$GLOBALS assignment
// ❌ DEPRECATED in PHP 8.2${$varName}; // Use $$varName or arraysutf8_encode() / utf8_decode(); // Use mb_convert_encoding()Type System Changes
Section titled “Type System Changes”// ❌ PHP 8.0+ enforces type declarationsfunction process(int $id) { // Passing string "123" now throws TypeError in strict mode}
// ✅ Handle with explicit castingfunction process(int $id) { $id = (int) $id;}
// ❌ Return type must matchfunction getData(): array { return null; // TypeError in PHP 8}
// ✅ Use nullable return typefunction getData(): ?array { return null; // OK}Modern PHP Features for XOOPS
Section titled “Modern PHP Features for XOOPS”Named Arguments (PHP 8.0+)
Section titled “Named Arguments (PHP 8.0+)”// Before: positional arguments$form->addElement(new XoopsFormText( 'Title', // caption 'title', // name 50, // size 255, // maxlength '' // value), true); // required
// After: named arguments (clearer)$form->addElement( element: new XoopsFormText( caption: 'Title', name: 'title', size: 50, maxlength: 255, value: '' ), required: true);Constructor Property Promotion (PHP 8.0+)
Section titled “Constructor Property Promotion (PHP 8.0+)”// Beforeclass ItemHandler{ private XoopsDatabase $db; private string $table;
public function __construct(XoopsDatabase $db, string $table) { $this->db = $db; $this->table = $table; }}
// After (PHP 8.0+)class ItemHandler{ public function __construct( private XoopsDatabase $db, private string $table ) {}}Union Types (PHP 8.0+)
Section titled “Union Types (PHP 8.0+)”// Accept multiple typespublic function setContent(string|array $content): void{ if (is_array($content)) { $content = implode("\n", $content); } $this->content = $content;}
// Nullable shorthandpublic function getUser(): User|null{ return $this->user;}Match Expression (PHP 8.0+)
Section titled “Match Expression (PHP 8.0+)”// Before: switch statementswitch ($status) { case 'draft': $label = 'Draft'; break; case 'published': $label = 'Published'; break; case 'archived': $label = 'Archived'; break; default: $label = 'Unknown';}
// After: match expression$label = match($status) { 'draft' => 'Draft', 'published' => 'Published', 'archived' => 'Archived', default => 'Unknown'};Attributes (PHP 8.0+)
Section titled “Attributes (PHP 8.0+)”use Attribute;
#[Attribute]class Route{ public function __construct( public string $path, public array $methods = ['GET'] ) {}}
class ItemController{ #[Route('/items', methods: ['GET'])] public function index(): Response { // ... }
#[Route('/items/{id}', methods: ['GET'])] public function show(int $id): Response { // ... }}Enums (PHP 8.1+)
Section titled “Enums (PHP 8.1+)”// Before: class constantsclass ItemStatus{ public const DRAFT = 'draft'; public const PUBLISHED = 'published'; public const ARCHIVED = 'archived';}
// After: native enumsenum ItemStatus: string{ case Draft = 'draft'; case Published = 'published'; case Archived = 'archived';
public function label(): string { return match($this) { self::Draft => 'Draft', self::Published => 'Published', self::Archived => 'Archived', }; }
public function canEdit(): bool { return $this === self::Draft; }}
// Usage$item->status = ItemStatus::Published;echo $item->status->label(); // "Published"Readonly Properties (PHP 8.1+)
Section titled “Readonly Properties (PHP 8.1+)”class Item{ public function __construct( public readonly int $id, public readonly string $title, public readonly \DateTimeImmutable $created ) {}}
$item = new Item(1, 'Title', new \DateTimeImmutable());$item->title = 'New Title'; // Error: Cannot modify readonly propertyReadonly Classes (PHP 8.2+)
Section titled “Readonly Classes (PHP 8.2+)”readonly class ItemDTO{ public function __construct( public int $id, public string $title, public string $content, public \DateTimeImmutable $created ) {}}Migration Patterns
Section titled “Migration Patterns”Null Coalescing
Section titled “Null Coalescing”// Before$value = isset($array['key']) ? $array['key'] : 'default';
// After (PHP 7.0+)$value = $array['key'] ?? 'default';
// Null coalescing assignment (PHP 7.4+)$array['key'] ??= 'default';Arrow Functions
Section titled “Arrow Functions”// Before$ids = array_map(function($item) { return $item->id;}, $items);
// After (PHP 7.4+)$ids = array_map(fn($item) => $item->id, $items);Spread Operator
Section titled “Spread Operator”// Array spread$merged = [...$array1, ...$array2];
// Named argument spread$config = ['size' => 50, 'maxlength' => 255];$element = new XoopsFormText( caption: 'Title', name: 'title', ...$config);Code Compatibility Checker
Section titled “Code Compatibility Checker”flowchart TD A[Run PHPStan] --> B{Issues Found?} B -->|Yes| C[Review Deprecations] C --> D[Update Code] D --> A B -->|No| E[Run PHP CS Fixer] E --> F[Test with PHP 8.x] F --> G{Tests Pass?} G -->|No| H[Fix Issues] H --> F G -->|Yes| I[Deploy]PHPStan Configuration
Section titled “PHPStan Configuration”# phpstan.neonparameters: level: 8 phpVersion: 80200 paths: - class/ - src/ excludePaths: - vendor/ reportUnmatchedIgnoredErrors: falseRunning Analysis
Section titled “Running Analysis”# Install PHPStancomposer require --dev phpstan/phpstan
# Run analysis./vendor/bin/phpstan analyse
# With specific PHP version./vendor/bin/phpstan analyse --php-version=8.4Common Fixes
Section titled “Common Fixes”String Access
Section titled “String Access”// ❌ Deprecated curly brace syntax$char = $string{0};
// ✅ Use square brackets$char = $string[0];Null Handling
Section titled “Null Handling”// ❌ Error in PHP 8: strlen(null)$length = strlen($value);
// ✅ Handle null explicitly$length = strlen($value ?? '');// or$length = $value !== null ? strlen($value) : 0;Array Functions
Section titled “Array Functions”// ❌ Passing null to array functionsarray_merge(null, $array);
// ✅ Ensure array typearray_merge($array1 ?? [], $array2);Class Properties
Section titled “Class Properties”// ❌ Dynamic properties deprecated in PHP 8.2class Item{ // No $title property declared}$item = new Item();$item->title = 'Test'; // Deprecated warning
// ✅ Declare all propertiesclass Item{ public string $title = '';}Related Documentation
Section titled “Related Documentation”- XOOPS 4.0 Specification
- Migration from 2.5.x
- Code Organization
#xoops #php8 #modernization #compatibility #upgrade