Skip to content

Release 4.1.3

Latest

Choose a tag to compare

@DariusIII DariusIII released this 24 Feb 14:57

Release Notes

Performance: Built-in function call optimisation (PHP 8.5)

Background

PHP 8.5's Zend compiler (zend_compile.c line 4975)
contains a function — zend_try_compile_special_func_ex — that compiles certain built-in calls
directly to single VM opcodes, completely bypassing the normal function-call overhead
(no frame push/pop, no argument marshalling, no symbol table lookup):

Function(s) Opcode emitted
strlen ZEND_STRLEN
is_null, is_bool, is_int, is_string, is_array, is_object, is_resource ZEND_TYPE_CHECK
count / sizeof ZEND_COUNT
in_array ZEND_IN_ARRAY
array_slice ZEND_ARRAY_SLICE
array_key_exists ZEND_ARRAY_KEY_EXISTS
intval, strval, boolval, floatval / doubleval ZEND_CAST
sprintf ZEND_FAST_CONCAT-like
chr, ord ZEND_CHR / ZEND_ORD
get_class, get_called_class, gettype ZEND_GET_CLASS / ZEND_GET_TYPE

The catch: this intrinsification only fires when the call is resolved at compile time.
Inside a namespace (e.g. DariusIII\NetNntp\Protocol), an unqualified call like trim() forces
the engine to check at runtime whether DariusIII\NetNntp\Protocol\trim exists before falling back
to the global. Prefixing with \ eliminates that runtime lookup and allows the compiler to
intrinsify or use the faster frameless call path.


Changes

src/Protocol/Client.php

  • Added \ prefix to all unqualified built-in function calls, enabling compile-time resolution
    and opcode intrinsification:
    openssl_error_string, strlen, strpbrk, fwrite, fgets, stream_get_meta_data,
    trim, substr, feof, str_ends_with, str_starts_with, str_replace, explode,
    reset, next, stream_socket_client, stream_set_timeout, stream_set_read_buffer,
    fclose, stream_socket_enable_crypto, ltrim, gmdate, implode, strcasecmp,
    is_numeric, is_array, is_resource, is_string.

  • Removed redundant || strlen($line) < 2 branch in _getTextResponse().
    str_ends_with($line, "\r\n") returning true already guarantees strlen ≥ 2, so the second
    operand was dead code that emitted a ZEND_STRLEN opcode on every iteration of the hot
    receive loop for no reason.

  • Removed redundant (string) cast on substr($response, 4) in _getStatusResponse().
    substr() always returns string when given a string input, so the ZEND_CAST opcode
    was emitted unnecessarily.

src/Client.php

  • Added \ prefix to all unqualified built-in function calls:
    implode, strtotime, substr, array_merge, array_keys, array_values,
    array_combine, array_slice, array_fill, array_splice, strpos, ltrim,
    ctype_digit, str_starts_with, str_ends_with, reset, preg_split, trim,
    is_int, is_array, is_string, count.

Other housekeeping

  • Removed legacy package.xml (PEAR descriptor, superseded entirely by composer.json).