Skip to content

Instantly share code, notes, and snippets.

@mortenson
Last active May 7, 2022 17:55
Show Gist options
  • Save mortenson/ea970a12431b30773f6856285f31268b to your computer and use it in GitHub Desktop.
Save mortenson/ea970a12431b30773f6856285f31268b to your computer and use it in GitHub Desktop.
Stupid auto wiring for any function/method in Drupal
<?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