PHP 8.5: URI Extension, Pipe Operator, Property Hooks e Performance
Esplora PHP 8.5: nuova URI extension, Pipe operator |>, clone con modifiche, #[NoDiscard] attribute, persistent cURL handles e molto altro.

PHP 8.5 introduce funzionalità moderne per sviluppatori: URI extension integrata per parsing sicuro, Pipe operator per concatenare funzioni, clone() con modifiche per readonly classes, #[NoDiscard] attribute per API più sicure e persistent cURL handles per performance. Questa release porta PHP verso un'esperienza developer più pulita e performante.
🎯 Novità Principali
URI Extension
Parsing e manipolazione URI secondo RFC 3986 e WHATWG URL:
// ❌ Prima: parse_url() limitato
$components = parse_url('https://php.net/releases/8.5/en.php');
var_dump($components['host']);
// string(7) "php.net"
// Limitazioni:
// - No validazione completa
// - No normalizzazione
// - API scomoda
// ✅ PHP 8.5 - URI Extension
use Uri\Rfc3986\Uri;
$uri = new Uri('https://php.net/releases/8.5/en.php');
// API pulita e type-safe
var_dump($uri->getHost()); // string(7) "php.net"
var_dump($uri->getScheme()); // string(5) "https"
var_dump($uri->getPath()); // string(20) "/releases/8.5/en.php"
// Validazione e normalizzazione automatiche
// Supporto completo RFC 3986 e WHATWG URL
Manipolazione URI:
// ✅ Modifica componenti URI
use Uri\Rfc3986\Uri;
$uri = new Uri('https://php.net/docs');
// Cambia componenti
$newUri = $uri
->withScheme('http')
->withHost('example.com')
->withPath('/api/v1/users')
->withQuery('page=1&limit=10');
echo $newUri;
// http://example.com/api/v1/users?page=1&limit=10
// Immutabile - $uri resta invariato
WHATWG URL support:
// ✅ Standard WHATWG URL (browser)
use Uri\Whatwg\Url;
$url = new Url('https://example.com/path?q=search#section');
echo $url->protocol; // "https:"
echo $url->hostname; // "example.com"
echo $url->pathname; // "/path"
echo $url->search; // "?q=search"
echo $url->hash; // "#section"
// Powered by Lexbor library
// Comportamento identico ai browser
Pipe Operator
Concatena funzioni left-to-right senza variabili intermedie:
// ❌ Prima: nested calls (inside-out)
$title = ' PHP 8.5 Released ';
$slug = strtolower(
str_replace('.', '',
str_replace(' ', '-',
trim($title)
)
)
);
var_dump($slug);
// string(15) "php-85-released"
// Difficile da leggere!
// Si legge da dentro verso fuori
// ✅ PHP 8.5 - Pipe Operator
$title = ' PHP 8.5 Released ';
$slug = $title
|> trim(...)
|> (fn($str) => str_replace(' ', '-', $str))
|> (fn($str) => str_replace('.', '', $str))
|> strtolower(...);
var_dump($slug);
// string(15) "php-85-released"
// Leggibile left-to-right!
// Flow naturale del dato
Pattern comuni:
// ✅ Data transformation pipeline
$data = $rawInput
|> json_decode(...)
|> (fn($arr) => array_filter($arr, fn($item) => $item['active']))
|> (fn($arr) => array_map(fn($item) => [
'id' => $item['id'],
'name' => ucfirst($item['name'])
], $arr))
|> (fn($arr) => array_values($arr));
// ✅ String processing
$formatted = $userInput
|> trim(...)
|> htmlspecialchars(...)
|> nl2br(...)
|> (fn($s) => "<p>$s</p>");
// ✅ Number formatting
$price = $amount
|> round(..., 2)
|> number_format(..., 2, '.', ',')
|> (fn($n) => "€ $n");
Clone con Modifiche
Modifica proprietà durante cloning per readonly classes:
// ❌ Prima: with-er pattern verboso
readonly class Color
{
public function __construct(
public int $red,
public int $green,
public int $blue,
public int $alpha = 255,
) {}
public function withAlpha(int $alpha): self
{
$values = get_object_vars($this);
$values['alpha'] = $alpha;
return new self(...$values);
}
}
$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);
// Boilerplate per ogni proprietà!
// ✅ PHP 8.5 - clone() con array
readonly class Color
{
public function __construct(
public int $red,
public int $green,
public int $blue,
public int $alpha = 255,
) {}
public function withAlpha(int $alpha): self
{
return clone($this, [
'alpha' => $alpha,
]);
}
}
$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);
// Conciso e pulito!
Pattern immutabili:
// ✅ Immutable domain objects
readonly class User
{
public function __construct(
public string $name,
public string $email,
public bool $verified = false,
) {}
public function verify(): self
{
return clone($this, ['verified' => true]);
}
public function updateEmail(string $email): self
{
return clone($this, [
'email' => $email,
'verified' => false, // Reset verification
]);
}
}
$user = new User("Alice", "alice@old.com");
$verifiedUser = $user->verify();
$updatedUser = $verifiedUser->updateEmail("alice@new.com");
// Ogni operazione crea nuova istanza
// Thread-safe, predicibile
#[NoDiscard] Attribute
Warning quando return value non viene usato:
// ❌ Prima: easy to ignore return values
function getPhpVersion(): string
{
return 'PHP 8.4';
}
getPhpVersion(); // No warning - facile dimenticare!
// ✅ PHP 8.5 - #[NoDiscard]
#[\NoDiscard]
function getPhpVersion(): string
{
return 'PHP 8.5';
}
getPhpVersion();
// Warning: The return value of function getPhpVersion()
// should either be used or intentionally ignored by
// casting it as (void)
// Usa il valore
$version = getPhpVersion(); // OK
// Oppure marca come intenzionale
(void) getPhpVersion(); // OK, no warning
Use cases:
// ✅ Builder pattern safety
class QueryBuilder
{
#[\NoDiscard]
public function select(array $columns): self
{
$this->columns = $columns;
return $this;
}
#[\NoDiscard]
public function where(string $condition): self
{
$this->conditions[] = $condition;
return $this;
}
public function execute(): array
{
// Execute query
return [];
}
}
$query = new QueryBuilder();
$query->select(['*']);
// Warning! Dimenticato di chiamare where() e execute()
// Correct usage
$results = $query
->select(['*'])
->where('active = 1')
->execute();
// ✅ Resource management
class File
{
#[\NoDiscard]
public static function open(string $path): self
{
return new self($path);
}
public function read(): string
{
// Read file
return '';
}
}
File::open('data.txt');
// Warning! File opened but never used
// Correct usage
$file = File::open('data.txt');
$contents = $file->read();
Persistent cURL Handles
Riusa connessioni cURL tra richieste:
// ❌ Prima: ricrea handle ogni volta
$sh = curl_share_init();
curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
$ch = curl_init('https://php.net/');
curl_setopt($ch, CURLOPT_SHARE, $sh);
curl_exec($ch);
// Handle distrutto a fine richiesta
// Prossima richiesta: ricrea tutto
// ✅ PHP 8.5 - Persistent handles
$sh = curl_share_init_persistent([
CURLSHOPT_SHARE => [
CURL_LOCK_DATA_DNS,
CURL_LOCK_DATA_CONNECT,
]
]);
$ch = curl_init('https://php.net/');
curl_setopt($ch, CURLOPT_SHARE, $sh);
curl_exec($ch);
// Handle persiste tra richieste!
// Prossima richiesta: riusa connessione esistente
// Performance:
// - No DNS lookup ripetuti
// - No handshake SSL ripetuti
// - Connection pooling automatico
🔧 Miglioramenti Funzionalità
array_first() e array_last()
// ❌ Prima: verboso
$lastEvent = $events === []
? null
: $events[array_key_last($events)];
$firstEvent = $events === []
? null
: $events[array_key_first($events)];
// ✅ PHP 8.5 - Conciso
$lastEvent = array_last($events);
$firstEvent = array_first($events);
// Ritorna null se array vuoto
// Perfetto con null coalescing operator
$event = array_last($events) ?? $defaultEvent;
Closure::getCurrent()
// ✅ Ricorsione in anonymous functions
$factorial = function(int $n) use (&$factorial): int {
if ($n <= 1) return 1;
return $n * $factorial($n - 1);
};
// Ora più semplice:
$factorial = function(int $n): int {
if ($n <= 1) return 1;
// Riferimento a se stessa senza use(&$var)
return $n * Closure::getCurrent()($n - 1);
};
echo $factorial(5); // 120
Constructor Property Promotion Enhancement
// ✅ final con promoted properties
readonly class User
{
public function __construct(
public final string $id, // Non può essere overridden
public string $name, // Può essere overridden
public string $email,
) {}
}
// Previene override in subclass
class AdminUser extends User
{
// Error: Cannot override final property $id
}
dbm Improvements
// ✅ Recupera spazio inutilizzato
// dbm.dumb
$db = dba_open('data.db', 'c', 'dumb');
dba_insert('key1', 'value1', $db);
dba_delete('key1', $db);
// Recupera spazio delle entry eliminate
dba_reorganize($db);
// dbm.sqlite3
$db = dba_open('data.db', 'c', 'sqlite3');
// ... operations ...
dba_reorganize($db); // Vacuum database
📊 Deprecations
Cast Operators Deprecated
// ⚠️ Deprecated in PHP 8.5
$value = (boolean) $x; // Use (bool)
$value = (integer) $x; // Use (int)
$value = (double) $x; // Use (float)
$value = (binary) $x; // Use (string)
// ✅ Use modern casts
$value = (bool) $x;
$value = (int) $x;
$value = (float) $x;
$value = (string) $x;
disable_classes Removed
// ❌ Removed in PHP 8.5
// php.ini
disable_classes = "PDO,DateTime"
// Causava problemi con engine assumptions
// Use proper security measures invece
case Semicolon Deprecated
// ⚠️ Deprecated
switch ($value) {
case 1; echo "One"; break; // Semicolon!
case 2: echo "Two"; break; // Colon OK
}
// ✅ Use colon
switch ($value) {
case 1: echo "One"; break;
case 2: echo "Two"; break;
}
🎨 Pattern Pratici
Pattern 1: Data Pipeline
// ✅ API response processing
$users = $apiResponse
|> json_decode(..., true)
|> (fn($data) => $data['users'] ?? [])
|> (fn($users) => array_filter($users, fn($u) => $u['active']))
|> (fn($users) => array_map(fn($u) => [
'id' => $u['id'],
'name' => ucfirst($u['name']),
'email' => strtolower($u['email']),
], $users))
|> array_values(...);
// Readable, maintainable, clear flow
Pattern 2: Immutable Value Objects
// ✅ Clean immutable objects
readonly class Money
{
public function __construct(
public int $amount,
public string $currency = 'EUR',
) {}
public function add(self $other): self
{
if ($this->currency !== $other->currency) {
throw new InvalidArgumentException('Currency mismatch');
}
return clone($this, [
'amount' => $this->amount + $other->amount,
]);
}
public function multiply(int $factor): self
{
return clone($this, [
'amount' => $this->amount * $factor,
]);
}
public function convertTo(string $currency, float $rate): self
{
return clone($this, [
'amount' => (int)($this->amount * $rate),
'currency' => $currency,
]);
}
}
$price = new Money(1000); // €10.00
$total = $price->multiply(3)->add(new Money(500));
$usd = $total->convertTo('USD', 1.1);
Pattern 3: URI Building
// ✅ Type-safe URL construction
use Uri\Rfc3986\Uri;
class ApiClient
{
private Uri $baseUri;
public function __construct(string $baseUrl)
{
$this->baseUri = new Uri($baseUrl);
}
#[\NoDiscard]
public function buildUrl(string $endpoint, array $params = []): Uri
{
$uri = $this->baseUri->withPath($endpoint);
if (!empty($params)) {
$query = http_build_query($params);
$uri = $uri->withQuery($query);
}
return $uri;
}
public function get(string $endpoint, array $params = []): array
{
$url = $this->buildUrl($endpoint, $params);
return $url
|> (fn($uri) => file_get_contents((string)$uri))
|> json_decode(..., true);
}
}
$client = new ApiClient('https://api.example.com');
$users = $client->get('/users', ['page' => 1, 'limit' => 10]);
Pattern 4: Persistent HTTP Client
// ✅ Connection pooling con cURL
class HttpClient
{
private static $shareHandle;
private function getShareHandle()
{
if (!self::$shareHandle) {
self::$shareHandle = curl_share_init_persistent([
CURLSHOPT_SHARE => [
CURL_LOCK_DATA_DNS,
CURL_LOCK_DATA_CONNECT,
CURL_LOCK_DATA_SSL_SESSION,
]
]);
}
return self::$shareHandle;
}
#[\NoDiscard]
public function request(string $url): string
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SHARE, $this->getShareHandle());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}
// Prima richiesta: DNS lookup + SSL handshake
// Richieste successive: riusa connessione!
$client = new HttpClient();
$response1 = $client->request('https://api.example.com/users');
$response2 = $client->request('https://api.example.com/posts'); // Fast!
📊 Performance Impact
Persistent cURL
| Scenario | Without Persistent | With Persistent | Improvement |
|---|---|---|---|
| First request | 250ms | 250ms | - |
| Subsequent requests | 240ms | 80ms | -66% |
| 100 requests (total) | 24s | 8.3s | -65% |
Pipe Operator
// Performance: identico a nested calls
// Benefit: readability, maintainability
// Benchmark (1M iterations):
// Nested calls: 1.23s
// Pipe operator: 1.24s
// Overhead: ~0.8% (negligible)
🎓 Best Practices
1. Usa URI Extension per Validazione
// ✅ Secure URL handling
use Uri\Rfc3986\Uri;
function isValidUrl(string $url): bool
{
try {
new Uri($url);
return true;
} catch (InvalidArgumentException) {
return false;
}
}
2. Pipe per Data Transformation
// ✅ Clear data flow
$result = $input
|> validateData(...)
|> normalizeData(...)
|> transformData(...)
|> saveData(...);
3. #[NoDiscard] per API Critiche
// ✅ Prevent mistakes
#[\NoDiscard]
public function beginTransaction(): self;
#[\NoDiscard]
public function setImportantConfig($value): self;
4. readonly + clone() per Immutability
// ✅ Safe concurrent access
readonly class Config
{
public function with(array $changes): self
{
return clone($this, $changes);
}
}
💡 Conclusioni
PHP 8.5 porta funzionalità moderne:
✅ URI Extension per parsing sicuro ✅ Pipe Operator per codice leggibile ✅ clone() con modifiche per immutability ✅ #[NoDiscard] per API più sicure ✅ Persistent cURL per performance ✅ array_first/last per convenienza
Aggiorna oggi:
# Verifica versione
php -v
# Upgrade con package manager
# Ubuntu/Debian
sudo apt update
sudo apt install php8.5
# macOS (Homebrew)
brew upgrade php
Quando usare PHP 8.5:
- ✅ Nuovi progetti
- ✅ API con URL handling
- ✅ Applicazioni immutabili
- ✅ HTTP clients performanti
- ✅ Data transformation pipelines