financialacls core extension uses a global callback in hook_civicrm_container, which might be iffy
This is a bit tricky. Here's what I think is happening at https://github.com/civicrm/civicrm-core/blob/f5041803b6af3a9122e844179be3ab72860fd61a/ext/financialacls/financialacls.php#L25:
function financialacls_civicrm_container($container) {
$dispatcherDefn = $container->getDefinition('dispatcher');
$container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
$dispatcherDefn->addMethodCall('addListener', ['civi.api4.authorizeRecord::Contribution', '_financialacls_civi_api4_authorizeContribution']);
}
Callables in php can be strings, e.g. global functions, such as in this case.
But if the function isn't "known to php" at the moment you pass it, then the data type assigned to it is string, not callable.
In symfony < 6, addListener() doesn't care about the type of the callback parameter. In symfony 6+, it wants callable|array
(not string).
When the cached container is built, it compiles these hooks into its own php code, e.g. $instance->addListener('civi.api4.authorizeRecord::Contribution', '_financialacls_civi_api4_authorizeContribution');
The cached container can be loaded earlier than the extension is "loaded", or whatever the word is. In symfony < 6, I think this works out ok because it doesn't actually get used until later, until after the extension is loaded, and there's no type checking so it doesn't error when loading the cached container.
In symfony 6+, when the parameter is an array like in a class member callback, it's fine because it doesn't trigger a parameter type fail. But when it's a string, loading the cached container fails because it can't resolve it to a callable, so it gets passed as a string, which then fails the type checking.
I can sort of prove this by changing the callback in addMethod() above to ts
. It's fine with that at the time the cached container is loaded because it can resolve it to a callable.
FYI @totten you might find this interesting.