Last active
May 7, 2022 17:55
-
-
Save mortenson/ea970a12431b30773f6856285f31268b to your computer and use it in GitHub Desktop.
Stupid auto wiring for any function/method in Drupal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// You shouldn't call this at runtime - do it when something builds (ex: plugins) and store the args somewhere. | |
function get_autowire_args(callable $callback, \Drupal\Component\DependencyInjection\Container $container) { | |
$cache = &drupal_static(__FUNCTION__); | |
$args = []; | |
$reflection = new \ReflectionFunction($callback); | |
foreach ($reflection->getParameters() as $i => $param) { | |
$type_obj = $param->getType(); | |
if (!$type_obj || ($type_obj instanceof \ReflectionNamedType && $type_obj->isBuiltin())) { | |
$args[$i] = NULL; | |
continue; | |
} | |
$type = $type_obj->getName(); | |
if (isset($cache[$type])) { | |
$args[$i] = $cache[$type]; | |
continue; | |
} | |
$match = FALSE; | |
$should_cache = TRUE; | |
foreach ($container->getServiceIds() as $service_id) { | |
$service = $container->get($service_id); | |
if ($service === $type) { | |
$match = $service_id; | |
break; | |
} | |
else if ($service instanceof $type) { | |
$match = $service_id; | |
// If an argument is named the same as a service, stop searching. | |
if ($param->getName() === str_replace('.', '_', $service_id)) { | |
$should_cache = FALSE; | |
break; | |
} | |
} | |
} | |
if (!$match) { | |
throw new \Exception("Cannot autowire parameter $i ($type)"); | |
} | |
if ($should_cache) { | |
$cache[$type] = $match; | |
} | |
$args[$i] = $match; | |
} | |
return $args; | |
} | |
// Then later when you want to call your function/method use this. | |
// $autowire_args is the return of get_autowire_args(), $defaults are values that you already have. | |
// $defaults are keyed by param index, but you can also set a default value for a service ID if you want to override the autowire args. | |
// Example: | |
// | |
// function foo(int $bar, array &$baz, \Symfony\Component\HttpFoundation\Request $request) {} | |
// $autowire_args = get_autowire_args(foo, \Drupal::getContainer()); | |
// $build = []; | |
// autowire_call(foo, $autowire_args, [1, &$build], \Drupal::getContainer()); | |
// | |
// But again, you should cache $autowire_args for performance reasons. | |
function autowire_call(callable $callback, array $autowire_args, array $defaults, \Drupal\Component\DependencyInjection\Container $container) { | |
$args = []; | |
foreach ($autowire_args as $i => $service_id) { | |
if (isset($defaults[$i])) { | |
$args[$i] = &$defaults[$i]; | |
} | |
else if (isset($defaults[$service_id])) { | |
$args[$i] = $defaults[$service_id]; | |
} | |
else { | |
$args[$i] = $container->get($service_id); | |
} | |
} | |
return call_user_func_array($callback, $args); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment