PHP 8.5: Major Features
PHP 8.5 continues the āexpress more in the languageā trajectory: a pipe operator for readable data pipelines, #[\NoDiscard] so return values are not silently ignored, and closures in constant expressions so attributes and defaults can reference real callables. At the platform level, an always-on URI extension, stricter filter failure modes, and substantial PDO / Opcache behavior changes mean you should budget real QA timeānot just for new syntax, but for extensions and INI packaging.
Table of Contents
- Pipe operator (
|>) - Closures & first-class callables in constant expressions
#[\NoDiscard]and the(void)cast- URI extension (RFC 3986 / WHATWG URL)
- Notable library & extension updates
- New functions (
array_first,array_last, handlers, ā¦) - Practical recipes
- Backward incompatible changes (migration notes)
- Deprecations (high-level checklist)
- Other changes & operations (Opcache, INI, performance)
Pipe operator (|>)
The pipe operator passes the left-hand value into a single-argument callable on the right. It shines when you chain several transformations without nested temporary variables.
$result = 'Hello World' |> strlen(...);
// Same as: $result = strlen('Hello World');
Rules that bite: the RHS must be a callable that accepts exactly one parameter (no by-ref). Arrow functions must be parenthesized when used with |> to avoid parse ambiguity. See the functional operators manual page for chaining examples.
Closures & first-class callables in constant expressions
PHP 8.5 allows closures and first-class callables in constant expressions, including:
- Attribute arguments
- Default values for properties and parameters
- Constants and class constants
This makes metadata and defaults more expressive when a static scalar is not enoughāwhile still being resolved at compile/link time where the language allows.
#[\NoDiscard] and the (void) cast
#[\NoDiscard] marks functions/methods whose return value should not be ignored (similar intent to C++ [[nodiscard]]). If the caller drops the return value, PHP can emit diagnostics (and static analysis tools can follow the same signal).
(void) is an explicit āI am intentionally discarding this valueā marker. It does not change runtime behavior by itself, but it can suppress warnings tied to #[\NoDiscard] and help IDEs understand intent.
#[\NoDiscard]
function loadConfig(): array { return []; }
(void) loadConfig(); // intentional discard
URI extension (RFC 3986 / WHATWG URL)
PHP 8.5 adds an always-enabled uri extension for parsing and handling URIs/URLs per RFC 3986 and the WHATWG URL model. Use it when you need spec-correct URL/URI behavior instead of ad hoc string slicing (especially for user-controlled input).
Notable library & extension updates
- Filter:
FILTER_THROW_ON_FAILUREā validation failures can throw instead of returningfalse(cannot combine withFILTER_NULL_ON_FAILURE). - Intl: e.g.
IntlListFormatter,Locale::addLikelySubtags()/minimizeSubtags(), extraNumberFormattercurrency-related style constants (ICU version requirements apply). - Session / cookies:
session_set_cookie_params(),session_get_cookie_params(),session_start(), plussetcookie()/setrawcookie()ā support partitioned cookies via a"partitioned"key (CHIPS-style deployment patterns). - Standard:
mail()with sendmail transport returns real errors and warns if the sendmail process dies unexpectedly;getimagesize()gains HEIF/HEIC, SVG (with libxml), and optional dimension unit metadata. - DOM: e.g.
Dom\Element::$outerHTML,$childrenonDom\ParentNodeimplementations. - cURL: new
curl_getinfo()keys and options (proxy/auth introspection, large upload sizes, follow-location modes, TLS signature algorithms, etc.āoften libcurl-version-gated).
New functions (array_first, array_last, handlers, ā¦)
Highlights:
- Core:
get_error_handler(),get_exception_handler(),Closure::getCurrent() - Standard:
array_first(),array_last() - Reflection: e.g. more on
ReflectionConstant,ReflectionProperty::getMangledName() - Opcache:
opcache_is_script_cached_in_file_cache() - PostgreSQL / SQLite / DOM: various driver helpers (see new functions)
Practical recipes
Pipe a normalization pipeline
$slug = $raw
|> trim(...)
|> strtolower(...)
|> preg_replace('/\s+/', '-', ...);
Filter with exceptions instead of false
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
First / last element without manual indexing
$first = array_first($list);
$last = array_last($list);
Backward incompatible changes (migration notes)
PHP core (language & runtime)
class_alias(): cannot use"array"or"callable"as alias names.- Loose compare to bool for āuncomparableā objects (enums,
CurlHandle, ā¦): behavior unified with(bool)$object. gc_collect_cycles()return value no longer counts strings/resources collected indirectly through cycles.- Final subclasses: may use
self/ concrete class name wherestaticsubstitution was restricted. - Tick handlers: run after shutdown functions, destructors, and output handler cleanup.
- Traits: binding order relative to parent class changed (subtle; re-test trait-heavy code).
- Compilation/linking errors: error handling order changed; user error handlers throwing may behave differently.
#[\Attribute]on abstract class, enum, interface, or trait: compile-time error unless validation is deferred with#[\DelayedTargetValidation](see manual).disable_classesINI: removed.- Destructuring non-array, non-null with
[]/list(): warning. - Int cast of out-of-range floats / numeric strings, and NAN casts: warnings.
Opcache
- Opcache is always built into the PHP binary and always loaded; loading
zend_extension=opcache.so/.dllwarns. Build flags--enable-opcache/ sharedopcache.soartifacts are goneāadjust deployment docs and Docker images.
PDO (high impact)
PDO::FETCH_*numeric values changed for several flags (FETCH_GROUP,FETCH_UNIQUE, ā¦)āre-test any code that stores numeric fetch modes in databases/configs.- PDO::FETCH_CLASS constructor args: now follow
call_user_func_arraysemantics (string keys behave like named args; by-ref wrapping rules changed). - Fetching:
FETCH_PROPS_LATEonly withFETCH_CLASS;FETCH_INTOrestrictions withfetchAll(), etc.āsee migration85 incompatible.
DOM / SPL / misc
- Cloning live node lists / named node maps (classic +
Dom\*) now fails (previously produced broken objects). ArrayObject: no longer accepts enums as elements.- mysqli: calling constructor on an already constructed object throws Error.
- Many extensions throw
ValueErrorwhere they previously usedTypeErroror silent failure (FileInfo nul bytes, LDAP options, sockets ports, SNMP host validation, etc.).
Deprecations (high-level checklist)
Deprecations in 8.5 span core syntax (non-canonical casts like (integer)), legacy operators (backticks), serialization habits (__sleep/__wakeup soft-deprecation in favor of __serialize/__unserialize), PDO surface ( uri: DSN scheme, driver constants/methods on the base PDO class), cURL curl_close / share close, resource-style finfo_close, imagedestroy, xml_parser_free, and more. Treat the official migration85 deprecated list as the source of truth and schedule cleanups before they become hard errors.
Other changes & operations (Opcache, INI, performance)
- CLI:
--ini=diffprints INI deltas vs built-in defaults;cli_set_process_title()fails if the title is too long (no silent truncate). - Core INI: e.g.
fatal_error_backtraces,max_memory_limit,startup-onlycaps on runtimememory_limitadjustments. - Opcache INI: e.g.
opcache.file_cache_read_onlyfor read-only file caches; JIT hot loop default tuned; memory consumption changes reported correctly. - Performance: faster exception creation, better array callbacks,
urlencodespeedups, Reflection property access improvements, SIMD paths on ARM, optional TAILCALL VM with Clang ā„ 19, and moreāsee other changes.
Closing thoughts
PHP 8.5 is a mix of ergonomic language features (pipes, richer constant expressions, discard semantics) and platform-level tightening (PDO modes, Opcache packaging, stricter validation across extensions). Plan upgrades as: adopt new syntax where it clarifies intent, then run integration tests against PDO fetch modes, session/cookie code paths, and anything that relied on deprecated resource-style close helpers or loose boolean comparisons to objects.