PHP 8.5 RFCs are actively being discussed on the php.net internals list. The pipe operator proposal has been debated for years and may finally ship. Readonly property inheritance fixes an inconsistency. Generics are discussed but will not come in 8.5. I look at the most impactful proposals, show what the code would look like, and explain why generics remain out of reach in the short term.
Pipe operator – |>
<?php
declare(strict_types=1);
// Without pipe operator - nested calls, read right-to-left
$result = array_sum(
array_map(
fn($p) => $p['price'],
array_filter($products, fn($p) => $p['status'] === 1)
)
);
// With pipe operator - left-to-right, readable as a pipeline
$result = $products
|> array_filter(?, fn($p) => $p['status'] === 1)
|> array_map(fn($p) => $p['price'], ?)
|> array_sum(?);
// The ? placeholder is where the piped value goes
// If no ?, the value is passed as the first argument
// More examples:
$slug = $productName
|> strtolower(?)
|> trim(?)
|> preg_replace('/[^a-z0-9]+/', '-', ?)
|> trim(?, '-');
// Chain with methods
$total = $order
|> $this->applyDiscount(?, $discountCode)
|> $this->calculateTax(?)
|> $this->addShipping(?, $shippingMethod);
Readonly property cloning – clean “with” pattern
<?php
declare(strict_types=1);
// PHP 8.4 - workaround for creating modified copies of readonly objects
readonly class Money
{
public function __construct(
public int $amount,
public string $currency,
) {}
// Have to create a new instance manually - verbose
public function withAmount(int $amount): static
{
return new static($amount, $this->currency);
}
}
// PHP 8.5 proposal: clone with syntax
readonly class Money
{
public function __construct(
public int $amount,
public string $currency,
) {}
public function withAmount(int $amount): static
{
// Clone this object but override specific properties
return clone($this, amount: $amount);
}
}
$price = new Money(9999, 'PLN');
$discount = clone($price, amount: 7999); // 79.99 PLN
// Original unchanged: $price->amount === 9999
Why generics are not coming in PHP 8.5
<?php
// What generics would look like (RFC level thinking):
class TypedCollection<T>
{
private array $items = [];
public function add(T $item): void { $this->items[] = $item; }
public function get(int $index): T { return $this->items[$index]; }
/** @return T[] */
public function all(): array { return $this->items; }
}
$products = new TypedCollection<ProductInterface>();
$products->add(new SimpleProduct()); // OK
$products->add("not a product"); // TypeError at runtime
// Why it is hard:
// 1. PHP's type system is runtime-checked, not compile-time
// Generics need either runtime enforcement (expensive) or erasure (no runtime checking)
// 2. Type erasure (like Java) means TypedCollection<Product> and TypedCollection<string>
// are the same class at runtime - no instanceof check possible
// 3. Reified generics (like C#) require generating separate classes for each type parameter
// - massive memory and performance overhead
// 4. Template-based (like C++) requires compilation step PHP doesn't have
// Current solution: PHPStan generics via docblocks (zero runtime cost)
/** @template T of ProductInterface */
class TypedCollection
{
/** @var T[] */
private array $items = [];
/** @param T $item */
public function add(mixed $item): void { $this->items[] = $item; }
/** @return T */
public function get(int $index): mixed { return $this->items[$index]; }
}
/** @var TypedCollection<SimpleProduct> $products */
$products = new TypedCollection();
// PHPStan enforces T = SimpleProduct at analysis time, zero runtime cost
Other PHP 8.5 proposals
<?php // 1. array_zip() - transpose multiple arrays // Before $names = ['Alice', 'Bob', 'Carol']; $emails = ['a@a.com', 'b@b.com', 'c@c.com']; $pairs = array_map(null, $names, $emails); // [[Alice, a@a.com], [Bob, b@b.com], ...] // PHP 8.5 proposal $pairs = array_zip($names, $emails); // 2. mb_str_split() improvements // Already exists in PHP 7.4, may get additional options // 3. Deprecate implicit nullable parameters (enforce what 8.4 started) // function foo(string $x = null) -> must become function foo(?string $x = null) // 4. throw expression in more contexts // Already works in PHP 8.0, 8.5 may extend further // 5. Scope resolution in more contexts $class = SomeClass::class; $result = $class::create(); // Currently requires extra step // May allow: SomeClass::class::create() or similar
Following PHP development
# Stay current with PHP development: # 1. php.net/rfc - all RFC proposals, votes, and discussions # 2. Externals.io - web interface for the php internals mailing list # 3. PHP Weekly newsletter (phpweekly.com) # 4. Brent Roose's blog (stitcher.io) - clear summaries of PHP changes # 5. GitHub: php/php-src - see actual implementation PRs # Watch these RFC discussions in 2025: # - Pipe operator: https://wiki.php.net/rfc/pipe-operator-v3 # - Clone with syntax: https://wiki.php.net/rfc/clone_with # - Generics (informational): https://externals.io/search?q=generics
Summary
PHP 8.5 is shaping up as a developer ergonomics release. The pipe operator, if it passes the vote, will change how functional-style data transformations are written. Clone-with is a small but meaningful quality-of-life improvement for immutable value objects. Generics remain the most requested feature that will not come soon – PHPStan’s @template approach provides the analysis benefits without the runtime complexity that native generics would require.
