Skip to content

Conversation

@arnaud-lb
Copy link
Owner

@arnaud-lb arnaud-lb commented Sep 8, 2025

PFAs are created by generating the AST of a Closure at runtime and compiling it. The compiled op_array can be cached similarly to linked classes.

About caching

There are three levels of caching:

  • The global opcache (ZCG(hash))
  • EG(partial_function_application_cache)
  • The caller's run time cache

The cache key in ZCG(hash) is composed of the address of the declaring opline, the address of the function being called, and a prefix.

EG(partial_function_application_cache) is used when opcache is disabled, or when the PFA can not be cached in SHM (e.g. because the actual function itself is not cached). This is mostly useful in CLI (opcache is disabled by default) for polymorphic PFAs (inline cache less useful).

TODO

  • Improve PFA caching when opcache is not enabled, and fix cases that are broken when opcache is not enabled
  • Reorganize tests
    • Update RFC-example tests
  • PFAs in constant expressions
  • bindTo() is a no-op for PFA of closures
  • assert(), compact(), extract(), func_get_arg(), get_defined_vars()
  • Pre-bound literal optimization
  • Test preloading
  • Test race between fetch pfa / compile pfa
  • Test hooks
  • ACC_NEVER_CACHE

@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 3 times, most recently from 7062e66 to f65bf45 Compare September 13, 2025 10:37
@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 2 times, most recently from d820a4f to 75dbd22 Compare November 3, 2025 17:04
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get a test case where the function has optional arguments and an incorrect number is given (just to see the error message once).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I just realize that the error messages for userland and native functions are already inconsistent with each other:

https://3v4l.org/3G8EO

@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 2 times, most recently from bf4b4f6 to b753b0c Compare November 3, 2025 18:55
Comment on lines +1236 to +1240
ZEND_API bool zend_check_type_ex(
const zend_type *type, zval *arg, zend_class_entry *scope,
bool is_return_type, bool is_internal)
{
return zend_check_type(type, arg, scope, is_return_type, is_internal);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this? Wouldn't it work to expose zend_check_type() directly?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that easy, as the function is always_inline, and uses always_inline functions from this file as well. zend_check_type() is critical, I didn't want to take the risk of performance regression.

So we have the following options:

  • Move the function and a bunch of functions it uses to a header file (zend_check_type, zend_check_type_slow, zend_check_intersection_type_from_list, probably others), so we don't lose inline-ability
  • Make the function extern inline, but we don't do that (there was a thread about that in a PR).
  • Export a wrapper, like I did here

I will think about about the other options

@arnaud-lb arnaud-lb changed the base branch from master to tmp-master December 10, 2025 17:35
@arnaud-lb arnaud-lb changed the base branch from tmp-master to master December 10, 2025 17:35
@arnaud-lb arnaud-lb changed the base branch from master to tmp-master December 16, 2025 12:00
@arnaud-lb arnaud-lb changed the base branch from tmp-master to master December 16, 2025 12:00
ndossche and others added 9 commits December 16, 2025 15:26
The string added had uninitialized memory due to
php_read_stream_all_chunks() not moving the buffer position, resulting
in the same data always being overwritten instead of new data being
added to the end of the buffer.

This is backport as there is a security impact as described in
GHSA-3237-qqm7-mfv7 .
* PHP-8.1:
  Update NEWS with info about security issues
  Fix GHSA-www2-q4fc-65wf
  Fix GHSA-h96m-rvf9-jgm2
  Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref
  Fix phpGH-20584: Information Leak of Memory
* PHP-8.2:
  Update NEWS with info about security issues
  Fix GHSA-www2-q4fc-65wf
  Fix GHSA-h96m-rvf9-jgm2
  Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref
  Fix phpGH-20584: Information Leak of Memory
* PHP-8.3:
  Update NEWS with info about security issues
  Fix GHSA-www2-q4fc-65wf
  Fix GHSA-h96m-rvf9-jgm2
  Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref
  Fix phpGH-20584: Information Leak of Memory
* PHP-8.4:
  Update NEWS with info about security issues
  Fix GHSA-www2-q4fc-65wf
  Fix GHSA-h96m-rvf9-jgm2
  Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref
  Fix phpGH-20584: Information Leak of Memory
ndossche and others added 22 commits January 2, 2026 18:03
zend_string_truncate() doesn't put a NUL byte.
* PHP-8.4:
  streams/memory: Ensure internal string is NUL terminated (php#20812)
* PHP-8.5:
  streams/memory: Ensure internal string is NUL terminated (php#20812)
* PHP-8.4:
  Update NEWS with fix for bug #74357
  Fix bug #74357: lchown fails to change ownership of symlink with ZTS
* PHP-8.5:
  Update NEWS with fix for bug #74357
  Fix bug #74357: lchown fails to change ownership of symlink with ZTS
These entries are duplicates, but also shouldn't be here in the first
place because they were also merged into stable branches.
This slightly extend error messages with locations taken from scanner / parser
* PHP-8.5:
  Remove duplicate no/yes print for preserve_none
* PHP-8.5:
  Fix missing liburiparser linker option
…alid in the encoding

If the padding string is not valid in the given encoding,
mb_get_strlen() can return 0.

Closes phpGH-20834.
* PHP-8.4:
  Fix phpGH-20833: mb_str_pad() divide by zero if padding string is invalid in the encoding
* PHP-8.5:
  Fix phpGH-20833: mb_str_pad() divide by zero if padding string is invalid in the encoding
RFC: https://wiki.php.net/rfc/partial_function_application_v2

For FCCs, the parser generates a normal function call AST node, the but argument
list is a ZEND_AST_CALLABLE_CONVERT / zend_ast_fcc node.

We extend this for PFAs so that zend_ast_fcc can represent arguments.

 * Support PFA syntax in grammar
 * Update zend_ast_fcc so that arguments can be represented
 * Support serialization of zend_ast_fcc arguments in SHM / file cache
 * Introduce zend_ast_arg_list_add(): Same as zend_ast_list_add(), but wraps the
   list in a ZEND_AST_CALLABLE_CONVERT when adding any placeholder argument.

Technically the arg list wrapping is not required, but it results in simpler
code later as it will be very convenient in the compiler (determines whether a
function calls is a PFA/FCC), and for PFA-in-const-expr support. It also allows
to unify FCCs and PFAs in the grammar.

Closes phpGH-20717.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.