HaPHPiness - Best things in PHP
This guide is inspired by haphpiness.com, highlighting the modern features that make PHP a great language to work with.
Modern Language Features
1. Named Arguments (PHP 8.0)
Named arguments allow you to pass values to a function by specifying the parameter name, so that you don't have to rely on their order.
// After: named arguments
htmlspecialchars($string, double_encode: false);
2. Union Types (PHP 8.0) and Intersection Types (PHP 8.1)
Union types allow variables to have more than one type. Intersection types require a value to satisfy multiple type constraints.
// Union types
function process(int|string $input): void {}
// Intersection types
function save(Countable&Iterator $collection): void {}
3. Enums (PHP 8.1)
First-class support for enumerations, providing type safety for fixed sets of values.
enum Status: string {
case Active = 'active';
case Inactive = 'inactive';
}
4. str_contains(), str_starts_with(), str_ends_with() (PHP 8.0)
Consistent and readable functions for common string operations.
if (str_contains($url, 'https')) { ... }
if (str_starts_with($file, '/var/www')) { ... }
if (str_ends_with($file, '.php')) { ... }
5. Array Unpacking with String Keys (PHP 8.1)
The spread operator now works with string keys, making array merging clean and expressive.
$config = [...$defaults, ...$custom];
6. Fibers (PHP 8.1)
Lightweight, cooperatively-scheduled coroutines for building async primitives.
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('paused');
echo "Resumed with: $value";
});
8. Built-in development server (php -S)
No Apache or Nginx config needed for local development. Just run:
php -S localhost:8000
10. Arrow Functions (PHP 7.4)
Concise syntax for short closures with automatic variable capture from parent scope.
$doubled = array_map(fn($n) => $n * 2, $numbers);
11. Match Expressions (PHP 8.0)
A more powerful and safer alternative to switch, providing strict comparison and returning a value.
$text = match($statusCode) {
200, 201 => 'Success',
404 => 'Not Found',
default => 'Unknown',
};
12. Null coalescing operator ?? and ??= (PHP 7.0+)
Clean way to handle defaults and lazy initialization.
$username = $_GET['user'] ?? 'anonymous';
$this->logger ??= new NullLogger();
15. Readonly properties and classes (PHP 8.1+)
Engine-enforced immutability for properties and entire classes.
readonly class Money {
public function __construct(public int $amount, public string $currency) {}
}
17. Attributes — native metadata
PHP 8.0 replaced docblock annotations with real syntax, checkable by static analysis.
#[Route('/users', method: 'GET')]
public function index(): Response { ... }
34. Property hooks (PHP 8.4)
Define get and set behavior directly on property declarations, eliminating getter/setter boilerplate.
class User {
public string $fullName {
get => $this->firstName . ' ' . $this->lastName;
}
}
39. Pipe operator |> (PHP 8.5)
Left-to-right data flow for function chaining.
$slug = $title
|> trim(...)
|> strtolower(...);
43. #[\NoDiscard] (PHP 8.5)
Warns when the return value of a function is ignored, essential for validation results or immutable operations.
#[\NoDiscard]
function validate(array $data): bool { ... }
44. Fatal error backtraces
PHP 8.5 gives you stack traces on fatal errors like "Maximum execution time exceeded", making production debugging much easier.
45. URI extension — proper URL parsing at last
PHP 8.5 introduces a standards-compliant URI extension with immutable URL objects.
use Uri\Rfc3986\Uri;
$uri = new Uri('https://example.com/path');
49. array_is_list() — finally answer "is this sequential?"
Determines if an array is a list (indexed from 0 without gaps).
array_is_list(['a', 'b', 'c']); // true
50. FFI — call C libraries directly from PHP
Foreign Function Interface (PHP 7.4) lets you call C functions without building extensions.
$ffi = FFI::cdef("double cos(double x);", "libm.so.6");
echo $ffi->cos(M_PI);
52. WeakMap (PHP 8.0)
Maps with objects as keys that don't prevent garbage collection, perfect for per-object caches.
$cache = new WeakMap();
$cache[$obj] = $computedData;
53. Named capture groups in preg_match
Regex captures can now be accessed by name instead of just numeric indices.
preg_match('/(?P<year>\d{4})/', '2024', $matches);
echo $matches['year'];
54. Spaceship operator <=> — three-way comparison
PHP 7.0 operator that returns -1, 0, or 1, perfect for sorting.
usort($users, fn($a, $b) => $a->age <=> $b->age);
55. Array destructuring with keys — pattern matching for arrays
Extract specific values from associative arrays easily.
['name' => $name, 'age' => $age] = $person;
58. PHPStan & Psalm — static analysis as a first-class citizen
Tools that provide TypeScript-grade type safety with zero runtime overhead.
59. Numeric literal separators (PHP 7.4)
Use underscores to make large numbers more readable.
$population = 8_000_000_000;
69. NativePHP — desktop and mobile apps, all in PHP
Build native cross-platform apps using the PHP you already know.
Ecosystem and Performance
- Composer: Best-in-class dependency management.
- OPcache & JIT: Significant performance boosts.
- Symfony & Laravel: Mature, high-quality frameworks.